summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/ec.c4
-rw-r--r--drivers/block/amiflop.c1
-rw-r--r--drivers/char/Kconfig17
-rw-r--r--drivers/char/Makefile4
-rw-r--r--drivers/char/drm/drmP.h16
-rw-r--r--drivers/char/drm/drm_bufs.c20
-rw-r--r--drivers/char/drm/drm_dma.c4
-rw-r--r--drivers/char/drm/drm_memory.c59
-rw-r--r--drivers/char/drm/drm_memory_debug.h70
-rw-r--r--drivers/char/drm/drm_pci.c29
-rw-r--r--drivers/char/drm/drm_pciids.h116
-rw-r--r--drivers/char/drm/i915_dma.c2
-rw-r--r--drivers/char/drm/i915_irq.c2
-rw-r--r--drivers/char/drm/r300_cmdbuf.c86
-rw-r--r--drivers/char/drm/r300_reg.h39
-rw-r--r--drivers/char/drm/radeon_cp.c151
-rw-r--r--drivers/char/drm/radeon_drm.h5
-rw-r--r--drivers/char/drm/radeon_drv.h25
-rw-r--r--drivers/char/drm/radeon_state.c105
-rw-r--r--drivers/char/drm/sis_mm.c2
-rw-r--r--drivers/char/generic_nvram.c5
-rw-r--r--drivers/char/hvc_console.c101
-rw-r--r--drivers/char/hvc_console.h63
-rw-r--r--drivers/char/hvc_rtas.c138
-rw-r--r--drivers/char/hvc_vio.c11
-rw-r--r--drivers/char/hvcs.c1
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c18
-rw-r--r--drivers/char/ipmi/ipmi_kcs_sm.c8
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c80
-rw-r--r--drivers/char/ipmi/ipmi_poweroff.c2
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c85
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c61
-rw-r--r--drivers/char/istallion.c32
-rw-r--r--drivers/char/stallion.c46
-rw-r--r--drivers/char/tty_io.c2
-rw-r--r--drivers/char/vt.c4
-rw-r--r--drivers/edac/Kconfig2
-rw-r--r--drivers/ide/ide-disk.c3
-rw-r--r--drivers/ide/ide-taskfile.c8
-rw-r--r--drivers/ide/pci/via82cxxx.c2
-rw-r--r--drivers/ide/ppc/pmac.c2
-rw-r--r--drivers/ieee1394/ohci1394.c4
-rw-r--r--drivers/infiniband/core/mad.c112
-rw-r--r--drivers/infiniband/core/mad_priv.h3
-rw-r--r--drivers/infiniband/core/mad_rmpp.c54
-rw-r--r--drivers/infiniband/core/user_mad.c30
-rw-r--r--drivers/infiniband/hw/mthca/mthca_av.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_cq.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_eq.c6
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mad.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mcg.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_mr.c4
-rw-r--r--drivers/infiniband/hw/mthca/mthca_pd.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_qp.c2
-rw-r--r--drivers/infiniband/hw/mthca/mthca_srq.c4
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_main.c2
-rw-r--r--drivers/infiniband/ulp/srp/ib_srp.c4
-rw-r--r--drivers/input/keyboard/hil_kbd.c56
-rw-r--r--drivers/input/keyboard/hilkbd.c53
-rw-r--r--drivers/input/mouse/hil_ptr.c90
-rw-r--r--drivers/input/serio/gscps2.c4
-rw-r--r--drivers/isdn/sc/ioctl.c9
-rw-r--r--drivers/leds/Kconfig77
-rw-r--r--drivers/leds/Makefile16
-rw-r--r--drivers/leds/led-class.c167
-rw-r--r--drivers/leds/led-core.c25
-rw-r--r--drivers/leds/led-triggers.c239
-rw-r--r--drivers/leds/leds-corgi.c121
-rw-r--r--drivers/leds/leds-ixp4xx-gpio.c215
-rw-r--r--drivers/leds/leds-locomo.c95
-rw-r--r--drivers/leds/leds-spitz.c125
-rw-r--r--drivers/leds/leds-tosa.c131
-rw-r--r--drivers/leds/leds.h44
-rw-r--r--drivers/leds/ledtrig-ide-disk.c62
-rw-r--r--drivers/leds/ledtrig-timer.c170
-rw-r--r--drivers/macintosh/adb.c3
-rw-r--r--drivers/macintosh/adbhid.c4
-rw-r--r--drivers/macintosh/mediabay.c4
-rw-r--r--drivers/md/md.c8
-rw-r--r--drivers/md/raid1.c13
-rw-r--r--drivers/md/raid6main.c2
-rw-r--r--drivers/media/video/cpia_pp.c2
-rw-r--r--drivers/media/video/planb.c2
-rw-r--r--drivers/mmc/Kconfig11
-rw-r--r--drivers/mmc/Makefile5
-rw-r--r--drivers/mmc/au1xmmc.c19
-rw-r--r--drivers/mmc/mmc.c19
-rw-r--r--drivers/mmc/mmci.c4
-rw-r--r--drivers/mmc/omap.c1226
-rw-r--r--drivers/mmc/omap.h55
-rw-r--r--drivers/mmc/pxamci.c24
-rw-r--r--drivers/mmc/sdhci.c6
-rw-r--r--drivers/mmc/wbsd.c9
-rw-r--r--drivers/mtd/chips/amd_flash.c4
-rw-r--r--drivers/mtd/chips/jedec_probe.c19
-rw-r--r--drivers/mtd/chips/sharp.c7
-rw-r--r--drivers/mtd/cmdlinepart.c7
-rw-r--r--drivers/mtd/devices/blkmtd.c13
-rw-r--r--drivers/mtd/devices/block2mtd.c13
-rw-r--r--drivers/mtd/devices/doc2000.c37
-rw-r--r--drivers/mtd/devices/lart.c10
-rw-r--r--drivers/mtd/devices/m25p80.c2
-rw-r--r--drivers/mtd/devices/ms02-nv.c2
-rw-r--r--drivers/mtd/inftlcore.c7
-rw-r--r--drivers/mtd/maps/alchemy-flash.c4
-rw-r--r--drivers/mtd/maps/cfi_flagadm.c2
-rw-r--r--drivers/mtd/maps/dbox2-flash.c2
-rw-r--r--drivers/mtd/maps/dilnetpc.c4
-rw-r--r--drivers/mtd/maps/dmv182.c2
-rw-r--r--drivers/mtd/maps/h720x-flash.c2
-rw-r--r--drivers/mtd/maps/netsc520.c4
-rw-r--r--drivers/mtd/maps/nettel.c3
-rw-r--r--drivers/mtd/maps/ocotea.c6
-rw-r--r--drivers/mtd/maps/pci.c3
-rw-r--r--drivers/mtd/maps/pcmciamtd.c2
-rw-r--r--drivers/mtd/maps/redwood.c3
-rw-r--r--drivers/mtd/maps/sbc8240.c8
-rw-r--r--drivers/mtd/maps/sc520cdp.c2
-rw-r--r--drivers/mtd/maps/scx200_docflash.c2
-rw-r--r--drivers/mtd/maps/sharpsl-flash.c4
-rw-r--r--drivers/mtd/maps/ts5500_flash.c2
-rw-r--r--drivers/mtd/maps/uclinux.c2
-rw-r--r--drivers/mtd/maps/vmax301.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c32
-rw-r--r--drivers/mtd/mtdblock.c14
-rw-r--r--drivers/mtd/mtdcore.c45
-rw-r--r--drivers/mtd/nand/Kconfig17
-rw-r--r--drivers/mtd/nand/au1550nd.c4
-rw-r--r--drivers/mtd/nand/nand_base.c26
-rw-r--r--drivers/mtd/redboot.c6
-rw-r--r--drivers/net/3c59x.c33
-rw-r--r--drivers/net/8390.h2
-rw-r--r--drivers/net/Kconfig2
-rw-r--r--drivers/net/acenic_firmware.h10
-rw-r--r--drivers/net/b44.c14
-rw-r--r--drivers/net/bonding/bond_3ad.c28
-rw-r--r--drivers/net/bonding/bond_3ad.h1
-rw-r--r--drivers/net/bonding/bond_main.c97
-rw-r--r--drivers/net/bonding/bonding.h4
-rw-r--r--drivers/net/ixp2000/ixpdev.c5
-rw-r--r--drivers/net/natsemi.c18
-rw-r--r--drivers/net/netconsole.c2
-rw-r--r--drivers/net/pcmcia/axnet_cs.c61
-rw-r--r--drivers/net/pcmcia/xirc2ps_cs.c2
-rw-r--r--drivers/net/pcnet32.c4
-rw-r--r--drivers/net/spider_net.c4
-rw-r--r--drivers/net/tg3.c118
-rw-r--r--drivers/net/tokenring/Kconfig2
-rw-r--r--drivers/net/tulip/de4x5.c2
-rw-r--r--drivers/net/via-rhine.c21
-rw-r--r--drivers/net/wireless/Kconfig9
-rw-r--r--drivers/net/wireless/Makefile1
-rw-r--r--drivers/net/wireless/bcm43xx/Kconfig62
-rw-r--r--drivers/net/wireless/bcm43xx/Makefile12
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h926
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c499
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h117
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_dma.c968
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_dma.h218
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c50
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h8
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_ilt.c337
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_ilt.h32
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_leds.c293
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_leds.h56
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c3973
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.h168
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_phy.c2345
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_phy.h74
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_pio.c606
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_pio.h138
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_power.c358
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_power.h47
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_radio.c2026
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_radio.h99
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c322
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h25
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_wx.c1002
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_wx.h36
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_xmit.c582
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_xmit.h156
-rw-r--r--drivers/net/wireless/hostap/hostap_80211.h2
-rw-r--r--drivers/net/wireless/hostap/hostap_80211_tx.c9
-rw-r--r--drivers/parisc/ccio-dma.c2
-rw-r--r--drivers/parisc/dino.c5
-rw-r--r--drivers/parisc/eisa.c2
-rw-r--r--drivers/parisc/iosapic.c2
-rw-r--r--drivers/parisc/lba_pci.c8
-rw-r--r--drivers/parisc/pdc_stable.c5
-rw-r--r--drivers/parisc/sba_iommu.c6
-rw-r--r--drivers/parisc/superio.c48
-rw-r--r--drivers/pcmcia/vrc4171_card.c12
-rw-r--r--drivers/pcmcia/vrc4173_cardu.c8
-rw-r--r--drivers/scsi/ahci.c4
-rw-r--r--drivers/scsi/ata_piix.c4
-rw-r--r--drivers/scsi/ibmmca.c2
-rw-r--r--drivers/scsi/lasi700.c2
-rw-r--r--drivers/scsi/libata-bmdma.c26
-rw-r--r--drivers/scsi/libata-core.c196
-rw-r--r--drivers/scsi/libata-scsi.c8
-rw-r--r--drivers/scsi/libata.h2
-rw-r--r--drivers/scsi/mesh.c2
-rw-r--r--drivers/scsi/sata_mv.c42
-rw-r--r--drivers/scsi/zalon.c2
-rw-r--r--drivers/serial/8250_gsc.c15
-rw-r--r--drivers/serial/jsm/jsm_tty.c29
-rw-r--r--drivers/serial/mux.c4
-rw-r--r--drivers/usb/core/hcd-pci.c4
-rw-r--r--drivers/usb/net/zd1201.c2
-rw-r--r--drivers/video/Kconfig12
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/aty/aty128fb.c7
-rw-r--r--drivers/video/aty/atyfb_base.c7
-rw-r--r--drivers/video/aty/radeon_pm.c4
-rw-r--r--drivers/video/backlight/Kconfig4
-rw-r--r--drivers/video/backlight/backlight.c84
-rw-r--r--drivers/video/backlight/corgi_bl.c124
-rw-r--r--drivers/video/backlight/hp680_bl.c139
-rw-r--r--drivers/video/cfbimgblt.c2
-rw-r--r--drivers/video/cirrusfb.c4
-rw-r--r--drivers/video/console/fbcon.c11
-rw-r--r--drivers/video/console/sticore.c22
-rw-r--r--drivers/video/fbmem.c2
-rw-r--r--drivers/video/matrox/matroxfb_base.c3
-rw-r--r--drivers/video/nvidia/nvidia.c5
-rw-r--r--drivers/video/pxafb.c8
-rw-r--r--drivers/video/radeonfb.c3167
-rw-r--r--drivers/video/riva/fbdev.c9
-rw-r--r--drivers/video/sticore.h37
-rw-r--r--drivers/video/stifb.c95
-rw-r--r--drivers/video/w100fb.c162
-rw-r--r--drivers/video/w100fb.h748
234 files changed, 21041 insertions, 5152 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 9f5c0da57c90..5c91d6afb117 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -64,6 +64,8 @@ source "drivers/usb/Kconfig"
source "drivers/mmc/Kconfig"
+source "drivers/leds/Kconfig"
+
source "drivers/infiniband/Kconfig"
source "drivers/sn/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 424955274e60..d6e8ffbd8132 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_MCA) += mca/
obj-$(CONFIG_EISA) += eisa/
obj-$(CONFIG_CPU_FREQ) += cpufreq/
obj-$(CONFIG_MMC) += mmc/
+obj-$(CONFIG_NEW_LEDS) += leds/
obj-$(CONFIG_INFINIBAND) += infiniband/
obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
index 79b09d76c180..eee0864ba300 100644
--- a/drivers/acpi/ec.c
+++ b/drivers/acpi/ec.c
@@ -1572,7 +1572,7 @@ static void __exit acpi_ec_exit(void)
static int __init acpi_fake_ecdt_setup(char *str)
{
acpi_fake_ecdt_enabled = 1;
- return 0;
+ return 1;
}
__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup);
@@ -1591,7 +1591,7 @@ static int __init acpi_ec_set_intr_mode(char *str)
acpi_ec_driver.ops.add = acpi_ec_poll_add;
}
printk(KERN_INFO PREFIX "EC %s mode.\n", intr ? "interrupt" : "polling");
- return 0;
+ return 1;
}
__setup("ec_intr=", acpi_ec_set_intr_mode);
diff --git a/drivers/block/amiflop.c b/drivers/block/amiflop.c
index b6e290956214..2a8af685926f 100644
--- a/drivers/block/amiflop.c
+++ b/drivers/block/amiflop.c
@@ -1850,6 +1850,7 @@ static int __init amiga_floppy_setup (char *str)
return 0;
printk (KERN_INFO "amiflop: Setting default df0 to %x\n", n);
fd_def_df0 = n;
+ return 1;
}
__setup("floppy=", amiga_floppy_setup);
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 73d30bf01582..889cad07774e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -561,14 +561,31 @@ config TIPAR
If unsure, say N.
+config HVC_DRIVER
+ bool
+ help
+ Users of pSeries machines that want to utilize the hvc console front-end
+ module for their backend console driver should select this option.
+ It will automatically be selected if one of the back-end console drivers
+ is selected.
+
+
config HVC_CONSOLE
bool "pSeries Hypervisor Virtual Console support"
depends on PPC_PSERIES
+ select HVC_DRIVER
help
pSeries machines when partitioned support a hypervisor virtual
console. This driver allows each pSeries partition to have a console
which is accessed via the HMC.
+config HVC_RTAS
+ bool "IBM RTAS Console support"
+ depends on PPC_RTAS
+ select HVC_DRIVER
+ help
+ IBM Console device driver which makes use of RTAS
+
config HVCS
tristate "IBM Hypervisor Virtual Console Server support"
depends on PPC_PSERIES
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index b2a11245fa95..a73cb4956928 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -41,7 +41,9 @@ obj-$(CONFIG_N_HDLC) += n_hdlc.o
obj-$(CONFIG_AMIGA_BUILTIN_SERIAL) += amiserial.o
obj-$(CONFIG_SX) += sx.o generic_serial.o
obj-$(CONFIG_RIO) += rio/ generic_serial.o
-obj-$(CONFIG_HVC_CONSOLE) += hvc_console.o hvc_vio.o hvsi.o
+obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
+obj-$(CONFIG_HVC_CONSOLE) += hvc_vio.o hvsi.o
+obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
obj-$(CONFIG_MMTIMER) += mmtimer.o
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index 107df9fdba4e..edc72a6348a7 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -357,6 +357,12 @@ typedef struct drm_freelist {
spinlock_t lock;
} drm_freelist_t;
+typedef struct drm_dma_handle {
+ dma_addr_t busaddr;
+ void *vaddr;
+ size_t size;
+} drm_dma_handle_t;
+
/**
* Buffer entry. There is one of this for each buffer size order.
*/
@@ -366,7 +372,7 @@ typedef struct drm_buf_entry {
drm_buf_t *buflist; /**< buffer list */
int seg_count;
int page_order;
- unsigned long *seglist;
+ drm_dma_handle_t **seglist;
drm_freelist_t freelist;
} drm_buf_entry_t;
@@ -483,12 +489,6 @@ typedef struct drm_sigdata {
drm_hw_lock_t *lock;
} drm_sigdata_t;
-typedef struct drm_dma_handle {
- dma_addr_t busaddr;
- void *vaddr;
- size_t size;
-} drm_dma_handle_t;
-
/**
* Mappings list
*/
@@ -813,8 +813,6 @@ extern void drm_mem_init(void);
extern int drm_mem_info(char *buf, char **start, off_t offset,
int request, int *eof, void *data);
extern void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area);
-extern unsigned long drm_alloc_pages(int order, int area);
-extern void drm_free_pages(unsigned long address, int order, int area);
extern void *drm_ioremap(unsigned long offset, unsigned long size,
drm_device_t * dev);
extern void *drm_ioremap_nocache(unsigned long offset, unsigned long size,
diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c
index e2637b4d51de..8a9cf12e6183 100644
--- a/drivers/char/drm/drm_bufs.c
+++ b/drivers/char/drm/drm_bufs.c
@@ -474,8 +474,7 @@ static void drm_cleanup_buf_error(drm_device_t * dev, drm_buf_entry_t * entry)
if (entry->seg_count) {
for (i = 0; i < entry->seg_count; i++) {
if (entry->seglist[i]) {
- drm_free_pages(entry->seglist[i],
- entry->page_order, DRM_MEM_DMA);
+ drm_pci_free(dev, entry->seglist[i]);
}
}
drm_free(entry->seglist,
@@ -678,7 +677,7 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request)
int total;
int page_order;
drm_buf_entry_t *entry;
- unsigned long page;
+ drm_dma_handle_t *dmah;
drm_buf_t *buf;
int alignment;
unsigned long offset;
@@ -781,8 +780,10 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request)
page_count = 0;
while (entry->buf_count < count) {
- page = drm_alloc_pages(page_order, DRM_MEM_DMA);
- if (!page) {
+
+ dmah = drm_pci_alloc(dev, PAGE_SIZE << page_order, 0x1000, 0xfffffffful);
+
+ if (!dmah) {
/* Set count correctly so we free the proper amount. */
entry->buf_count = count;
entry->seg_count = count;
@@ -794,13 +795,13 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request)
atomic_dec(&dev->buf_alloc);
return -ENOMEM;
}
- entry->seglist[entry->seg_count++] = page;
+ entry->seglist[entry->seg_count++] = dmah;
for (i = 0; i < (1 << page_order); i++) {
DRM_DEBUG("page %d @ 0x%08lx\n",
dma->page_count + page_count,
- page + PAGE_SIZE * i);
+ (unsigned long)dmah->vaddr + PAGE_SIZE * i);
temp_pagelist[dma->page_count + page_count++]
- = page + PAGE_SIZE * i;
+ = (unsigned long)dmah->vaddr + PAGE_SIZE * i;
}
for (offset = 0;
offset + size <= total && entry->buf_count < count;
@@ -811,7 +812,8 @@ int drm_addbufs_pci(drm_device_t * dev, drm_buf_desc_t * request)
buf->order = order;
buf->used = 0;
buf->offset = (dma->byte_count + byte_count + offset);
- buf->address = (void *)(page + offset);
+ buf->address = (void *)(dmah->vaddr + offset);
+ buf->bus_address = dmah->busaddr + offset;
buf->next = NULL;
buf->waiting = 0;
buf->pending = 0;
diff --git a/drivers/char/drm/drm_dma.c b/drivers/char/drm/drm_dma.c
index 2afab95ca036..892db7096986 100644
--- a/drivers/char/drm/drm_dma.c
+++ b/drivers/char/drm/drm_dma.c
@@ -85,9 +85,7 @@ void drm_dma_takedown(drm_device_t * dev)
dma->bufs[i].seg_count);
for (j = 0; j < dma->bufs[i].seg_count; j++) {
if (dma->bufs[i].seglist[j]) {
- drm_free_pages(dma->bufs[i].seglist[j],
- dma->bufs[i].page_order,
- DRM_MEM_DMA);
+ drm_pci_free(dev, dma->bufs[i].seglist[j]);
}
}
drm_free(dma->bufs[i].seglist,
diff --git a/drivers/char/drm/drm_memory.c b/drivers/char/drm/drm_memory.c
index 8074771e348f..dddf8de66143 100644
--- a/drivers/char/drm/drm_memory.c
+++ b/drivers/char/drm/drm_memory.c
@@ -79,65 +79,6 @@ void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area)
return pt;
}
-/**
- * Allocate pages.
- *
- * \param order size order.
- * \param area memory area. (Not used.)
- * \return page address on success, or zero on failure.
- *
- * Allocate and reserve free pages.
- */
-unsigned long drm_alloc_pages(int order, int area)
-{
- unsigned long address;
- unsigned long bytes = PAGE_SIZE << order;
- unsigned long addr;
- unsigned int sz;
-
- address = __get_free_pages(GFP_KERNEL|__GFP_COMP, order);
- if (!address)
- return 0;
-
- /* Zero */
- memset((void *)address, 0, bytes);
-
- /* Reserve */
- for (addr = address, sz = bytes;
- sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
- SetPageReserved(virt_to_page(addr));
- }
-
- return address;
-}
-
-/**
- * Free pages.
- *
- * \param address address of the pages to free.
- * \param order size order.
- * \param area memory area. (Not used.)
- *
- * Unreserve and free pages allocated by alloc_pages().
- */
-void drm_free_pages(unsigned long address, int order, int area)
-{
- unsigned long bytes = PAGE_SIZE << order;
- unsigned long addr;
- unsigned int sz;
-
- if (!address)
- return;
-
- /* Unreserve */
- for (addr = address, sz = bytes;
- sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
- ClearPageReserved(virt_to_page(addr));
- }
-
- free_pages(address, order);
-}
-
#if __OS_HAS_AGP
/** Wrapper around agp_allocate_memory() */
DRM_AGP_MEM *drm_alloc_agp(drm_device_t * dev, int pages, u32 type)
diff --git a/drivers/char/drm/drm_memory_debug.h b/drivers/char/drm/drm_memory_debug.h
index e84605fc54af..7868341817da 100644
--- a/drivers/char/drm/drm_memory_debug.h
+++ b/drivers/char/drm/drm_memory_debug.h
@@ -206,76 +206,6 @@ void drm_free (void *pt, size_t size, int area) {
}
}
-unsigned long drm_alloc_pages (int order, int area) {
- unsigned long address;
- unsigned long bytes = PAGE_SIZE << order;
- unsigned long addr;
- unsigned int sz;
-
- spin_lock(&drm_mem_lock);
- if ((drm_ram_used >> PAGE_SHIFT)
- > (DRM_RAM_PERCENT * drm_ram_available) / 100) {
- spin_unlock(&drm_mem_lock);
- return 0;
- }
- spin_unlock(&drm_mem_lock);
-
- address = __get_free_pages(GFP_KERNEL|__GFP_COMP, order);
- if (!address) {
- spin_lock(&drm_mem_lock);
- ++drm_mem_stats[area].fail_count;
- spin_unlock(&drm_mem_lock);
- return 0;
- }
- spin_lock(&drm_mem_lock);
- ++drm_mem_stats[area].succeed_count;
- drm_mem_stats[area].bytes_allocated += bytes;
- drm_ram_used += bytes;
- spin_unlock(&drm_mem_lock);
-
- /* Zero outside the lock */
- memset((void *)address, 0, bytes);
-
- /* Reserve */
- for (addr = address, sz = bytes;
- sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
- SetPageReserved(virt_to_page(addr));
- }
-
- return address;
-}
-
-void drm_free_pages (unsigned long address, int order, int area) {
- unsigned long bytes = PAGE_SIZE << order;
- int alloc_count;
- int free_count;
- unsigned long addr;
- unsigned int sz;
-
- if (!address) {
- DRM_MEM_ERROR(area, "Attempt to free address 0\n");
- } else {
- /* Unreserve */
- for (addr = address, sz = bytes;
- sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
- ClearPageReserved(virt_to_page(addr));
- }
- free_pages(address, order);
- }
-
- spin_lock(&drm_mem_lock);
- free_count = ++drm_mem_stats[area].free_count;
- alloc_count = drm_mem_stats[area].succeed_count;
- drm_mem_stats[area].bytes_freed += bytes;
- drm_ram_used -= bytes;
- spin_unlock(&drm_mem_lock);
- if (free_count > alloc_count) {
- DRM_MEM_ERROR(area,
- "Excess frees: %d frees, %d allocs\n",
- free_count, alloc_count);
- }
-}
-
void *drm_ioremap (unsigned long offset, unsigned long size,
drm_device_t * dev) {
void *pt;
diff --git a/drivers/char/drm/drm_pci.c b/drivers/char/drm/drm_pci.c
index 1fd7ff164817..b28ca9cea8a2 100644
--- a/drivers/char/drm/drm_pci.c
+++ b/drivers/char/drm/drm_pci.c
@@ -50,6 +50,10 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align,
dma_addr_t maxaddr)
{
drm_dma_handle_t *dmah;
+#if 1
+ unsigned long addr;
+ size_t sz;
+#endif
#ifdef DRM_DEBUG_MEMORY
int area = DRM_MEM_DMA;
@@ -79,7 +83,7 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align,
return NULL;
dmah->size = size;
- dmah->vaddr = pci_alloc_consistent(dev->pdev, size, &dmah->busaddr);
+ dmah->vaddr = dma_alloc_coherent(&dev->pdev->dev, size, &dmah->busaddr, GFP_KERNEL | __GFP_COMP);
#ifdef DRM_DEBUG_MEMORY
if (dmah->vaddr == NULL) {
@@ -104,18 +108,29 @@ drm_dma_handle_t *drm_pci_alloc(drm_device_t * dev, size_t size, size_t align,
memset(dmah->vaddr, 0, size);
+ /* XXX - Is virt_to_page() legal for consistent mem? */
+ /* Reserve */
+ for (addr = (unsigned long)dmah->vaddr, sz = size;
+ sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+ SetPageReserved(virt_to_page(addr));
+ }
+
return dmah;
}
EXPORT_SYMBOL(drm_pci_alloc);
/**
- * \brief Free a PCI consistent memory block with freeing its descriptor.
+ * \brief Free a PCI consistent memory block without freeing its descriptor.
*
* This function is for internal use in the Linux-specific DRM core code.
*/
void __drm_pci_free(drm_device_t * dev, drm_dma_handle_t * dmah)
{
+#if 1
+ unsigned long addr;
+ size_t sz;
+#endif
#ifdef DRM_DEBUG_MEMORY
int area = DRM_MEM_DMA;
int alloc_count;
@@ -127,8 +142,14 @@ void __drm_pci_free(drm_device_t * dev, drm_dma_handle_t * dmah)
DRM_MEM_ERROR(area, "Attempt to free address 0\n");
#endif
} else {
- pci_free_consistent(dev->pdev, dmah->size, dmah->vaddr,
- dmah->busaddr);
+ /* XXX - Is virt_to_page() legal for consistent mem? */
+ /* Unreserve */
+ for (addr = (unsigned long)dmah->vaddr, sz = dmah->size;
+ sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
+ ClearPageReserved(virt_to_page(addr));
+ }
+ dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr,
+ dmah->busaddr);
}
#ifdef DRM_DEBUG_MEMORY
diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h
index 2c17e88a8847..b1bb3c7b568d 100644
--- a/drivers/char/drm/drm_pciids.h
+++ b/drivers/char/drm/drm_pciids.h
@@ -3,49 +3,69 @@
Please contact dri-devel@lists.sf.net to add new cards to this list
*/
#define radeon_PCI_IDS \
- {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350},\
+ {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
{0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP}, \
{0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \
{0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
+ {0x1002, 0x4148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+ {0x1002, 0x4149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+ {0x1002, 0x414A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+ {0x1002, 0x414B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4151, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4153, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+ {0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
- {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS250|CHIP_IS_IGP}, \
+ {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \
{0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
{0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS250|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \
- {0x1002, 0x4965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \
- {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \
- {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250}, \
- {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420}, \
- {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420}, \
+ {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
+ {0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
+ {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
{0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \
{0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \
{0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \
{0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R250|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \
{0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
- {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+ {0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E47, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
- {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
+ {0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
{0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
{0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
{0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
{0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
@@ -53,44 +73,66 @@
{0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
{0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
{0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x5149, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x514A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x514B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x514E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x514F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x5157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
{0x1002, 0x5158, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200}, \
{0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
{0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
{0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
- {0x1002, 0x5168, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x5169, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x516A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x516B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x516C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
- {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
+ {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
{0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \
{0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5836, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \
- {0x1002, 0x5837, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \
{0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
- {0x1002, 0x5963, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
- {0x1002, 0x5968, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+ {0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
- {0x1002, 0x596A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
- {0x1002, 0x596B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
+ {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
{0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5c62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5c64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
- {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
- {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420}, \
+ {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
{0, 0, 0}
#define r128_PCI_IDS \
diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c
index 1ff4c7ca0bff..9f4b8ce4c05e 100644
--- a/drivers/char/drm/i915_dma.c
+++ b/drivers/char/drm/i915_dma.c
@@ -495,8 +495,6 @@ static int i915_dispatch_batchbuffer(drm_device_t * dev,
}
}
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;
-
i915_emit_breadcrumb(dev);
return 0;
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c
index d3879ac9970f..a752afd86ab8 100644
--- a/drivers/char/drm/i915_irq.c
+++ b/drivers/char/drm/i915_irq.c
@@ -53,6 +53,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
I915_WRITE16(I915REG_INT_IDENTITY_R, temp);
+ dev_priv->sarea_priv->last_dispatch = READ_BREADCRUMB(dev_priv);
+
if (temp & USER_INT_FLAG)
DRM_WAKEUP(&dev_priv->irq_queue);
diff --git a/drivers/char/drm/r300_cmdbuf.c b/drivers/char/drm/r300_cmdbuf.c
index c08fa5076f05..b108c7f913b2 100644
--- a/drivers/char/drm/r300_cmdbuf.c
+++ b/drivers/char/drm/r300_cmdbuf.c
@@ -214,13 +214,13 @@ void r300_init_reg_flags(void)
ADD_RANGE(0x4F54, 1);
ADD_RANGE(R300_TX_FILTER_0, 16);
- ADD_RANGE(R300_TX_UNK1_0, 16);
+ ADD_RANGE(R300_TX_FILTER1_0, 16);
ADD_RANGE(R300_TX_SIZE_0, 16);
ADD_RANGE(R300_TX_FORMAT_0, 16);
ADD_RANGE(R300_TX_PITCH_0, 16);
/* Texture offset is dangerous and needs more checking */
ADD_RANGE_MARK(R300_TX_OFFSET_0, 16, MARK_CHECK_OFFSET);
- ADD_RANGE(R300_TX_UNK4_0, 16);
+ ADD_RANGE(R300_TX_CHROMA_KEY_0, 16);
ADD_RANGE(R300_TX_BORDER_COLOR_0, 16);
/* Sporadic registers used as primitives are emitted */
@@ -242,8 +242,10 @@ static __inline__ int r300_check_range(unsigned reg, int count)
return 0;
}
- /* we expect offsets passed to the framebuffer to be either within video memory or
- within AGP space */
+/*
+ * we expect offsets passed to the framebuffer to be either within video
+ * memory or within AGP space
+ */
static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv,
u32 offset)
{
@@ -251,11 +253,11 @@ static __inline__ int r300_check_offset(drm_radeon_private_t *dev_priv,
but this value is not being kept.
This code is correct for now (does the same thing as the
code that sets MC_FB_LOCATION) in radeon_cp.c */
- if ((offset >= dev_priv->fb_location) &&
- (offset < dev_priv->gart_vm_start))
+ if (offset >= dev_priv->fb_location &&
+ offset < (dev_priv->fb_location + dev_priv->fb_size))
return 0;
- if ((offset >= dev_priv->gart_vm_start) &&
- (offset < dev_priv->gart_vm_start + dev_priv->gart_size))
+ if (offset >= dev_priv->gart_vm_start &&
+ offset < (dev_priv->gart_vm_start + dev_priv->gart_size))
return 0;
return 1;
}
@@ -490,6 +492,7 @@ static __inline__ int r300_emit_3d_load_vbpntr(drm_radeon_private_t *dev_priv,
return 0;
}
+
static __inline__ int r300_emit_bitblt_multi(drm_radeon_private_t *dev_priv,
drm_radeon_kcmd_buffer_t *cmdbuf)
{
@@ -701,6 +704,64 @@ static void r300_discard_buffer(drm_device_t * dev, drm_buf_t * buf)
buf->used = 0;
}
+static int r300_scratch(drm_radeon_private_t *dev_priv,
+ drm_radeon_kcmd_buffer_t *cmdbuf,
+ drm_r300_cmd_header_t header)
+{
+ u32 *ref_age_base;
+ u32 i, buf_idx, h_pending;
+ RING_LOCALS;
+
+ if (cmdbuf->bufsz <
+ (sizeof(u64) + header.scratch.n_bufs * sizeof(buf_idx))) {
+ return DRM_ERR(EINVAL);
+ }
+
+ if (header.scratch.reg >= 5) {
+ return DRM_ERR(EINVAL);
+ }
+
+ dev_priv->scratch_ages[header.scratch.reg]++;
+
+ ref_age_base = *(u32 **)cmdbuf->buf;
+
+ cmdbuf->buf += sizeof(u64);
+ cmdbuf->bufsz -= sizeof(u64);
+
+ for (i=0; i < header.scratch.n_bufs; i++) {
+ buf_idx = *(u32 *)cmdbuf->buf;
+ buf_idx *= 2; /* 8 bytes per buf */
+
+ if (DRM_COPY_TO_USER(ref_age_base + buf_idx, &dev_priv->scratch_ages[header.scratch.reg], sizeof(u32))) {
+ return DRM_ERR(EINVAL);
+ }
+
+ if (DRM_COPY_FROM_USER(&h_pending, ref_age_base + buf_idx + 1, sizeof(u32))) {
+ return DRM_ERR(EINVAL);
+ }
+
+ if (h_pending == 0) {
+ return DRM_ERR(EINVAL);
+ }
+
+ h_pending--;
+
+ if (DRM_COPY_TO_USER(ref_age_base + buf_idx + 1, &h_pending, sizeof(u32))) {
+ return DRM_ERR(EINVAL);
+ }
+
+ cmdbuf->buf += sizeof(buf_idx);
+ cmdbuf->bufsz -= sizeof(buf_idx);
+ }
+
+ BEGIN_RING(2);
+ OUT_RING(CP_PACKET0(RADEON_SCRATCH_REG0 + header.scratch.reg * 4, 0));
+ OUT_RING(dev_priv->scratch_ages[header.scratch.reg]);
+ ADVANCE_RING();
+
+ return 0;
+}
+
/**
* Parses and validates a user-supplied command buffer and emits appropriate
* commands on the DMA ring buffer.
@@ -838,6 +899,15 @@ int r300_do_cp_cmdbuf(drm_device_t *dev,
}
break;
+ case R300_CMD_SCRATCH:
+ DRM_DEBUG("R300_CMD_SCRATCH\n");
+ ret = r300_scratch(dev_priv, cmdbuf, header);
+ if (ret) {
+ DRM_ERROR("r300_scratch failed\n");
+ goto cleanup;
+ }
+ break;
+
default:
DRM_ERROR("bad cmd_type %i at %p\n",
header.header.cmd_type,
diff --git a/drivers/char/drm/r300_reg.h b/drivers/char/drm/r300_reg.h
index d1e19954406b..a881f96c983e 100644
--- a/drivers/char/drm/r300_reg.h
+++ b/drivers/char/drm/r300_reg.h
@@ -711,8 +711,22 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_TX_MAX_ANISO_16_TO_1 (8 << 21)
# define R300_TX_MAX_ANISO_MASK (14 << 21)
-#define R300_TX_UNK1_0 0x4440
+#define R300_TX_FILTER1_0 0x4440
+# define R300_CHROMA_KEY_MODE_DISABLE 0
+# define R300_CHROMA_KEY_FORCE 1
+# define R300_CHROMA_KEY_BLEND 2
+# define R300_MC_ROUND_NORMAL (0<<2)
+# define R300_MC_ROUND_MPEG4 (1<<2)
# define R300_LOD_BIAS_MASK 0x1fff
+# define R300_EDGE_ANISO_EDGE_DIAG (0<<13)
+# define R300_EDGE_ANISO_EDGE_ONLY (1<<13)
+# define R300_MC_COORD_TRUNCATE_DISABLE (0<<14)
+# define R300_MC_COORD_TRUNCATE_MPEG (1<<14)
+# define R300_TX_TRI_PERF_0_8 (0<<15)
+# define R300_TX_TRI_PERF_1_8 (1<<15)
+# define R300_TX_TRI_PERF_1_4 (2<<15)
+# define R300_TX_TRI_PERF_3_8 (3<<15)
+# define R300_ANISO_THRESHOLD_MASK (7<<17)
#define R300_TX_SIZE_0 0x4480
# define R300_TX_WIDTHMASK_SHIFT 0
@@ -722,6 +736,8 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_TX_UNK23 (1 << 23)
# define R300_TX_SIZE_SHIFT 26 /* largest of width, height */
# define R300_TX_SIZE_MASK (15 << 26)
+# define R300_TX_SIZE_PROJECTED (1<<30)
+# define R300_TX_SIZE_TXPITCH_EN (1<<31)
#define R300_TX_FORMAT_0 0x44C0
/* The interpretation of the format word by Wladimir van der Laan */
/* The X, Y, Z and W refer to the layout of the components.
@@ -750,7 +766,8 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_TX_FORMAT_B8G8_B8G8 0x14 /* no swizzle */
# define R300_TX_FORMAT_G8R8_G8B8 0x15 /* no swizzle */
/* 0x16 - some 16 bit green format.. ?? */
-# define R300_TX_FORMAT_UNK25 (1 << 25) /* no swizzle */
+# define R300_TX_FORMAT_UNK25 (1 << 25) /* no swizzle */
+# define R300_TX_FORMAT_CUBIC_MAP (1 << 26)
/* gap */
/* Floating point formats */
@@ -800,18 +817,20 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_TX_FORMAT_YUV_MODE 0x00800000
-#define R300_TX_PITCH_0 0x4500
+#define R300_TX_PITCH_0 0x4500 /* obvious missing in gap */
#define R300_TX_OFFSET_0 0x4540
/* BEGIN: Guess from R200 */
# define R300_TXO_ENDIAN_NO_SWAP (0 << 0)
# define R300_TXO_ENDIAN_BYTE_SWAP (1 << 0)
# define R300_TXO_ENDIAN_WORD_SWAP (2 << 0)
# define R300_TXO_ENDIAN_HALFDW_SWAP (3 << 0)
+# define R300_TXO_MACRO_TILE (1 << 2)
+# define R300_TXO_MICRO_TILE (1 << 3)
# define R300_TXO_OFFSET_MASK 0xffffffe0
# define R300_TXO_OFFSET_SHIFT 5
/* END */
-#define R300_TX_UNK4_0 0x4580
-#define R300_TX_BORDER_COLOR_0 0x45C0 //ff00ff00 == { 0, 1.0, 0, 1.0 }
+#define R300_TX_CHROMA_KEY_0 0x4580 /* 32 bit chroma key */
+#define R300_TX_BORDER_COLOR_0 0x45C0 //ff00ff00 == { 0, 1.0, 0, 1.0 }
/* END */
@@ -868,7 +887,9 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_PFS_NODE_TEX_OFFSET_MASK (31 << 12)
# define R300_PFS_NODE_TEX_END_SHIFT 17
# define R300_PFS_NODE_TEX_END_MASK (31 << 17)
-# define R300_PFS_NODE_LAST_NODE (1 << 22)
+/*# define R300_PFS_NODE_LAST_NODE (1 << 22) */
+# define R300_PFS_NODE_OUTPUT_COLOR (1 << 22)
+# define R300_PFS_NODE_OUTPUT_DEPTH (1 << 23)
/* TEX
// As far as I can tell, texture instructions cannot write into output
@@ -887,6 +908,7 @@ I am fairly certain that they are correct unless stated otherwise in comments.
*/
# define R300_FPITX_OPCODE_SHIFT 15
# define R300_FPITX_OP_TEX 1
+# define R300_FPITX_OP_KIL 2
# define R300_FPITX_OP_TXP 3
# define R300_FPITX_OP_TXB 4
@@ -962,9 +984,11 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_FPI1_SRC2C_CONST (1 << 17)
# define R300_FPI1_DSTC_SHIFT 18
# define R300_FPI1_DSTC_MASK (31 << 18)
+# define R300_FPI1_DSTC_REG_MASK_SHIFT 23
# define R300_FPI1_DSTC_REG_X (1 << 23)
# define R300_FPI1_DSTC_REG_Y (1 << 24)
# define R300_FPI1_DSTC_REG_Z (1 << 25)
+# define R300_FPI1_DSTC_OUTPUT_MASK_SHIFT 26
# define R300_FPI1_DSTC_OUTPUT_X (1 << 26)
# define R300_FPI1_DSTC_OUTPUT_Y (1 << 27)
# define R300_FPI1_DSTC_OUTPUT_Z (1 << 28)
@@ -983,6 +1007,7 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_FPI3_DSTA_MASK (31 << 18)
# define R300_FPI3_DSTA_REG (1 << 23)
# define R300_FPI3_DSTA_OUTPUT (1 << 24)
+# define R300_FPI3_DSTA_DEPTH (1 << 27)
#define R300_PFS_INSTR0_0 0x48C0
# define R300_FPI0_ARGC_SRC0C_XYZ 0
@@ -1036,7 +1061,7 @@ I am fairly certain that they are correct unless stated otherwise in comments.
# define R300_FPI0_OUTC_FRC (9 << 23)
# define R300_FPI0_OUTC_REPL_ALPHA (10 << 23)
# define R300_FPI0_OUTC_SAT (1 << 30)
-# define R300_FPI0_UNKNOWN_31 (1 << 31)
+# define R300_FPI0_INSERT_NOP (1 << 31)
#define R300_PFS_INSTR2_0 0x49C0
# define R300_FPI2_ARGA_SRC0C_X 0
diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c
index 9bb8ae0c1c27..7f949c9c9691 100644
--- a/drivers/char/drm/radeon_cp.c
+++ b/drivers/char/drm/radeon_cp.c
@@ -1118,14 +1118,20 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
{
u32 ring_start, cur_read_ptr;
u32 tmp;
-
- /* Initialize the memory controller */
- RADEON_WRITE(RADEON_MC_FB_LOCATION,
- ((dev_priv->gart_vm_start - 1) & 0xffff0000)
- | (dev_priv->fb_location >> 16));
+
+ /* Initialize the memory controller. With new memory map, the fb location
+ * is not changed, it should have been properly initialized already. Part
+ * of the problem is that the code below is bogus, assuming the GART is
+ * always appended to the fb which is not necessarily the case
+ */
+ if (!dev_priv->new_memmap)
+ RADEON_WRITE(RADEON_MC_FB_LOCATION,
+ ((dev_priv->gart_vm_start - 1) & 0xffff0000)
+ | (dev_priv->fb_location >> 16));
#if __OS_HAS_AGP
if (dev_priv->flags & CHIP_IS_AGP) {
+ RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base);
RADEON_WRITE(RADEON_MC_AGP_LOCATION,
(((dev_priv->gart_vm_start - 1 +
dev_priv->gart_size) & 0xffff0000) |
@@ -1153,8 +1159,6 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
#if __OS_HAS_AGP
if (dev_priv->flags & CHIP_IS_AGP) {
- /* set RADEON_AGP_BASE here instead of relying on X from user space */
- RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base);
RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR,
dev_priv->ring_rptr->offset
- dev->agp->base + dev_priv->gart_vm_start);
@@ -1174,6 +1178,17 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
entry->handle + tmp_ofs);
}
+ /* Set ring buffer size */
+#ifdef __BIG_ENDIAN
+ RADEON_WRITE(RADEON_CP_RB_CNTL,
+ dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT);
+#else
+ RADEON_WRITE(RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw);
+#endif
+
+ /* Start with assuming that writeback doesn't work */
+ dev_priv->writeback_works = 0;
+
/* Initialize the scratch register pointer. This will cause
* the scratch register values to be written out to memory
* whenever they are updated.
@@ -1190,28 +1205,9 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
RADEON_WRITE(RADEON_SCRATCH_UMSK, 0x7);
- /* Writeback doesn't seem to work everywhere, test it first */
- DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0);
- RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef);
-
- for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) {
- if (DRM_READ32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1)) ==
- 0xdeadbeef)
- break;
- DRM_UDELAY(1);
- }
-
- if (tmp < dev_priv->usec_timeout) {
- dev_priv->writeback_works = 1;
- DRM_DEBUG("writeback test succeeded, tmp=%d\n", tmp);
- } else {
- dev_priv->writeback_works = 0;
- DRM_DEBUG("writeback test failed\n");
- }
- if (radeon_no_wb == 1) {
- dev_priv->writeback_works = 0;
- DRM_DEBUG("writeback forced off\n");
- }
+ /* Turn on bus mastering */
+ tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
+ RADEON_WRITE(RADEON_BUS_CNTL, tmp);
dev_priv->sarea_priv->last_frame = dev_priv->scratch[0] = 0;
RADEON_WRITE(RADEON_LAST_FRAME_REG, dev_priv->sarea_priv->last_frame);
@@ -1223,26 +1219,45 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
dev_priv->sarea_priv->last_clear = dev_priv->scratch[2] = 0;
RADEON_WRITE(RADEON_LAST_CLEAR_REG, dev_priv->sarea_priv->last_clear);
- /* Set ring buffer size */
-#ifdef __BIG_ENDIAN
- RADEON_WRITE(RADEON_CP_RB_CNTL,
- dev_priv->ring.size_l2qw | RADEON_BUF_SWAP_32BIT);
-#else
- RADEON_WRITE(RADEON_CP_RB_CNTL, dev_priv->ring.size_l2qw);
-#endif
-
radeon_do_wait_for_idle(dev_priv);
- /* Turn on bus mastering */
- tmp = RADEON_READ(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
- RADEON_WRITE(RADEON_BUS_CNTL, tmp);
-
/* Sync everything up */
RADEON_WRITE(RADEON_ISYNC_CNTL,
(RADEON_ISYNC_ANY2D_IDLE3D |
RADEON_ISYNC_ANY3D_IDLE2D |
RADEON_ISYNC_WAIT_IDLEGUI |
RADEON_ISYNC_CPSCRATCH_IDLEGUI));
+
+}
+
+static void radeon_test_writeback(drm_radeon_private_t * dev_priv)
+{
+ u32 tmp;
+
+ /* Writeback doesn't seem to work everywhere, test it here and possibly
+ * enable it if it appears to work
+ */
+ DRM_WRITE32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1), 0);
+ RADEON_WRITE(RADEON_SCRATCH_REG1, 0xdeadbeef);
+
+ for (tmp = 0; tmp < dev_priv->usec_timeout; tmp++) {
+ if (DRM_READ32(dev_priv->ring_rptr, RADEON_SCRATCHOFF(1)) ==
+ 0xdeadbeef)
+ break;
+ DRM_UDELAY(1);
+ }
+
+ if (tmp < dev_priv->usec_timeout) {
+ dev_priv->writeback_works = 1;
+ DRM_INFO("writeback test succeeded in %d usecs\n", tmp);
+ } else {
+ dev_priv->writeback_works = 0;
+ DRM_INFO("writeback test failed\n");
+ }
+ if (radeon_no_wb == 1) {
+ dev_priv->writeback_works = 0;
+ DRM_INFO("writeback forced off\n");
+ }
}
/* Enable or disable PCI-E GART on the chip */
@@ -1317,6 +1332,14 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
DRM_DEBUG("\n");
+ /* if we require new memory map but we don't have it fail */
+ if ((dev_priv->flags & CHIP_NEW_MEMMAP) && !dev_priv->new_memmap)
+ {
+ DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX\n");
+ radeon_do_cleanup_cp(dev);
+ return DRM_ERR(EINVAL);
+ }
+
if (init->is_pci && (dev_priv->flags & CHIP_IS_AGP))
{
DRM_DEBUG("Forcing AGP card to PCI mode\n");
@@ -1496,6 +1519,9 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
dev_priv->fb_location = (RADEON_READ(RADEON_MC_FB_LOCATION)
& 0xffff) << 16;
+ dev_priv->fb_size =
+ ((RADEON_READ(RADEON_MC_FB_LOCATION) & 0xffff0000u) + 0x10000)
+ - dev_priv->fb_location;
dev_priv->front_pitch_offset = (((dev_priv->front_pitch / 64) << 22) |
((dev_priv->front_offset
@@ -1510,8 +1536,46 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
+ dev_priv->fb_location) >> 10));
dev_priv->gart_size = init->gart_size;
- dev_priv->gart_vm_start = dev_priv->fb_location
- + RADEON_READ(RADEON_CONFIG_APER_SIZE);
+
+ /* New let's set the memory map ... */
+ if (dev_priv->new_memmap) {
+ u32 base = 0;
+
+ DRM_INFO("Setting GART location based on new memory map\n");
+
+ /* If using AGP, try to locate the AGP aperture at the same
+ * location in the card and on the bus, though we have to
+ * align it down.
+ */
+#if __OS_HAS_AGP
+ if (dev_priv->flags & CHIP_IS_AGP) {
+ base = dev->agp->base;
+ /* Check if valid */
+ if ((base + dev_priv->gart_size) > dev_priv->fb_location &&
+ base < (dev_priv->fb_location + dev_priv->fb_size)) {
+ DRM_INFO("Can't use AGP base @0x%08lx, won't fit\n",
+ dev->agp->base);
+ base = 0;
+ }
+ }
+#endif
+ /* If not or if AGP is at 0 (Macs), try to put it elsewhere */
+ if (base == 0) {
+ base = dev_priv->fb_location + dev_priv->fb_size;
+ if (((base + dev_priv->gart_size) & 0xfffffffful)
+ < base)
+ base = dev_priv->fb_location
+ - dev_priv->gart_size;
+ }
+ dev_priv->gart_vm_start = base & 0xffc00000u;
+ if (dev_priv->gart_vm_start != base)
+ DRM_INFO("GART aligned down from 0x%08x to 0x%08x\n",
+ base, dev_priv->gart_vm_start);
+ } else {
+ DRM_INFO("Setting GART location based on old memory map\n");
+ dev_priv->gart_vm_start = dev_priv->fb_location +
+ RADEON_READ(RADEON_CONFIG_APER_SIZE);
+ }
#if __OS_HAS_AGP
if (dev_priv->flags & CHIP_IS_AGP)
@@ -1596,6 +1660,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
dev_priv->last_buf = 0;
radeon_do_engine_reset(dev);
+ radeon_test_writeback(dev_priv);
return 0;
}
diff --git a/drivers/char/drm/radeon_drm.h b/drivers/char/drm/radeon_drm.h
index 9c177a6b2a4c..c8e279e89c2e 100644
--- a/drivers/char/drm/radeon_drm.h
+++ b/drivers/char/drm/radeon_drm.h
@@ -222,6 +222,7 @@ typedef union {
# define R300_WAIT_3D 0x2
# define R300_WAIT_2D_CLEAN 0x3
# define R300_WAIT_3D_CLEAN 0x4
+#define R300_CMD_SCRATCH 8
typedef union {
unsigned int u;
@@ -247,6 +248,9 @@ typedef union {
struct {
unsigned char cmd_type, flags, pad0, pad1;
} wait;
+ struct {
+ unsigned char cmd_type, reg, n_bufs, flags;
+ } scratch;
} drm_r300_cmd_header_t;
#define RADEON_FRONT 0x1
@@ -697,6 +701,7 @@ typedef struct drm_radeon_setparam {
#define RADEON_SETPARAM_FB_LOCATION 1 /* determined framebuffer location */
#define RADEON_SETPARAM_SWITCH_TILING 2 /* enable/disable color tiling */
#define RADEON_SETPARAM_PCIGART_LOCATION 3 /* PCI Gart Location */
+#define RADEON_SETPARAM_NEW_MEMMAP 4 /* Use new memory map */
/* 1.14: Clients can allocate/free a surface
*/
diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h
index 1f7d2ab8c4fc..78345cee8f8e 100644
--- a/drivers/char/drm/radeon_drv.h
+++ b/drivers/char/drm/radeon_drv.h
@@ -38,7 +38,7 @@
#define DRIVER_NAME "radeon"
#define DRIVER_DESC "ATI Radeon"
-#define DRIVER_DATE "20051229"
+#define DRIVER_DATE "20060225"
/* Interface history:
*
@@ -91,9 +91,11 @@
* 1.20- Add support for r300 texrect
* 1.21- Add support for card type getparam
* 1.22- Add support for texture cache flushes (R300_TX_CNTL)
+ * 1.23- Add new radeon memory map work from benh
+ * 1.24- Add general-purpose packet for manipulating scratch registers (r300)
*/
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 22
+#define DRIVER_MINOR 24
#define DRIVER_PATCHLEVEL 0
/*
@@ -101,20 +103,21 @@
*/
enum radeon_family {
CHIP_R100,
- CHIP_RS100,
CHIP_RV100,
+ CHIP_RS100,
CHIP_RV200,
- CHIP_R200,
CHIP_RS200,
- CHIP_R250,
- CHIP_RS250,
+ CHIP_R200,
CHIP_RV250,
+ CHIP_RS300,
CHIP_RV280,
CHIP_R300,
- CHIP_RS300,
CHIP_R350,
CHIP_RV350,
+ CHIP_RV380,
CHIP_R420,
+ CHIP_RV410,
+ CHIP_RS400,
CHIP_LAST,
};
@@ -136,9 +139,11 @@ enum radeon_chip_flags {
CHIP_IS_AGP = 0x00080000UL,
CHIP_HAS_HIERZ = 0x00100000UL,
CHIP_IS_PCIE = 0x00200000UL,
+ CHIP_NEW_MEMMAP = 0x00400000UL,
};
-#define GET_RING_HEAD(dev_priv) DRM_READ32( (dev_priv)->ring_rptr, 0 )
+#define GET_RING_HEAD(dev_priv) (dev_priv->writeback_works ? \
+ DRM_READ32( (dev_priv)->ring_rptr, 0 ) : RADEON_READ(RADEON_CP_RB_RPTR))
#define SET_RING_HEAD(dev_priv,val) DRM_WRITE32( (dev_priv)->ring_rptr, 0, (val) )
typedef struct drm_radeon_freelist {
@@ -199,6 +204,8 @@ typedef struct drm_radeon_private {
drm_radeon_sarea_t *sarea_priv;
u32 fb_location;
+ u32 fb_size;
+ int new_memmap;
int gart_size;
u32 gart_vm_start;
@@ -272,6 +279,8 @@ typedef struct drm_radeon_private {
unsigned long pcigart_offset;
drm_ati_pcigart_info gart_info;
+ u32 scratch_ages[5];
+
/* starting from here on, data is preserved accross an open */
uint32_t flags; /* see radeon_chip_flags */
} drm_radeon_private_t;
diff --git a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c
index 7bc27516d425..c5b8f774a599 100644
--- a/drivers/char/drm/radeon_state.c
+++ b/drivers/char/drm/radeon_state.c
@@ -45,22 +45,53 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
u32 off = *offset;
struct drm_radeon_driver_file_fields *radeon_priv;
- if (off >= dev_priv->fb_location &&
- off < (dev_priv->gart_vm_start + dev_priv->gart_size))
- return 0;
-
- radeon_priv = filp_priv->driver_priv;
- off += radeon_priv->radeon_fb_delta;
+ /* Hrm ... the story of the offset ... So this function converts
+ * the various ideas of what userland clients might have for an
+ * offset in the card address space into an offset into the card
+ * address space :) So with a sane client, it should just keep
+ * the value intact and just do some boundary checking. However,
+ * not all clients are sane. Some older clients pass us 0 based
+ * offsets relative to the start of the framebuffer and some may
+ * assume the AGP aperture it appended to the framebuffer, so we
+ * try to detect those cases and fix them up.
+ *
+ * Note: It might be a good idea here to make sure the offset lands
+ * in some "allowed" area to protect things like the PCIE GART...
+ */
- DRM_DEBUG("offset fixed up to 0x%x\n", off);
+ /* First, the best case, the offset already lands in either the
+ * framebuffer or the GART mapped space
+ */
+ if ((off >= dev_priv->fb_location &&
+ off < (dev_priv->fb_location + dev_priv->fb_size)) ||
+ (off >= dev_priv->gart_vm_start &&
+ off < (dev_priv->gart_vm_start + dev_priv->gart_size)))
+ return 0;
- if (off < dev_priv->fb_location ||
- off >= (dev_priv->gart_vm_start + dev_priv->gart_size))
- return DRM_ERR(EINVAL);
+ /* Ok, that didn't happen... now check if we have a zero based
+ * offset that fits in the framebuffer + gart space, apply the
+ * magic offset we get from SETPARAM or calculated from fb_location
+ */
+ if (off < (dev_priv->fb_size + dev_priv->gart_size)) {
+ radeon_priv = filp_priv->driver_priv;
+ off += radeon_priv->radeon_fb_delta;
+ }
- *offset = off;
+ /* Finally, assume we aimed at a GART offset if beyond the fb */
+ if (off > (dev_priv->fb_location + dev_priv->fb_size))
+ off = off - (dev_priv->fb_location + dev_priv->fb_size) +
+ dev_priv->gart_vm_start;
- return 0;
+ /* Now recheck and fail if out of bounds */
+ if ((off >= dev_priv->fb_location &&
+ off < (dev_priv->fb_location + dev_priv->fb_size)) ||
+ (off >= dev_priv->gart_vm_start &&
+ off < (dev_priv->gart_vm_start + dev_priv->gart_size))) {
+ DRM_DEBUG("offset fixed up to 0x%x\n", off);
+ *offset = off;
+ return 0;
+ }
+ return DRM_ERR(EINVAL);
}
static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t *
@@ -1939,11 +1970,6 @@ static int radeon_surface_alloc(DRM_IOCTL_ARGS)
drm_radeon_private_t *dev_priv = dev->dev_private;
drm_radeon_surface_alloc_t alloc;
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_COPY_FROM_USER_IOCTL(alloc,
(drm_radeon_surface_alloc_t __user *) data,
sizeof(alloc));
@@ -1960,12 +1986,7 @@ static int radeon_surface_free(DRM_IOCTL_ARGS)
drm_radeon_private_t *dev_priv = dev->dev_private;
drm_radeon_surface_free_t memfree;
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
- DRM_COPY_FROM_USER_IOCTL(memfree, (drm_radeon_mem_free_t __user *) data,
+ DRM_COPY_FROM_USER_IOCTL(memfree, (drm_radeon_surface_free_t __user *) data,
sizeof(memfree));
if (free_surface(filp, dev_priv, memfree.address))
@@ -2100,11 +2121,6 @@ static int radeon_cp_vertex(DRM_IOCTL_ARGS)
LOCK_TEST_WITH_RETURN(dev, filp);
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_GET_PRIV_WITH_RETURN(filp_priv, filp);
DRM_COPY_FROM_USER_IOCTL(vertex, (drm_radeon_vertex_t __user *) data,
@@ -2189,11 +2205,6 @@ static int radeon_cp_indices(DRM_IOCTL_ARGS)
LOCK_TEST_WITH_RETURN(dev, filp);
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_GET_PRIV_WITH_RETURN(filp_priv, filp);
DRM_COPY_FROM_USER_IOCTL(elts, (drm_radeon_indices_t __user *) data,
@@ -2340,11 +2351,6 @@ static int radeon_cp_indirect(DRM_IOCTL_ARGS)
LOCK_TEST_WITH_RETURN(dev, filp);
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_COPY_FROM_USER_IOCTL(indirect,
(drm_radeon_indirect_t __user *) data,
sizeof(indirect));
@@ -2417,11 +2423,6 @@ static int radeon_cp_vertex2(DRM_IOCTL_ARGS)
LOCK_TEST_WITH_RETURN(dev, filp);
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_GET_PRIV_WITH_RETURN(filp_priv, filp);
DRM_COPY_FROM_USER_IOCTL(vertex, (drm_radeon_vertex2_t __user *) data,
@@ -2738,11 +2739,6 @@ static int radeon_cp_cmdbuf(DRM_IOCTL_ARGS)
LOCK_TEST_WITH_RETURN(dev, filp);
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_GET_PRIV_WITH_RETURN(filp_priv, filp);
DRM_COPY_FROM_USER_IOCTL(cmdbuf,
@@ -2897,11 +2893,6 @@ static int radeon_cp_getparam(DRM_IOCTL_ARGS)
drm_radeon_getparam_t param;
int value;
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_COPY_FROM_USER_IOCTL(param, (drm_radeon_getparam_t __user *) data,
sizeof(param));
@@ -2981,11 +2972,6 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS)
drm_radeon_setparam_t sp;
struct drm_radeon_driver_file_fields *radeon_priv;
- if (!dev_priv) {
- DRM_ERROR("%s called with no initialization\n", __FUNCTION__);
- return DRM_ERR(EINVAL);
- }
-
DRM_GET_PRIV_WITH_RETURN(filp_priv, filp);
DRM_COPY_FROM_USER_IOCTL(sp, (drm_radeon_setparam_t __user *) data,
@@ -3012,6 +2998,9 @@ static int radeon_cp_setparam(DRM_IOCTL_ARGS)
case RADEON_SETPARAM_PCIGART_LOCATION:
dev_priv->pcigart_offset = sp.value;
break;
+ case RADEON_SETPARAM_NEW_MEMMAP:
+ dev_priv->new_memmap = sp.value;
+ break;
default:
DRM_DEBUG("Invalid parameter %d\n", sp.param);
return DRM_ERR(EINVAL);
diff --git a/drivers/char/drm/sis_mm.c b/drivers/char/drm/sis_mm.c
index 6774d2fe3452..5e9936bc307f 100644
--- a/drivers/char/drm/sis_mm.c
+++ b/drivers/char/drm/sis_mm.c
@@ -110,7 +110,7 @@ static int sis_fb_alloc(DRM_IOCTL_ARGS)
DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb));
- DRM_DEBUG("alloc fb, size = %d, offset = %ld\n", fb.size, req.offset);
+ DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, req.offset);
return retval;
}
diff --git a/drivers/char/generic_nvram.c b/drivers/char/generic_nvram.c
index 1b5e01e6e129..43ff59816511 100644
--- a/drivers/char/generic_nvram.c
+++ b/drivers/char/generic_nvram.c
@@ -22,6 +22,9 @@
#include <linux/smp_lock.h>
#include <asm/uaccess.h>
#include <asm/nvram.h>
+#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
+#endif
#define NVRAM_SIZE 8192
@@ -92,7 +95,7 @@ static int nvram_ioctl(struct inode *inode, struct file *file,
case IOC_NVRAM_GET_OFFSET: {
int part, offset;
- if (_machine != _MACH_Pmac)
+ if (!machine_is(powermac))
return -EINVAL;
if (copy_from_user(&part, (void __user*)arg, sizeof(part)) != 0)
return -EFAULT;
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index f65b2e14a485..2b6a56b2bf35 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -39,8 +39,10 @@
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/delay.h>
+
#include <asm/uaccess.h>
-#include <asm/hvconsole.h>
+
+#include "hvc_console.h"
#define HVC_MAJOR 229
#define HVC_MINOR 0
@@ -54,17 +56,14 @@
#define HVC_CLOSE_WAIT (HZ/100) /* 1/10 of a second */
/*
- * The Linux TTY code does not support dynamic addition of tty derived devices
- * so we need to know how many tty devices we might need when space is allocated
- * for the tty device. Since this driver supports hotplug of vty adapters we
- * need to make sure we have enough allocated.
+ * These sizes are most efficient for vio, because they are the
+ * native transfer size. We could make them selectable in the
+ * future to better deal with backends that want other buffer sizes.
*/
-#define HVC_ALLOC_TTY_ADAPTERS 8
-
#define N_OUTBUF 16
#define N_INBUF 16
-#define __ALIGNED__ __attribute__((__aligned__(8)))
+#define __ALIGNED__ __attribute__((__aligned__(sizeof(long))))
static struct tty_driver *hvc_driver;
static struct task_struct *hvc_task;
@@ -154,7 +153,7 @@ static uint32_t vtermnos[MAX_NR_HVC_CONSOLES] =
void hvc_console_print(struct console *co, const char *b, unsigned count)
{
- char c[16] __ALIGNED__;
+ char c[N_OUTBUF] __ALIGNED__;
unsigned i = 0, n = 0;
int r, donecr = 0, index = co->index;
@@ -473,8 +472,10 @@ static void hvc_push(struct hvc_struct *hp)
n = hp->ops->put_chars(hp->vtermno, hp->outbuf, hp->n_outbuf);
if (n <= 0) {
- if (n == 0)
+ if (n == 0) {
+ hp->do_wakeup = 1;
return;
+ }
/* throw away output on error; this happens when
there is no session connected to the vterm. */
hp->n_outbuf = 0;
@@ -486,12 +487,19 @@ static void hvc_push(struct hvc_struct *hp)
hp->do_wakeup = 1;
}
-static inline int __hvc_write_kernel(struct hvc_struct *hp,
- const unsigned char *buf, int count)
+static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
+ struct hvc_struct *hp = tty->driver_data;
unsigned long flags;
int rsize, written = 0;
+ /* This write was probably executed during a tty close. */
+ if (!hp)
+ return -EPIPE;
+
+ if (hp->count <= 0)
+ return -EIO;
+
spin_lock_irqsave(&hp->lock, flags);
/* Push pending writes */
@@ -510,26 +518,8 @@ static inline int __hvc_write_kernel(struct hvc_struct *hp,
}
spin_unlock_irqrestore(&hp->lock, flags);
- return written;
-}
-static int hvc_write(struct tty_struct *tty, const unsigned char *buf, int count)
-{
- struct hvc_struct *hp = tty->driver_data;
- int written;
-
- /* This write was probably executed during a tty close. */
- if (!hp)
- return -EPIPE;
-
- if (hp->count <= 0)
- return -EIO;
-
- written = __hvc_write_kernel(hp, buf, count);
-
/*
* Racy, but harmless, kick thread if there is still pending data.
- * There really is nothing wrong with kicking the thread, even if there
- * is no buffered data.
*/
if (hp->n_outbuf)
hvc_kick();
@@ -614,6 +604,13 @@ static int hvc_poll(struct hvc_struct *hp)
spin_unlock_irqrestore(&hp->lock, flags);
tty_hangup(tty);
spin_lock_irqsave(&hp->lock, flags);
+ } else if ( n == -EAGAIN ) {
+ /*
+ * Some back-ends can only ensure a certain min
+ * num of bytes read, which may be > 'count'.
+ * Let the tty clear the flip buff to make room.
+ */
+ poll_mask |= HVC_POLL_READ;
}
break;
}
@@ -635,16 +632,7 @@ static int hvc_poll(struct hvc_struct *hp)
tty_insert_flip_char(tty, buf[i], 0);
}
- /*
- * Account for the total amount read in one loop, and if above
- * 64 bytes, we do a quick schedule loop to let the tty grok
- * the data and eventually throttle us.
- */
read_total += n;
- if (read_total >= 64) {
- poll_mask |= HVC_POLL_QUICK;
- break;
- }
}
throttled:
/* Wakeup write queue if necessary */
@@ -767,7 +755,8 @@ struct hvc_struct __devinit *hvc_alloc(uint32_t vtermno, int irq,
* see if this vterm id matches one registered for console.
*/
for (i=0; i < MAX_NR_HVC_CONSOLES; i++)
- if (vtermnos[i] == hp->vtermno)
+ if (vtermnos[i] == hp->vtermno &&
+ cons_ops[i] == hp->ops)
break;
/* no matching slot, just use a counter */
@@ -823,34 +812,38 @@ EXPORT_SYMBOL(hvc_remove);
* interfaces start to become available. */
int __init hvc_init(void)
{
+ struct tty_driver *drv;
+
/* We need more than hvc_count adapters due to hotplug additions. */
- hvc_driver = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);
- if (!hvc_driver)
+ drv = alloc_tty_driver(HVC_ALLOC_TTY_ADAPTERS);
+ if (!drv)
return -ENOMEM;
- hvc_driver->owner = THIS_MODULE;
- hvc_driver->devfs_name = "hvc/";
- hvc_driver->driver_name = "hvc";
- hvc_driver->name = "hvc";
- hvc_driver->major = HVC_MAJOR;
- hvc_driver->minor_start = HVC_MINOR;
- hvc_driver->type = TTY_DRIVER_TYPE_SYSTEM;
- hvc_driver->init_termios = tty_std_termios;
- hvc_driver->flags = TTY_DRIVER_REAL_RAW;
- tty_set_operations(hvc_driver, &hvc_ops);
+ drv->owner = THIS_MODULE;
+ drv->devfs_name = "hvc/";
+ drv->driver_name = "hvc";
+ drv->name = "hvc";
+ drv->major = HVC_MAJOR;
+ drv->minor_start = HVC_MINOR;
+ drv->type = TTY_DRIVER_TYPE_SYSTEM;
+ drv->init_termios = tty_std_termios;
+ drv->flags = TTY_DRIVER_REAL_RAW;
+ tty_set_operations(drv, &hvc_ops);
/* Always start the kthread because there can be hotplug vty adapters
* added later. */
hvc_task = kthread_run(khvcd, NULL, "khvcd");
if (IS_ERR(hvc_task)) {
panic("Couldn't create kthread for console.\n");
- put_tty_driver(hvc_driver);
+ put_tty_driver(drv);
return -EIO;
}
- if (tty_register_driver(hvc_driver))
+ if (tty_register_driver(drv))
panic("Couldn't register hvc console driver\n");
+ mb();
+ hvc_driver = drv;
return 0;
}
module_init(hvc_init);
diff --git a/drivers/char/hvc_console.h b/drivers/char/hvc_console.h
new file mode 100644
index 000000000000..96b7401319c1
--- /dev/null
+++ b/drivers/char/hvc_console.h
@@ -0,0 +1,63 @@
+/*
+ * hvc_console.h
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Author(s):
+ * Ryan S. Arnold <rsa@us.ibm.com>
+ *
+ * hvc_console header information:
+ * moved here from include/asm-powerpc/hvconsole.h
+ * and drivers/char/hvc_console.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef HVC_CONSOLE_H
+#define HVC_CONSOLE_H
+
+/*
+ * This is the max number of console adapters that can/will be found as
+ * console devices on first stage console init. Any number beyond this range
+ * can't be used as a console device but is still a valid tty device.
+ */
+#define MAX_NR_HVC_CONSOLES 16
+
+/*
+ * The Linux TTY code does not support dynamic addition of tty derived devices
+ * so we need to know how many tty devices we might need when space is allocated
+ * for the tty device. Since this driver supports hotplug of vty adapters we
+ * need to make sure we have enough allocated.
+ */
+#define HVC_ALLOC_TTY_ADAPTERS 8
+
+
+/* implemented by a low level driver */
+struct hv_ops {
+ int (*get_chars)(uint32_t vtermno, char *buf, int count);
+ int (*put_chars)(uint32_t vtermno, const char *buf, int count);
+};
+
+struct hvc_struct;
+
+/* Register a vterm and a slot index for use as a console (console_init) */
+extern int hvc_instantiate(uint32_t vtermno, int index, struct hv_ops *ops);
+
+/* register a vterm for hvc tty operation (module_init or hotplug add) */
+extern struct hvc_struct * __devinit hvc_alloc(uint32_t vtermno, int irq,
+ struct hv_ops *ops);
+/* remove a vterm from hvc tty operation (modele_exit or hotplug remove) */
+extern int __devexit hvc_remove(struct hvc_struct *hp);
+
+#endif // HVC_CONSOLE_H
diff --git a/drivers/char/hvc_rtas.c b/drivers/char/hvc_rtas.c
new file mode 100644
index 000000000000..83364ea63cba
--- /dev/null
+++ b/drivers/char/hvc_rtas.c
@@ -0,0 +1,138 @@
+/*
+ * IBM RTAS driver interface to hvc_console.c
+ *
+ * (C) Copyright IBM Corporation 2001-2005
+ * (C) Copyright Red Hat, Inc. 2005
+ *
+ * Author(s): Maximino Augilar <IBM STI Design Center>
+ * : Ryan S. Arnold <rsa@us.ibm.com>
+ * : Utz Bacher <utz.bacher@de.ibm.com>
+ * : David Woodhouse <dwmw2@infradead.org>
+ *
+ * inspired by drivers/char/hvc_console.c
+ * written by Anton Blanchard and Paul Mackerras
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/console.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+
+#include <asm/irq.h>
+#include <asm/rtas.h>
+#include "hvc_console.h"
+
+#define hvc_rtas_cookie 0x67781e15
+struct hvc_struct *hvc_rtas_dev;
+
+#define RTASCONS_PUT_ATTEMPTS 16
+
+static int rtascons_put_char_token = RTAS_UNKNOWN_SERVICE;
+static int rtascons_get_char_token = RTAS_UNKNOWN_SERVICE;
+static int rtascons_put_delay = 100;
+module_param_named(put_delay, rtascons_put_delay, int, 0644);
+
+static inline int hvc_rtas_write_console(uint32_t vtermno, const char *buf, int count)
+{
+ int done;
+
+ /* if there is more than one character to be displayed, wait a bit */
+ for (done = 0; done < count; done++) {
+ int result;
+ result = rtas_call(rtascons_put_char_token, 1, 1, NULL, buf[done]);
+ if (result)
+ break;
+ }
+ /* the calling routine expects to receive the number of bytes sent */
+ return done;
+}
+
+static int hvc_rtas_read_console(uint32_t vtermno, char *buf, int count)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ int c, err;
+
+ err = rtas_call(rtascons_get_char_token, 0, 2, &c);
+ if (err)
+ break;
+
+ buf[i] = c;
+ }
+
+ return i;
+}
+
+static struct hv_ops hvc_rtas_get_put_ops = {
+ .get_chars = hvc_rtas_read_console,
+ .put_chars = hvc_rtas_write_console,
+};
+
+static int hvc_rtas_init(void)
+{
+ struct hvc_struct *hp;
+
+ if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE)
+ rtascons_put_char_token = rtas_token("put-term-char");
+ if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE)
+ return -EIO;
+
+ if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE)
+ rtascons_get_char_token = rtas_token("get-term-char");
+ if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE)
+ return -EIO;
+
+ BUG_ON(hvc_rtas_dev);
+
+ /* Allocate an hvc_struct for the console device we instantiated
+ * earlier. Save off hp so that we can return it on exit */
+ hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops);
+ if (IS_ERR(hp))
+ return PTR_ERR(hp);
+ hvc_rtas_dev = hp;
+ return 0;
+}
+module_init(hvc_rtas_init);
+
+/* This will tear down the tty portion of the driver */
+static void __exit hvc_rtas_exit(void)
+{
+ /* Really the fun isn't over until the worker thread breaks down and the
+ * tty cleans up */
+ if (hvc_rtas_dev)
+ hvc_remove(hvc_rtas_dev);
+}
+module_exit(hvc_rtas_exit);
+
+/* This will happen prior to module init. There is no tty at this time? */
+static int hvc_rtas_console_init(void)
+{
+ rtascons_put_char_token = rtas_token("put-term-char");
+ if (rtascons_put_char_token == RTAS_UNKNOWN_SERVICE)
+ return -EIO;
+ rtascons_get_char_token = rtas_token("get-term-char");
+ if (rtascons_get_char_token == RTAS_UNKNOWN_SERVICE)
+ return -EIO;
+
+ hvc_instantiate(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops );
+ add_preferred_console("hvc", 0, NULL);
+ return 0;
+}
+console_initcall(hvc_rtas_console_init);
diff --git a/drivers/char/hvc_vio.c b/drivers/char/hvc_vio.c
index f5212eb2b41d..9add81ceb440 100644
--- a/drivers/char/hvc_vio.c
+++ b/drivers/char/hvc_vio.c
@@ -31,10 +31,13 @@
#include <linux/types.h>
#include <linux/init.h>
+
#include <asm/hvconsole.h>
#include <asm/vio.h>
#include <asm/prom.h>
+#include "hvc_console.h"
+
char hvc_driver_name[] = "hvc_console";
static struct vio_device_id hvc_driver_table[] __devinitdata = {
@@ -48,6 +51,14 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
unsigned long got;
int i;
+ /*
+ * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion
+ * so we play safe and avoid the situation where got > count which could
+ * overload the flip buffer.
+ */
+ if (count < SIZE_VIO_GET_CHARS)
+ return -EAGAIN;
+
got = hvc_get_chars(vtermno, buf, count);
/*
diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c
index f7ac31856572..327b00c3c45e 100644
--- a/drivers/char/hvcs.c
+++ b/drivers/char/hvcs.c
@@ -439,7 +439,6 @@ static int hvcs_io(struct hvcs_struct *hvcsd)
char buf[HVCS_BUFF_LEN] __ALIGNED__;
unsigned long flags;
int got = 0;
- int i;
spin_lock_irqsave(&hvcsd->lock, flags);
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 932feedda262..e1c95374984c 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -42,7 +42,7 @@
#include <linux/slab.h>
#include <linux/devfs_fs_kernel.h>
#include <linux/ipmi.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/compat.h>
@@ -55,7 +55,7 @@ struct ipmi_file_private
struct file *file;
struct fasync_struct *fasync_queue;
wait_queue_head_t wait;
- struct semaphore recv_sem;
+ struct mutex recv_mutex;
int default_retries;
unsigned int default_retry_time_ms;
};
@@ -141,7 +141,7 @@ static int ipmi_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&(priv->recv_msgs));
init_waitqueue_head(&priv->wait);
priv->fasync_queue = NULL;
- sema_init(&(priv->recv_sem), 1);
+ mutex_init(&priv->recv_mutex);
/* Use the low-level defaults. */
priv->default_retries = -1;
@@ -285,15 +285,15 @@ static int ipmi_ioctl(struct inode *inode,
break;
}
- /* We claim a semaphore because we don't want two
+ /* We claim a mutex because we don't want two
users getting something from the queue at a time.
Since we have to release the spinlock before we can
copy the data to the user, it's possible another
user will grab something from the queue, too. Then
the messages might get out of order if something
fails and the message gets put back onto the
- queue. This semaphore prevents that problem. */
- down(&(priv->recv_sem));
+ queue. This mutex prevents that problem. */
+ mutex_lock(&priv->recv_mutex);
/* Grab the message off the list. */
spin_lock_irqsave(&(priv->recv_msg_lock), flags);
@@ -352,7 +352,7 @@ static int ipmi_ioctl(struct inode *inode,
goto recv_putback_on_err;
}
- up(&(priv->recv_sem));
+ mutex_unlock(&priv->recv_mutex);
ipmi_free_recv_msg(msg);
break;
@@ -362,11 +362,11 @@ static int ipmi_ioctl(struct inode *inode,
spin_lock_irqsave(&(priv->recv_msg_lock), flags);
list_add(entry, &(priv->recv_msgs));
spin_unlock_irqrestore(&(priv->recv_msg_lock), flags);
- up(&(priv->recv_sem));
+ mutex_unlock(&priv->recv_mutex);
break;
recv_err:
- up(&(priv->recv_sem));
+ mutex_unlock(&priv->recv_mutex);
break;
}
diff --git a/drivers/char/ipmi/ipmi_kcs_sm.c b/drivers/char/ipmi/ipmi_kcs_sm.c
index da1554194d3d..2062675f9e99 100644
--- a/drivers/char/ipmi/ipmi_kcs_sm.c
+++ b/drivers/char/ipmi/ipmi_kcs_sm.c
@@ -227,7 +227,7 @@ static inline int check_ibf(struct si_sm_data *kcs, unsigned char status,
static inline int check_obf(struct si_sm_data *kcs, unsigned char status,
long time)
{
- if (! GET_STATUS_OBF(status)) {
+ if (!GET_STATUS_OBF(status)) {
kcs->obf_timeout -= time;
if (kcs->obf_timeout < 0) {
start_error_recovery(kcs, "OBF not ready in time");
@@ -407,7 +407,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
}
if (state == KCS_READ_STATE) {
- if (! check_obf(kcs, status, time))
+ if (!check_obf(kcs, status, time))
return SI_SM_CALL_WITH_DELAY;
read_next_byte(kcs);
} else {
@@ -447,7 +447,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
"Not in read state for error2");
break;
}
- if (! check_obf(kcs, status, time))
+ if (!check_obf(kcs, status, time))
return SI_SM_CALL_WITH_DELAY;
clear_obf(kcs, status);
@@ -462,7 +462,7 @@ static enum si_sm_result kcs_event(struct si_sm_data *kcs, long time)
break;
}
- if (! check_obf(kcs, status, time))
+ if (!check_obf(kcs, status, time))
return SI_SM_CALL_WITH_DELAY;
clear_obf(kcs, status);
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 40eb005b9d77..0ded046d5aa8 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -38,6 +38,7 @@
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/ipmi.h>
#include <linux/ipmi_smi.h>
@@ -234,7 +235,7 @@ struct ipmi_smi
/* The list of command receivers that are registered for commands
on this interface. */
- struct semaphore cmd_rcvrs_lock;
+ struct mutex cmd_rcvrs_mutex;
struct list_head cmd_rcvrs;
/* Events that were queues because no one was there to receive
@@ -387,10 +388,10 @@ static void clean_up_interface_data(ipmi_smi_t intf)
/* Wholesale remove all the entries from the list in the
* interface and wait for RCU to know that none are in use. */
- down(&intf->cmd_rcvrs_lock);
+ mutex_lock(&intf->cmd_rcvrs_mutex);
list_add_rcu(&list, &intf->cmd_rcvrs);
list_del_rcu(&intf->cmd_rcvrs);
- up(&intf->cmd_rcvrs_lock);
+ mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
list_for_each_entry_safe(rcvr, rcvr2, &list, link)
@@ -557,7 +558,7 @@ unsigned int ipmi_addr_length(int addr_type)
static void deliver_response(struct ipmi_recv_msg *msg)
{
- if (! msg->user) {
+ if (!msg->user) {
ipmi_smi_t intf = msg->user_msg_data;
unsigned long flags;
@@ -598,11 +599,11 @@ static int intf_next_seq(ipmi_smi_t intf,
(i+1)%IPMI_IPMB_NUM_SEQ != intf->curr_seq;
i = (i+1)%IPMI_IPMB_NUM_SEQ)
{
- if (! intf->seq_table[i].inuse)
+ if (!intf->seq_table[i].inuse)
break;
}
- if (! intf->seq_table[i].inuse) {
+ if (!intf->seq_table[i].inuse) {
intf->seq_table[i].recv_msg = recv_msg;
/* Start with the maximum timeout, when the send response
@@ -763,7 +764,7 @@ int ipmi_create_user(unsigned int if_num,
}
new_user = kmalloc(sizeof(*new_user), GFP_KERNEL);
- if (! new_user)
+ if (!new_user)
return -ENOMEM;
spin_lock_irqsave(&interfaces_lock, flags);
@@ -819,14 +820,13 @@ static void free_user(struct kref *ref)
int ipmi_destroy_user(ipmi_user_t user)
{
- int rv = -ENODEV;
ipmi_smi_t intf = user->intf;
int i;
unsigned long flags;
struct cmd_rcvr *rcvr;
struct cmd_rcvr *rcvrs = NULL;
- user->valid = 1;
+ user->valid = 0;
/* Remove the user from the interface's sequence table. */
spin_lock_irqsave(&intf->seq_lock, flags);
@@ -847,7 +847,7 @@ int ipmi_destroy_user(ipmi_user_t user)
* since other things may be using it till we do
* synchronize_rcu()) then free everything in that list.
*/
- down(&intf->cmd_rcvrs_lock);
+ mutex_lock(&intf->cmd_rcvrs_mutex);
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
if (rcvr->user == user) {
list_del_rcu(&rcvr->link);
@@ -855,7 +855,7 @@ int ipmi_destroy_user(ipmi_user_t user)
rcvrs = rcvr;
}
}
- up(&intf->cmd_rcvrs_lock);
+ mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
while (rcvrs) {
rcvr = rcvrs;
@@ -871,7 +871,7 @@ int ipmi_destroy_user(ipmi_user_t user)
kref_put(&user->refcount, free_user);
- return rv;
+ return 0;
}
void ipmi_get_version(ipmi_user_t user,
@@ -936,7 +936,8 @@ int ipmi_set_gets_events(ipmi_user_t user, int val)
if (val) {
/* Deliver any queued events. */
- list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link) {
+ list_for_each_entry_safe(msg, msg2, &intf->waiting_events,
+ link) {
list_del(&msg->link);
list_add_tail(&msg->link, &msgs);
}
@@ -978,13 +979,13 @@ int ipmi_register_for_cmd(ipmi_user_t user,
rcvr = kmalloc(sizeof(*rcvr), GFP_KERNEL);
- if (! rcvr)
+ if (!rcvr)
return -ENOMEM;
rcvr->cmd = cmd;
rcvr->netfn = netfn;
rcvr->user = user;
- down(&intf->cmd_rcvrs_lock);
+ mutex_lock(&intf->cmd_rcvrs_mutex);
/* Make sure the command/netfn is not already registered. */
entry = find_cmd_rcvr(intf, netfn, cmd);
if (entry) {
@@ -995,7 +996,7 @@ int ipmi_register_for_cmd(ipmi_user_t user,
list_add_rcu(&rcvr->link, &intf->cmd_rcvrs);
out_unlock:
- up(&intf->cmd_rcvrs_lock);
+ mutex_unlock(&intf->cmd_rcvrs_mutex);
if (rv)
kfree(rcvr);
@@ -1009,17 +1010,17 @@ int ipmi_unregister_for_cmd(ipmi_user_t user,
ipmi_smi_t intf = user->intf;
struct cmd_rcvr *rcvr;
- down(&intf->cmd_rcvrs_lock);
+ mutex_lock(&intf->cmd_rcvrs_mutex);
/* Make sure the command/netfn is not already registered. */
rcvr = find_cmd_rcvr(intf, netfn, cmd);
if ((rcvr) && (rcvr->user == user)) {
list_del_rcu(&rcvr->link);
- up(&intf->cmd_rcvrs_lock);
+ mutex_unlock(&intf->cmd_rcvrs_mutex);
synchronize_rcu();
kfree(rcvr);
return 0;
} else {
- up(&intf->cmd_rcvrs_lock);
+ mutex_unlock(&intf->cmd_rcvrs_mutex);
return -ENOENT;
}
}
@@ -1514,7 +1515,7 @@ int ipmi_request_settime(ipmi_user_t user,
unsigned char saddr, lun;
int rv;
- if (! user)
+ if (!user)
return -EINVAL;
rv = check_addr(user->intf, addr, &saddr, &lun);
if (rv)
@@ -1545,7 +1546,7 @@ int ipmi_request_supply_msgs(ipmi_user_t user,
unsigned char saddr, lun;
int rv;
- if (! user)
+ if (!user)
return -EINVAL;
rv = check_addr(user->intf, addr, &saddr, &lun);
if (rv)
@@ -1570,7 +1571,7 @@ static int ipmb_file_read_proc(char *page, char **start, off_t off,
char *out = (char *) page;
ipmi_smi_t intf = data;
int i;
- int rv= 0;
+ int rv = 0;
for (i = 0; i < IPMI_MAX_CHANNELS; i++)
rv += sprintf(out+rv, "%x ", intf->channels[i].address);
@@ -1989,7 +1990,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf)
} else {
bmc->dev = platform_device_alloc("ipmi_bmc",
bmc->id.device_id);
- if (! bmc->dev) {
+ if (!bmc->dev) {
printk(KERN_ERR
"ipmi_msghandler:"
" Unable to allocate platform device\n");
@@ -2305,8 +2306,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
void *send_info,
struct ipmi_device_id *device_id,
struct device *si_dev,
- unsigned char slave_addr,
- ipmi_smi_t *new_intf)
+ unsigned char slave_addr)
{
int i, j;
int rv;
@@ -2366,7 +2366,7 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
spin_lock_init(&intf->events_lock);
INIT_LIST_HEAD(&intf->waiting_events);
intf->waiting_events_count = 0;
- init_MUTEX(&intf->cmd_rcvrs_lock);
+ mutex_init(&intf->cmd_rcvrs_mutex);
INIT_LIST_HEAD(&intf->cmd_rcvrs);
init_waitqueue_head(&intf->waitq);
@@ -2388,9 +2388,9 @@ int ipmi_register_smi(struct ipmi_smi_handlers *handlers,
if (rv)
goto out;
- /* FIXME - this is an ugly kludge, this sets the intf for the
- caller before sending any messages with it. */
- *new_intf = intf;
+ rv = handlers->start_processing(send_info, intf);
+ if (rv)
+ goto out;
get_guid(intf);
@@ -2622,7 +2622,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
spin_unlock_irqrestore(&intf->counter_lock, flags);
recv_msg = ipmi_alloc_recv_msg();
- if (! recv_msg) {
+ if (!recv_msg) {
/* We couldn't allocate memory for the
message, so requeue it for handling
later. */
@@ -2777,7 +2777,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf,
spin_unlock_irqrestore(&intf->counter_lock, flags);
recv_msg = ipmi_alloc_recv_msg();
- if (! recv_msg) {
+ if (!recv_msg) {
/* We couldn't allocate memory for the
message, so requeue it for handling
later. */
@@ -2869,13 +2869,14 @@ static int handle_read_event_rsp(ipmi_smi_t intf,
events. */
rcu_read_lock();
list_for_each_entry_rcu(user, &intf->users, link) {
- if (! user->gets_events)
+ if (!user->gets_events)
continue;
recv_msg = ipmi_alloc_recv_msg();
- if (! recv_msg) {
+ if (!recv_msg) {
rcu_read_unlock();
- list_for_each_entry_safe(recv_msg, recv_msg2, &msgs, link) {
+ list_for_each_entry_safe(recv_msg, recv_msg2, &msgs,
+ link) {
list_del(&recv_msg->link);
ipmi_free_recv_msg(recv_msg);
}
@@ -2905,7 +2906,7 @@ static int handle_read_event_rsp(ipmi_smi_t intf,
/* No one to receive the message, put it in queue if there's
not already too many things in the queue. */
recv_msg = ipmi_alloc_recv_msg();
- if (! recv_msg) {
+ if (!recv_msg) {
/* We couldn't allocate memory for the
message, so requeue it for handling
later. */
@@ -3190,7 +3191,7 @@ void ipmi_smi_watchdog_pretimeout(ipmi_smi_t intf)
rcu_read_lock();
list_for_each_entry_rcu(user, &intf->users, link) {
- if (! user->handler->ipmi_watchdog_pretimeout)
+ if (!user->handler->ipmi_watchdog_pretimeout)
continue;
user->handler->ipmi_watchdog_pretimeout(user->handler_data);
@@ -3278,7 +3279,7 @@ static void check_msg_timeout(ipmi_smi_t intf, struct seq_table *ent,
smi_msg = smi_from_recv_msg(intf, ent->recv_msg, slot,
ent->seqid);
- if (! smi_msg)
+ if (!smi_msg)
return;
spin_unlock_irqrestore(&intf->seq_lock, *flags);
@@ -3314,8 +3315,9 @@ static void ipmi_timeout_handler(long timeout_period)
/* See if any waiting messages need to be processed. */
spin_lock_irqsave(&intf->waiting_msgs_lock, flags);
- list_for_each_entry_safe(smi_msg, smi_msg2, &intf->waiting_msgs, link) {
- if (! handle_new_recv_msg(intf, smi_msg)) {
+ list_for_each_entry_safe(smi_msg, smi_msg2,
+ &intf->waiting_msgs, link) {
+ if (!handle_new_recv_msg(intf, smi_msg)) {
list_del(&smi_msg->link);
ipmi_free_smi_msg(smi_msg);
} else {
diff --git a/drivers/char/ipmi/ipmi_poweroff.c b/drivers/char/ipmi/ipmi_poweroff.c
index 786a2802ca34..d0b5c08e7b4e 100644
--- a/drivers/char/ipmi/ipmi_poweroff.c
+++ b/drivers/char/ipmi/ipmi_poweroff.c
@@ -346,7 +346,7 @@ static int ipmi_dell_chassis_detect (ipmi_user_t user)
{
const char ipmi_version_major = ipmi_version & 0xF;
const char ipmi_version_minor = (ipmi_version >> 4) & 0xF;
- const char mfr[3]=DELL_IANA_MFR_ID;
+ const char mfr[3] = DELL_IANA_MFR_ID;
if (!memcmp(mfr, &mfg_id, sizeof(mfr)) &&
ipmi_version_major <= 1 &&
ipmi_version_minor < 5)
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 35fbd4d8ed4b..a86c0f29953e 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -803,7 +803,7 @@ static int ipmi_thread(void *data)
set_user_nice(current, 19);
while (!kthread_should_stop()) {
spin_lock_irqsave(&(smi_info->si_lock), flags);
- smi_result=smi_event_handler(smi_info, 0);
+ smi_result = smi_event_handler(smi_info, 0);
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
if (smi_result == SI_SM_CALL_WITHOUT_DELAY) {
/* do nothing */
@@ -972,10 +972,37 @@ static irqreturn_t si_bt_irq_handler(int irq, void *data, struct pt_regs *regs)
return si_irq_handler(irq, data, regs);
}
+static int smi_start_processing(void *send_info,
+ ipmi_smi_t intf)
+{
+ struct smi_info *new_smi = send_info;
+
+ new_smi->intf = intf;
+
+ /* Set up the timer that drives the interface. */
+ setup_timer(&new_smi->si_timer, smi_timeout, (long)new_smi);
+ new_smi->last_timeout_jiffies = jiffies;
+ mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
+
+ if (new_smi->si_type != SI_BT) {
+ new_smi->thread = kthread_run(ipmi_thread, new_smi,
+ "kipmi%d", new_smi->intf_num);
+ if (IS_ERR(new_smi->thread)) {
+ printk(KERN_NOTICE "ipmi_si_intf: Could not start"
+ " kernel thread due to error %ld, only using"
+ " timers to drive the interface\n",
+ PTR_ERR(new_smi->thread));
+ new_smi->thread = NULL;
+ }
+ }
+
+ return 0;
+}
static struct ipmi_smi_handlers handlers =
{
.owner = THIS_MODULE,
+ .start_processing = smi_start_processing,
.sender = sender,
.request_events = request_events,
.set_run_to_completion = set_run_to_completion,
@@ -987,7 +1014,7 @@ static struct ipmi_smi_handlers handlers =
#define SI_MAX_PARMS 4
static LIST_HEAD(smi_infos);
-static DECLARE_MUTEX(smi_infos_lock);
+static DEFINE_MUTEX(smi_infos_lock);
static int smi_num; /* Used to sequence the SMIs */
#define DEFAULT_REGSPACING 1
@@ -2162,9 +2189,13 @@ static void setup_xaction_handlers(struct smi_info *smi_info)
static inline void wait_for_timer_and_thread(struct smi_info *smi_info)
{
- if (smi_info->thread != NULL && smi_info->thread != ERR_PTR(-ENOMEM))
- kthread_stop(smi_info->thread);
- del_timer_sync(&smi_info->si_timer);
+ if (smi_info->intf) {
+ /* The timer and thread are only running if the
+ interface has been started up and registered. */
+ if (smi_info->thread != NULL)
+ kthread_stop(smi_info->thread);
+ del_timer_sync(&smi_info->si_timer);
+ }
}
static struct ipmi_default_vals
@@ -2245,7 +2276,7 @@ static int try_smi_init(struct smi_info *new_smi)
new_smi->slave_addr, new_smi->irq);
}
- down(&smi_infos_lock);
+ mutex_lock(&smi_infos_lock);
if (!is_new_interface(new_smi)) {
printk(KERN_WARNING "ipmi_si: duplicate interface\n");
rv = -EBUSY;
@@ -2341,21 +2372,6 @@ static int try_smi_init(struct smi_info *new_smi)
if (new_smi->irq)
new_smi->si_state = SI_CLEARING_FLAGS_THEN_SET_IRQ;
- /* The ipmi_register_smi() code does some operations to
- determine the channel information, so we must be ready to
- handle operations before it is called. This means we have
- to stop the timer if we get an error after this point. */
- init_timer(&(new_smi->si_timer));
- new_smi->si_timer.data = (long) new_smi;
- new_smi->si_timer.function = smi_timeout;
- new_smi->last_timeout_jiffies = jiffies;
- new_smi->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES;
-
- add_timer(&(new_smi->si_timer));
- if (new_smi->si_type != SI_BT)
- new_smi->thread = kthread_run(ipmi_thread, new_smi,
- "kipmi%d", new_smi->intf_num);
-
if (!new_smi->dev) {
/* If we don't already have a device from something
* else (like PCI), then register a new one. */
@@ -2365,7 +2381,7 @@ static int try_smi_init(struct smi_info *new_smi)
printk(KERN_ERR
"ipmi_si_intf:"
" Unable to allocate platform device\n");
- goto out_err_stop_timer;
+ goto out_err;
}
new_smi->dev = &new_smi->pdev->dev;
new_smi->dev->driver = &ipmi_driver;
@@ -2377,7 +2393,7 @@ static int try_smi_init(struct smi_info *new_smi)
" Unable to register system interface device:"
" %d\n",
rv);
- goto out_err_stop_timer;
+ goto out_err;
}
new_smi->dev_registered = 1;
}
@@ -2386,8 +2402,7 @@ static int try_smi_init(struct smi_info *new_smi)
new_smi,
&new_smi->device_id,
new_smi->dev,
- new_smi->slave_addr,
- &(new_smi->intf));
+ new_smi->slave_addr);
if (rv) {
printk(KERN_ERR
"ipmi_si: Unable to register device: error %d\n",
@@ -2417,7 +2432,7 @@ static int try_smi_init(struct smi_info *new_smi)
list_add_tail(&new_smi->link, &smi_infos);
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
printk(" IPMI %s interface initialized\n",si_to_str[new_smi->si_type]);
@@ -2454,7 +2469,7 @@ static int try_smi_init(struct smi_info *new_smi)
kfree(new_smi);
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
return rv;
}
@@ -2512,26 +2527,26 @@ static __devinit int init_ipmi_si(void)
#endif
if (si_trydefaults) {
- down(&smi_infos_lock);
+ mutex_lock(&smi_infos_lock);
if (list_empty(&smi_infos)) {
/* No BMC was found, try defaults. */
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
default_find_bmc();
} else {
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
}
}
- down(&smi_infos_lock);
+ mutex_lock(&smi_infos_lock);
if (list_empty(&smi_infos)) {
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
#ifdef CONFIG_PCI
pci_unregister_driver(&ipmi_pci_driver);
#endif
printk("ipmi_si: Unable to find any System Interface(s)\n");
return -ENODEV;
} else {
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
return 0;
}
}
@@ -2607,10 +2622,10 @@ static __exit void cleanup_ipmi_si(void)
pci_unregister_driver(&ipmi_pci_driver);
#endif
- down(&smi_infos_lock);
+ mutex_lock(&smi_infos_lock);
list_for_each_entry_safe(e, tmp_e, &smi_infos, link)
cleanup_one_si(e);
- up(&smi_infos_lock);
+ mutex_unlock(&smi_infos_lock);
driver_unregister(&ipmi_driver);
}
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 7ece9f3c8f70..2d11ddd99e55 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -39,6 +39,7 @@
#include <linux/watchdog.h>
#include <linux/miscdevice.h>
#include <linux/init.h>
+#include <linux/completion.h>
#include <linux/rwsem.h>
#include <linux/errno.h>
#include <asm/uaccess.h>
@@ -303,21 +304,22 @@ static int ipmi_heartbeat(void);
static void panic_halt_ipmi_heartbeat(void);
-/* We use a semaphore to make sure that only one thing can send a set
+/* We use a mutex to make sure that only one thing can send a set
timeout at one time, because we only have one copy of the data.
- The semaphore is claimed when the set_timeout is sent and freed
+ The mutex is claimed when the set_timeout is sent and freed
when both messages are free. */
static atomic_t set_timeout_tofree = ATOMIC_INIT(0);
-static DECLARE_MUTEX(set_timeout_lock);
+static DEFINE_MUTEX(set_timeout_lock);
+static DECLARE_COMPLETION(set_timeout_wait);
static void set_timeout_free_smi(struct ipmi_smi_msg *msg)
{
if (atomic_dec_and_test(&set_timeout_tofree))
- up(&set_timeout_lock);
+ complete(&set_timeout_wait);
}
static void set_timeout_free_recv(struct ipmi_recv_msg *msg)
{
if (atomic_dec_and_test(&set_timeout_tofree))
- up(&set_timeout_lock);
+ complete(&set_timeout_wait);
}
static struct ipmi_smi_msg set_timeout_smi_msg =
{
@@ -399,7 +401,7 @@ static int ipmi_set_timeout(int do_heartbeat)
/* We can only send one of these at a time. */
- down(&set_timeout_lock);
+ mutex_lock(&set_timeout_lock);
atomic_set(&set_timeout_tofree, 2);
@@ -407,16 +409,21 @@ static int ipmi_set_timeout(int do_heartbeat)
&set_timeout_recv_msg,
&send_heartbeat_now);
if (rv) {
- up(&set_timeout_lock);
- } else {
- if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB)
- || ((send_heartbeat_now)
- && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY)))
- {
- rv = ipmi_heartbeat();
- }
+ mutex_unlock(&set_timeout_lock);
+ goto out;
}
+ wait_for_completion(&set_timeout_wait);
+
+ if ((do_heartbeat == IPMI_SET_TIMEOUT_FORCE_HB)
+ || ((send_heartbeat_now)
+ && (do_heartbeat == IPMI_SET_TIMEOUT_HB_IF_NECESSARY)))
+ {
+ rv = ipmi_heartbeat();
+ }
+ mutex_unlock(&set_timeout_lock);
+
+out:
return rv;
}
@@ -458,17 +465,17 @@ static void panic_halt_ipmi_set_timeout(void)
The semaphore is claimed when the set_timeout is sent and freed
when both messages are free. */
static atomic_t heartbeat_tofree = ATOMIC_INIT(0);
-static DECLARE_MUTEX(heartbeat_lock);
-static DECLARE_MUTEX_LOCKED(heartbeat_wait_lock);
+static DEFINE_MUTEX(heartbeat_lock);
+static DECLARE_COMPLETION(heartbeat_wait);
static void heartbeat_free_smi(struct ipmi_smi_msg *msg)
{
if (atomic_dec_and_test(&heartbeat_tofree))
- up(&heartbeat_wait_lock);
+ complete(&heartbeat_wait);
}
static void heartbeat_free_recv(struct ipmi_recv_msg *msg)
{
if (atomic_dec_and_test(&heartbeat_tofree))
- up(&heartbeat_wait_lock);
+ complete(&heartbeat_wait);
}
static struct ipmi_smi_msg heartbeat_smi_msg =
{
@@ -511,14 +518,14 @@ static int ipmi_heartbeat(void)
return ipmi_set_timeout(IPMI_SET_TIMEOUT_HB_IF_NECESSARY);
}
- down(&heartbeat_lock);
+ mutex_lock(&heartbeat_lock);
atomic_set(&heartbeat_tofree, 2);
/* Don't reset the timer if we have the timer turned off, that
re-enables the watchdog. */
if (ipmi_watchdog_state == WDOG_TIMEOUT_NONE) {
- up(&heartbeat_lock);
+ mutex_unlock(&heartbeat_lock);
return 0;
}
@@ -539,14 +546,14 @@ static int ipmi_heartbeat(void)
&heartbeat_recv_msg,
1);
if (rv) {
- up(&heartbeat_lock);
+ mutex_unlock(&heartbeat_lock);
printk(KERN_WARNING PFX "heartbeat failure: %d\n",
rv);
return rv;
}
/* Wait for the heartbeat to be sent. */
- down(&heartbeat_wait_lock);
+ wait_for_completion(&heartbeat_wait);
if (heartbeat_recv_msg.msg.data[0] != 0) {
/* Got an error in the heartbeat response. It was already
@@ -555,7 +562,7 @@ static int ipmi_heartbeat(void)
rv = -EINVAL;
}
- up(&heartbeat_lock);
+ mutex_unlock(&heartbeat_lock);
return rv;
}
@@ -589,7 +596,7 @@ static void panic_halt_ipmi_heartbeat(void)
1);
}
-static struct watchdog_info ident=
+static struct watchdog_info ident =
{
.options = 0, /* WDIOF_SETTIMEOUT, */
.firmware_version = 1,
@@ -790,13 +797,13 @@ static int ipmi_fasync(int fd, struct file *file, int on)
static int ipmi_close(struct inode *ino, struct file *filep)
{
- if (iminor(ino)==WATCHDOG_MINOR)
- {
+ if (iminor(ino) == WATCHDOG_MINOR) {
if (expect_close == 42) {
ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
ipmi_set_timeout(IPMI_SET_TIMEOUT_NO_HB);
} else {
- printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
+ printk(KERN_CRIT PFX
+ "Unexpected close, not stopping watchdog!\n");
ipmi_heartbeat();
}
clear_bit(0, &ipmi_wdog_open);
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index e5247f85a446..ef20c1fc9c4c 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -706,7 +706,6 @@ static int stli_portcmdstats(stliport_t *portp);
static int stli_clrportstats(stliport_t *portp, comstats_t __user *cp);
static int stli_getportstruct(stliport_t __user *arg);
static int stli_getbrdstruct(stlibrd_t __user *arg);
-static void *stli_memalloc(int len);
static stlibrd_t *stli_allocbrd(void);
static void stli_ecpinit(stlibrd_t *brdp);
@@ -997,17 +996,6 @@ static int stli_parsebrd(stlconf_t *confp, char **argp)
/*****************************************************************************/
-/*
- * Local driver kernel malloc routine.
- */
-
-static void *stli_memalloc(int len)
-{
- return((void *) kmalloc(len, GFP_KERNEL));
-}
-
-/*****************************************************************************/
-
static int stli_open(struct tty_struct *tty, struct file *filp)
{
stlibrd_t *brdp;
@@ -3227,13 +3215,12 @@ static int stli_initports(stlibrd_t *brdp)
#endif
for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
- portp = (stliport_t *) stli_memalloc(sizeof(stliport_t));
- if (portp == (stliport_t *) NULL) {
+ portp = kzalloc(sizeof(stliport_t), GFP_KERNEL);
+ if (!portp) {
printk("STALLION: failed to allocate port structure\n");
continue;
}
- memset(portp, 0, sizeof(stliport_t));
portp->magic = STLI_PORTMAGIC;
portp->portnr = i;
portp->brdnr = brdp->brdnr;
@@ -4610,14 +4597,13 @@ static stlibrd_t *stli_allocbrd(void)
{
stlibrd_t *brdp;
- brdp = (stlibrd_t *) stli_memalloc(sizeof(stlibrd_t));
- if (brdp == (stlibrd_t *) NULL) {
+ brdp = kzalloc(sizeof(stlibrd_t), GFP_KERNEL);
+ if (!brdp) {
printk(KERN_ERR "STALLION: failed to allocate memory "
"(size=%d)\n", sizeof(stlibrd_t));
- return((stlibrd_t *) NULL);
+ return NULL;
}
- memset(brdp, 0, sizeof(stlibrd_t));
brdp->magic = STLI_BOARDMAGIC;
return(brdp);
}
@@ -5210,12 +5196,12 @@ int __init stli_init(void)
/*
* Allocate a temporary write buffer.
*/
- stli_tmpwritebuf = (char *) stli_memalloc(STLI_TXBUFSIZE);
- if (stli_tmpwritebuf == (char *) NULL)
+ stli_tmpwritebuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL);
+ if (!stli_tmpwritebuf)
printk(KERN_ERR "STALLION: failed to allocate memory "
"(size=%d)\n", STLI_TXBUFSIZE);
- stli_txcookbuf = stli_memalloc(STLI_TXBUFSIZE);
- if (stli_txcookbuf == (char *) NULL)
+ stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL);
+ if (!stli_txcookbuf)
printk(KERN_ERR "STALLION: failed to allocate memory "
"(size=%d)\n", STLI_TXBUFSIZE);
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 3f5d6077f39c..a9c5a7230f89 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -504,7 +504,6 @@ static int stl_echmcaintr(stlbrd_t *brdp);
static int stl_echpciintr(stlbrd_t *brdp);
static int stl_echpci64intr(stlbrd_t *brdp);
static void stl_offintr(void *private);
-static void *stl_memalloc(int len);
static stlbrd_t *stl_allocbrd(void);
static stlport_t *stl_getport(int brdnr, int panelnr, int portnr);
@@ -940,17 +939,6 @@ static int stl_parsebrd(stlconf_t *confp, char **argp)
/*****************************************************************************/
/*
- * Local driver kernel memory allocation routine.
- */
-
-static void *stl_memalloc(int len)
-{
- return (void *) kmalloc(len, GFP_KERNEL);
-}
-
-/*****************************************************************************/
-
-/*
* Allocate a new board structure. Fill out the basic info in it.
*/
@@ -958,14 +946,13 @@ static stlbrd_t *stl_allocbrd(void)
{
stlbrd_t *brdp;
- brdp = (stlbrd_t *) stl_memalloc(sizeof(stlbrd_t));
- if (brdp == (stlbrd_t *) NULL) {
+ brdp = kzalloc(sizeof(stlbrd_t), GFP_KERNEL);
+ if (!brdp) {
printk("STALLION: failed to allocate memory (size=%d)\n",
sizeof(stlbrd_t));
- return (stlbrd_t *) NULL;
+ return NULL;
}
- memset(brdp, 0, sizeof(stlbrd_t));
brdp->magic = STL_BOARDMAGIC;
return brdp;
}
@@ -1017,9 +1004,9 @@ static int stl_open(struct tty_struct *tty, struct file *filp)
portp->refcount++;
if ((portp->flags & ASYNC_INITIALIZED) == 0) {
- if (portp->tx.buf == (char *) NULL) {
- portp->tx.buf = (char *) stl_memalloc(STL_TXBUFSIZE);
- if (portp->tx.buf == (char *) NULL)
+ if (!portp->tx.buf) {
+ portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
+ if (!portp->tx.buf)
return -ENOMEM;
portp->tx.head = portp->tx.buf;
portp->tx.tail = portp->tx.buf;
@@ -2178,13 +2165,12 @@ static int __init stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
* each ports data structures.
*/
for (i = 0; (i < panelp->nrports); i++) {
- portp = (stlport_t *) stl_memalloc(sizeof(stlport_t));
- if (portp == (stlport_t *) NULL) {
+ portp = kzalloc(sizeof(stlport_t), GFP_KERNEL);
+ if (!portp) {
printk("STALLION: failed to allocate memory "
"(size=%d)\n", sizeof(stlport_t));
break;
}
- memset(portp, 0, sizeof(stlport_t));
portp->magic = STL_PORTMAGIC;
portp->portnr = i;
@@ -2315,13 +2301,12 @@ static inline int stl_initeio(stlbrd_t *brdp)
* can complete the setup.
*/
- panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
- if (panelp == (stlpanel_t *) NULL) {
+ panelp = kzalloc(sizeof(stlpanel_t), GFP_KERNEL);
+ if (!panelp) {
printk(KERN_WARNING "STALLION: failed to allocate memory "
"(size=%d)\n", sizeof(stlpanel_t));
- return(-ENOMEM);
+ return -ENOMEM;
}
- memset(panelp, 0, sizeof(stlpanel_t));
panelp->magic = STL_PANELMAGIC;
panelp->brdnr = brdp->brdnr;
@@ -2490,13 +2475,12 @@ static inline int stl_initech(stlbrd_t *brdp)
status = inb(ioaddr + ECH_PNLSTATUS);
if ((status & ECH_PNLIDMASK) != nxtid)
break;
- panelp = (stlpanel_t *) stl_memalloc(sizeof(stlpanel_t));
- if (panelp == (stlpanel_t *) NULL) {
+ panelp = kzalloc(sizeof(stlpanel_t), GFP_KERNEL);
+ if (!panelp) {
printk("STALLION: failed to allocate memory "
"(size=%d)\n", sizeof(stlpanel_t));
break;
}
- memset(panelp, 0, sizeof(stlpanel_t));
panelp->magic = STL_PANELMAGIC;
panelp->brdnr = brdp->brdnr;
panelp->panelnr = panelnr;
@@ -3074,8 +3058,8 @@ static int __init stl_init(void)
/*
* Allocate a temporary write buffer.
*/
- stl_tmpwritebuf = (char *) stl_memalloc(STL_TXBUFSIZE);
- if (stl_tmpwritebuf == (char *) NULL)
+ stl_tmpwritebuf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
+ if (!stl_tmpwritebuf)
printk("STALLION: failed to allocate memory (size=%d)\n",
STL_TXBUFSIZE);
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 0bfd1b63662e..98b126c2ded8 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -376,7 +376,7 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars, s
return copied;
}
-EXPORT_SYMBOL_GPL(tty_insert_flip_string);
+EXPORT_SYMBOL(tty_insert_flip_string);
int tty_insert_flip_string_flags(struct tty_struct *tty, const unsigned char *chars, const char *flags, size_t size)
{
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index ca4844c527da..acc5d47844eb 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -2328,6 +2328,10 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
case TIOCL_SETVESABLANK:
set_vesa_blanking(p);
break;
+ case TIOCL_GETKMSGREDIRECT:
+ data = kmsg_redirect;
+ ret = __put_user(data, p);
+ break;
case TIOCL_SETKMSGREDIRECT:
if (!capable(CAP_SYS_ADMIN)) {
ret = -EPERM;
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index b582d0cdc24f..4f0898400c6d 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -71,7 +71,7 @@ config EDAC_E7XXX
config EDAC_E752X
tristate "Intel e752x (e7520, e7525, e7320)"
- depends on EDAC_MM_EDAC && PCI && X86
+ depends on EDAC_MM_EDAC && PCI && X86 && HOTPLUG
help
Support for error detection and correction on the Intel
E7520, E7525, E7320 server chipsets.
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index ccf528d733bf..a5017de72da5 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -61,6 +61,7 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/mutex.h>
+#include <linux/leds.h>
#define _IDE_DISK
@@ -317,6 +318,8 @@ static ide_startstop_t ide_do_rw_disk (ide_drive_t *drive, struct request *rq, s
return ide_stopped;
}
+ ledtrig_ide_activity();
+
pr_debug("%s: %sing: block=%llu, sectors=%lu, buffer=0x%08lx\n",
drive->name, rq_data_dir(rq) == READ ? "read" : "writ",
(unsigned long long)block, rq->nr_sectors,
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 0606bd2f6020..9233b8109a0f 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -375,7 +375,13 @@ static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
}
}
- ide_end_request(drive, 1, rq->hard_nr_sectors);
+ if (rq->rq_disk) {
+ ide_driver_t *drv;
+
+ drv = *(ide_driver_t **)rq->rq_disk->private_data;;
+ drv->end_request(drive, 1, rq->hard_nr_sectors);
+ } else
+ ide_end_request(drive, 1, rq->hard_nr_sectors);
}
/*
diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c
index c85b87cb59d1..3e677c4f8c28 100644
--- a/drivers/ide/pci/via82cxxx.c
+++ b/drivers/ide/pci/via82cxxx.c
@@ -440,7 +440,7 @@ static void __devinit init_hwif_via82cxxx(ide_hwif_t *hwif)
#if defined(CONFIG_PPC_CHRP) && defined(CONFIG_PPC32)
- if(_machine == _MACH_chrp && _chrp_type == _CHRP_Pegasos) {
+ if(machine_is(chrp) && _chrp_type == _CHRP_Pegasos) {
hwif->irq = hwif->channel ? 15 : 14;
}
#endif
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 5013b1285e22..78e30f803671 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -1677,7 +1677,7 @@ MODULE_DEVICE_TABLE(pci, pmac_ide_pci_match);
void __init
pmac_ide_probe(void)
{
- if (_machine != _MACH_Pmac)
+ if (!machine_is(powermac))
return;
#ifdef CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
index a86beeb6af5d..19222878aae9 100644
--- a/drivers/ieee1394/ohci1394.c
+++ b/drivers/ieee1394/ohci1394.c
@@ -3529,7 +3529,7 @@ static void ohci1394_pci_remove(struct pci_dev *pdev)
static int ohci1394_pci_resume (struct pci_dev *pdev)
{
#ifdef CONFIG_PPC_PMAC
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
struct device_node *of_node;
/* Re-enable 1394 */
@@ -3548,7 +3548,7 @@ static int ohci1394_pci_resume (struct pci_dev *pdev)
static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state)
{
#ifdef CONFIG_PPC_PMAC
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
struct device_node *of_node;
/* Disable 1394 */
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index f7854b65fd55..ba54c856b0e5 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -227,6 +227,14 @@ struct ib_mad_agent *ib_register_mad_agent(struct ib_device *device,
if (!is_vendor_oui(mad_reg_req->oui))
goto error1;
}
+ /* Make sure class supplied is consistent with RMPP */
+ if (ib_is_mad_class_rmpp(mad_reg_req->mgmt_class)) {
+ if (!rmpp_version)
+ goto error1;
+ } else {
+ if (rmpp_version)
+ goto error1;
+ }
/* Make sure class supplied is consistent with QP type */
if (qp_type == IB_QPT_SMI) {
if ((mad_reg_req->mgmt_class !=
@@ -890,6 +898,35 @@ struct ib_mad_send_buf * ib_create_send_mad(struct ib_mad_agent *mad_agent,
}
EXPORT_SYMBOL(ib_create_send_mad);
+int ib_get_mad_data_offset(u8 mgmt_class)
+{
+ if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM)
+ return IB_MGMT_SA_HDR;
+ else if ((mgmt_class == IB_MGMT_CLASS_DEVICE_MGMT) ||
+ (mgmt_class == IB_MGMT_CLASS_DEVICE_ADM) ||
+ (mgmt_class == IB_MGMT_CLASS_BIS))
+ return IB_MGMT_DEVICE_HDR;
+ else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
+ (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))
+ return IB_MGMT_VENDOR_HDR;
+ else
+ return IB_MGMT_MAD_HDR;
+}
+EXPORT_SYMBOL(ib_get_mad_data_offset);
+
+int ib_is_mad_class_rmpp(u8 mgmt_class)
+{
+ if ((mgmt_class == IB_MGMT_CLASS_SUBN_ADM) ||
+ (mgmt_class == IB_MGMT_CLASS_DEVICE_MGMT) ||
+ (mgmt_class == IB_MGMT_CLASS_DEVICE_ADM) ||
+ (mgmt_class == IB_MGMT_CLASS_BIS) ||
+ ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
+ (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END)))
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(ib_is_mad_class_rmpp);
+
void *ib_get_rmpp_segment(struct ib_mad_send_buf *send_buf, int seg_num)
{
struct ib_mad_send_wr_private *mad_send_wr;
@@ -1022,6 +1059,13 @@ int ib_post_send_mad(struct ib_mad_send_buf *send_buf,
goto error;
}
+ if (!ib_is_mad_class_rmpp(((struct ib_mad_hdr *) send_buf->mad)->mgmt_class)) {
+ if (mad_agent_priv->agent.rmpp_version) {
+ ret = -EINVAL;
+ goto error;
+ }
+ }
+
/*
* Save pointer to next work request to post in case the
* current one completes, and the user modifies the work
@@ -1618,14 +1662,59 @@ static int is_data_mad(struct ib_mad_agent_private *mad_agent_priv,
(rmpp_mad->rmpp_hdr.rmpp_type == IB_MGMT_RMPP_TYPE_DATA);
}
+static inline int rcv_has_same_class(struct ib_mad_send_wr_private *wr,
+ struct ib_mad_recv_wc *rwc)
+{
+ return ((struct ib_mad *)(wr->send_buf.mad))->mad_hdr.mgmt_class ==
+ rwc->recv_buf.mad->mad_hdr.mgmt_class;
+}
+
+static inline int rcv_has_same_gid(struct ib_mad_send_wr_private *wr,
+ struct ib_mad_recv_wc *rwc )
+{
+ struct ib_ah_attr attr;
+ u8 send_resp, rcv_resp;
+
+ send_resp = ((struct ib_mad *)(wr->send_buf.mad))->
+ mad_hdr.method & IB_MGMT_METHOD_RESP;
+ rcv_resp = rwc->recv_buf.mad->mad_hdr.method & IB_MGMT_METHOD_RESP;
+
+ if (!send_resp && rcv_resp)
+ /* is request/response. GID/LIDs are both local (same). */
+ return 1;
+
+ if (send_resp == rcv_resp)
+ /* both requests, or both responses. GIDs different */
+ return 0;
+
+ if (ib_query_ah(wr->send_buf.ah, &attr))
+ /* Assume not equal, to avoid false positives. */
+ return 0;
+
+ if (!(attr.ah_flags & IB_AH_GRH) && !(rwc->wc->wc_flags & IB_WC_GRH))
+ return attr.dlid == rwc->wc->slid;
+ else if ((attr.ah_flags & IB_AH_GRH) &&
+ (rwc->wc->wc_flags & IB_WC_GRH))
+ return memcmp(attr.grh.dgid.raw,
+ rwc->recv_buf.grh->sgid.raw, 16) == 0;
+ else
+ /* one has GID, other does not. Assume different */
+ return 0;
+}
struct ib_mad_send_wr_private*
-ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, __be64 tid)
+ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
+ struct ib_mad_recv_wc *mad_recv_wc)
{
struct ib_mad_send_wr_private *mad_send_wr;
+ struct ib_mad *mad;
+
+ mad = (struct ib_mad *)mad_recv_wc->recv_buf.mad;
list_for_each_entry(mad_send_wr, &mad_agent_priv->wait_list,
agent_list) {
- if (mad_send_wr->tid == tid)
+ if ((mad_send_wr->tid == mad->mad_hdr.tid) &&
+ rcv_has_same_class(mad_send_wr, mad_recv_wc) &&
+ rcv_has_same_gid(mad_send_wr, mad_recv_wc))
return mad_send_wr;
}
@@ -1636,7 +1725,10 @@ ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, __be64 tid)
list_for_each_entry(mad_send_wr, &mad_agent_priv->send_list,
agent_list) {
if (is_data_mad(mad_agent_priv, mad_send_wr->send_buf.mad) &&
- mad_send_wr->tid == tid && mad_send_wr->timeout) {
+ mad_send_wr->tid == mad->mad_hdr.tid &&
+ mad_send_wr->timeout &&
+ rcv_has_same_class(mad_send_wr, mad_recv_wc) &&
+ rcv_has_same_gid(mad_send_wr, mad_recv_wc)) {
/* Verify request has not been canceled */
return (mad_send_wr->status == IB_WC_SUCCESS) ?
mad_send_wr : NULL;
@@ -1661,7 +1753,6 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
struct ib_mad_send_wr_private *mad_send_wr;
struct ib_mad_send_wc mad_send_wc;
unsigned long flags;
- __be64 tid;
INIT_LIST_HEAD(&mad_recv_wc->rmpp_list);
list_add(&mad_recv_wc->recv_buf.list, &mad_recv_wc->rmpp_list);
@@ -1677,9 +1768,8 @@ static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
/* Complete corresponding request */
if (response_mad(mad_recv_wc->recv_buf.mad)) {
- tid = mad_recv_wc->recv_buf.mad->mad_hdr.tid;
spin_lock_irqsave(&mad_agent_priv->lock, flags);
- mad_send_wr = ib_find_send_mad(mad_agent_priv, tid);
+ mad_send_wr = ib_find_send_mad(mad_agent_priv, mad_recv_wc);
if (!mad_send_wr) {
spin_unlock_irqrestore(&mad_agent_priv->lock, flags);
ib_free_recv_mad(mad_recv_wc);
@@ -2408,11 +2498,11 @@ static int ib_mad_post_receive_mads(struct ib_mad_qp_info *qp_info,
}
}
sg_list.addr = dma_map_single(qp_info->port_priv->
- device->dma_device,
- &mad_priv->grh,
- sizeof *mad_priv -
- sizeof mad_priv->header,
- DMA_FROM_DEVICE);
+ device->dma_device,
+ &mad_priv->grh,
+ sizeof *mad_priv -
+ sizeof mad_priv->header,
+ DMA_FROM_DEVICE);
pci_unmap_addr_set(&mad_priv->header, mapping, sg_list.addr);
recv_wr.wr_id = (unsigned long)&mad_priv->header.mad_list;
mad_priv->header.mad_list.mad_queue = recv_queue;
diff --git a/drivers/infiniband/core/mad_priv.h b/drivers/infiniband/core/mad_priv.h
index a7125d4b5ccf..6c9c133d71ef 100644
--- a/drivers/infiniband/core/mad_priv.h
+++ b/drivers/infiniband/core/mad_priv.h
@@ -216,7 +216,8 @@ extern kmem_cache_t *ib_mad_cache;
int ib_send_mad(struct ib_mad_send_wr_private *mad_send_wr);
struct ib_mad_send_wr_private *
-ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv, __be64 tid);
+ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
+ struct ib_mad_recv_wc *mad_recv_wc);
void ib_mad_complete_send_wr(struct ib_mad_send_wr_private *mad_send_wr,
struct ib_mad_send_wc *mad_send_wc);
diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c
index bacfdd5bddad..dfd4e588ce03 100644
--- a/drivers/infiniband/core/mad_rmpp.c
+++ b/drivers/infiniband/core/mad_rmpp.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2005 Intel Inc. All rights reserved.
- * Copyright (c) 2005 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2005-2006 Voltaire, 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
@@ -100,17 +100,6 @@ void ib_cancel_rmpp_recvs(struct ib_mad_agent_private *agent)
}
}
-static int data_offset(u8 mgmt_class)
-{
- if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM)
- return IB_MGMT_SA_HDR;
- else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
- (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))
- return IB_MGMT_VENDOR_HDR;
- else
- return IB_MGMT_RMPP_HDR;
-}
-
static void format_ack(struct ib_mad_send_buf *msg,
struct ib_rmpp_mad *data,
struct mad_rmpp_recv *rmpp_recv)
@@ -137,7 +126,7 @@ static void ack_recv(struct mad_rmpp_recv *rmpp_recv,
struct ib_mad_send_buf *msg;
int ret, hdr_len;
- hdr_len = data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
+ hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
msg = ib_create_send_mad(&rmpp_recv->agent->agent, recv_wc->wc->src_qp,
recv_wc->wc->pkey_index, 1, hdr_len,
0, GFP_KERNEL);
@@ -163,7 +152,7 @@ static struct ib_mad_send_buf *alloc_response_msg(struct ib_mad_agent *agent,
if (IS_ERR(ah))
return (void *) ah;
- hdr_len = data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
+ hdr_len = ib_get_mad_data_offset(recv_wc->recv_buf.mad->mad_hdr.mgmt_class);
msg = ib_create_send_mad(agent, recv_wc->wc->src_qp,
recv_wc->wc->pkey_index, 1,
hdr_len, 0, GFP_KERNEL);
@@ -408,7 +397,7 @@ static inline int get_mad_len(struct mad_rmpp_recv *rmpp_recv)
rmpp_mad = (struct ib_rmpp_mad *)rmpp_recv->cur_seg_buf->mad;
- hdr_size = data_offset(rmpp_mad->mad_hdr.mgmt_class);
+ hdr_size = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class);
data_size = sizeof(struct ib_rmpp_mad) - hdr_size;
pad = IB_MGMT_RMPP_DATA - be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
if (pad > IB_MGMT_RMPP_DATA || pad < 0)
@@ -562,15 +551,15 @@ static int send_next_seg(struct ib_mad_send_wr_private *mad_send_wr)
return ib_send_mad(mad_send_wr);
}
-static void abort_send(struct ib_mad_agent_private *agent, __be64 tid,
- u8 rmpp_status)
+static void abort_send(struct ib_mad_agent_private *agent,
+ struct ib_mad_recv_wc *mad_recv_wc, u8 rmpp_status)
{
struct ib_mad_send_wr_private *mad_send_wr;
struct ib_mad_send_wc wc;
unsigned long flags;
spin_lock_irqsave(&agent->lock, flags);
- mad_send_wr = ib_find_send_mad(agent, tid);
+ mad_send_wr = ib_find_send_mad(agent, mad_recv_wc);
if (!mad_send_wr)
goto out; /* Unmatched send */
@@ -612,8 +601,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad;
if (rmpp_mad->rmpp_hdr.rmpp_status) {
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_BAD_STATUS);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
return;
}
@@ -621,14 +609,13 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
seg_num = be32_to_cpu(rmpp_mad->rmpp_hdr.seg_num);
newwin = be32_to_cpu(rmpp_mad->rmpp_hdr.paylen_newwin);
if (newwin < seg_num) {
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_W2S);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_W2S);
return;
}
spin_lock_irqsave(&agent->lock, flags);
- mad_send_wr = ib_find_send_mad(agent, rmpp_mad->mad_hdr.tid);
+ mad_send_wr = ib_find_send_mad(agent, mad_recv_wc);
if (!mad_send_wr)
goto out; /* Unmatched ACK */
@@ -639,8 +626,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
if (seg_num > mad_send_wr->send_buf.seg_count ||
seg_num > mad_send_wr->newwin) {
spin_unlock_irqrestore(&agent->lock, flags);
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_S2B);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_S2B);
return;
}
@@ -728,12 +714,10 @@ static void process_rmpp_stop(struct ib_mad_agent_private *agent,
rmpp_mad = (struct ib_rmpp_mad *)mad_recv_wc->recv_buf.mad;
if (rmpp_mad->rmpp_hdr.rmpp_status != IB_MGMT_RMPP_STATUS_RESX) {
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_BAD_STATUS);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
} else
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- rmpp_mad->rmpp_hdr.rmpp_status);
+ abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status);
}
static void process_rmpp_abort(struct ib_mad_agent_private *agent,
@@ -745,12 +729,10 @@ static void process_rmpp_abort(struct ib_mad_agent_private *agent,
if (rmpp_mad->rmpp_hdr.rmpp_status < IB_MGMT_RMPP_STATUS_ABORT_MIN ||
rmpp_mad->rmpp_hdr.rmpp_status > IB_MGMT_RMPP_STATUS_ABORT_MAX) {
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_BAD_STATUS);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BAD_STATUS);
} else
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- rmpp_mad->rmpp_hdr.rmpp_status);
+ abort_send(agent, mad_recv_wc, rmpp_mad->rmpp_hdr.rmpp_status);
}
struct ib_mad_recv_wc *
@@ -764,8 +746,7 @@ ib_process_rmpp_recv_wc(struct ib_mad_agent_private *agent,
return mad_recv_wc;
if (rmpp_mad->rmpp_hdr.rmpp_version != IB_MGMT_RMPP_VERSION) {
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_UNV);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_UNV);
goto out;
}
@@ -783,8 +764,7 @@ ib_process_rmpp_recv_wc(struct ib_mad_agent_private *agent,
process_rmpp_abort(agent, mad_recv_wc);
break;
default:
- abort_send(agent, rmpp_mad->mad_hdr.tid,
- IB_MGMT_RMPP_STATUS_BADT);
+ abort_send(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT);
nack_recv(agent, mad_recv_wc, IB_MGMT_RMPP_STATUS_BADT);
break;
}
diff --git a/drivers/infiniband/core/user_mad.c b/drivers/infiniband/core/user_mad.c
index fb6cd42601f9..afe70a549c2f 100644
--- a/drivers/infiniband/core/user_mad.c
+++ b/drivers/infiniband/core/user_mad.c
@@ -177,17 +177,6 @@ static int queue_packet(struct ib_umad_file *file,
return ret;
}
-static int data_offset(u8 mgmt_class)
-{
- if (mgmt_class == IB_MGMT_CLASS_SUBN_ADM)
- return IB_MGMT_SA_HDR;
- else if ((mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START) &&
- (mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END))
- return IB_MGMT_VENDOR_HDR;
- else
- return IB_MGMT_RMPP_HDR;
-}
-
static void send_handler(struct ib_mad_agent *agent,
struct ib_mad_send_wc *send_wc)
{
@@ -283,7 +272,7 @@ static ssize_t copy_recv_mad(char __user *buf, struct ib_umad_packet *packet,
*/
return -ENOSPC;
}
- offset = data_offset(recv_buf->mad->mad_hdr.mgmt_class);
+ offset = ib_get_mad_data_offset(recv_buf->mad->mad_hdr.mgmt_class);
max_seg_payload = sizeof (struct ib_mad) - offset;
for (left = packet->length - seg_payload, buf += seg_payload;
@@ -441,21 +430,14 @@ static ssize_t ib_umad_write(struct file *filp, const char __user *buf,
}
rmpp_mad = (struct ib_rmpp_mad *) packet->mad.data;
- if (rmpp_mad->mad_hdr.mgmt_class == IB_MGMT_CLASS_SUBN_ADM) {
- hdr_len = IB_MGMT_SA_HDR;
- copy_offset = IB_MGMT_RMPP_HDR;
- rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
- IB_MGMT_RMPP_FLAG_ACTIVE;
- } else if (rmpp_mad->mad_hdr.mgmt_class >= IB_MGMT_CLASS_VENDOR_RANGE2_START &&
- rmpp_mad->mad_hdr.mgmt_class <= IB_MGMT_CLASS_VENDOR_RANGE2_END) {
- hdr_len = IB_MGMT_VENDOR_HDR;
+ hdr_len = ib_get_mad_data_offset(rmpp_mad->mad_hdr.mgmt_class);
+ if (!ib_is_mad_class_rmpp(rmpp_mad->mad_hdr.mgmt_class)) {
+ copy_offset = IB_MGMT_MAD_HDR;
+ rmpp_active = 0;
+ } else {
copy_offset = IB_MGMT_RMPP_HDR;
rmpp_active = ib_get_rmpp_flags(&rmpp_mad->rmpp_hdr) &
IB_MGMT_RMPP_FLAG_ACTIVE;
- } else {
- hdr_len = IB_MGMT_MAD_HDR;
- copy_offset = IB_MGMT_MAD_HDR;
- rmpp_active = 0;
}
data_len = count - sizeof (struct ib_user_mad) - hdr_len;
diff --git a/drivers/infiniband/hw/mthca/mthca_av.c b/drivers/infiniband/hw/mthca/mthca_av.c
index f023d3936518..bc5bdcbe51b5 100644
--- a/drivers/infiniband/hw/mthca/mthca_av.c
+++ b/drivers/infiniband/hw/mthca/mthca_av.c
@@ -265,7 +265,7 @@ int __devinit mthca_init_av_table(struct mthca_dev *dev)
return -ENOMEM;
}
-void __devexit mthca_cleanup_av_table(struct mthca_dev *dev)
+void mthca_cleanup_av_table(struct mthca_dev *dev)
{
if (mthca_is_memfree(dev))
return;
diff --git a/drivers/infiniband/hw/mthca/mthca_cq.c b/drivers/infiniband/hw/mthca/mthca_cq.c
index 76aabc5bf371..312cf90731ea 100644
--- a/drivers/infiniband/hw/mthca/mthca_cq.c
+++ b/drivers/infiniband/hw/mthca/mthca_cq.c
@@ -973,7 +973,7 @@ int __devinit mthca_init_cq_table(struct mthca_dev *dev)
return err;
}
-void __devexit mthca_cleanup_cq_table(struct mthca_dev *dev)
+void mthca_cleanup_cq_table(struct mthca_dev *dev)
{
mthca_array_cleanup(&dev->cq_table.cq, dev->limits.num_cqs);
mthca_alloc_cleanup(&dev->cq_table.alloc);
diff --git a/drivers/infiniband/hw/mthca/mthca_eq.c b/drivers/infiniband/hw/mthca/mthca_eq.c
index cbdc348fb689..99f109c3815d 100644
--- a/drivers/infiniband/hw/mthca/mthca_eq.c
+++ b/drivers/infiniband/hw/mthca/mthca_eq.c
@@ -765,7 +765,7 @@ static int __devinit mthca_map_eq_regs(struct mthca_dev *dev)
}
-static void __devexit mthca_unmap_eq_regs(struct mthca_dev *dev)
+static void mthca_unmap_eq_regs(struct mthca_dev *dev)
{
if (mthca_is_memfree(dev)) {
mthca_unmap_reg(dev, (pci_resource_len(dev->pdev, 0) - 1) &
@@ -821,7 +821,7 @@ int __devinit mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt)
return ret;
}
-void __devexit mthca_unmap_eq_icm(struct mthca_dev *dev)
+void mthca_unmap_eq_icm(struct mthca_dev *dev)
{
u8 status;
@@ -954,7 +954,7 @@ err_out_free:
return err;
}
-void __devexit mthca_cleanup_eq_table(struct mthca_dev *dev)
+void mthca_cleanup_eq_table(struct mthca_dev *dev)
{
u8 status;
int i;
diff --git a/drivers/infiniband/hw/mthca/mthca_mad.c b/drivers/infiniband/hw/mthca/mthca_mad.c
index 4ace6a392f41..dfb482eac9a2 100644
--- a/drivers/infiniband/hw/mthca/mthca_mad.c
+++ b/drivers/infiniband/hw/mthca/mthca_mad.c
@@ -271,7 +271,7 @@ err:
return PTR_ERR(agent);
}
-void mthca_free_agents(struct mthca_dev *dev)
+void __devexit mthca_free_agents(struct mthca_dev *dev)
{
struct ib_mad_agent *agent;
int p, q;
diff --git a/drivers/infiniband/hw/mthca/mthca_mcg.c b/drivers/infiniband/hw/mthca/mthca_mcg.c
index 9965bda9afed..47ca8a9b7247 100644
--- a/drivers/infiniband/hw/mthca/mthca_mcg.c
+++ b/drivers/infiniband/hw/mthca/mthca_mcg.c
@@ -388,7 +388,7 @@ int __devinit mthca_init_mcg_table(struct mthca_dev *dev)
return 0;
}
-void __devexit mthca_cleanup_mcg_table(struct mthca_dev *dev)
+void mthca_cleanup_mcg_table(struct mthca_dev *dev)
{
mthca_alloc_cleanup(&dev->mcg_table.alloc);
}
diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c
index 698b62125765..25e1c1db9a40 100644
--- a/drivers/infiniband/hw/mthca/mthca_mr.c
+++ b/drivers/infiniband/hw/mthca/mthca_mr.c
@@ -170,7 +170,7 @@ err_out:
return -ENOMEM;
}
-static void __devexit mthca_buddy_cleanup(struct mthca_buddy *buddy)
+static void mthca_buddy_cleanup(struct mthca_buddy *buddy)
{
int i;
@@ -866,7 +866,7 @@ err_mtt_buddy:
return err;
}
-void __devexit mthca_cleanup_mr_table(struct mthca_dev *dev)
+void mthca_cleanup_mr_table(struct mthca_dev *dev)
{
/* XXX check if any MRs are still allocated? */
if (dev->limits.fmr_reserved_mtts)
diff --git a/drivers/infiniband/hw/mthca/mthca_pd.c b/drivers/infiniband/hw/mthca/mthca_pd.c
index 105fc5faaddf..59df51614c85 100644
--- a/drivers/infiniband/hw/mthca/mthca_pd.c
+++ b/drivers/infiniband/hw/mthca/mthca_pd.c
@@ -77,7 +77,7 @@ int __devinit mthca_init_pd_table(struct mthca_dev *dev)
dev->limits.reserved_pds);
}
-void __devexit mthca_cleanup_pd_table(struct mthca_dev *dev)
+void mthca_cleanup_pd_table(struct mthca_dev *dev)
{
/* XXX check if any PDs are still allocated? */
mthca_alloc_cleanup(&dev->pd_table.alloc);
diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c
index 1bc2678c2fae..057c8e6af87b 100644
--- a/drivers/infiniband/hw/mthca/mthca_qp.c
+++ b/drivers/infiniband/hw/mthca/mthca_qp.c
@@ -2204,7 +2204,7 @@ int __devinit mthca_init_qp_table(struct mthca_dev *dev)
return err;
}
-void __devexit mthca_cleanup_qp_table(struct mthca_dev *dev)
+void mthca_cleanup_qp_table(struct mthca_dev *dev)
{
int i;
u8 status;
diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c
index 0cfd15802217..2dd3aea05341 100644
--- a/drivers/infiniband/hw/mthca/mthca_srq.c
+++ b/drivers/infiniband/hw/mthca/mthca_srq.c
@@ -206,7 +206,7 @@ int mthca_alloc_srq(struct mthca_dev *dev, struct mthca_pd *pd,
roundup_pow_of_two(sizeof (struct mthca_next_seg) +
srq->max_gs * sizeof (struct mthca_data_seg)));
- if (ds > dev->limits.max_desc_sz)
+ if (!mthca_is_memfree(dev) && (ds > dev->limits.max_desc_sz))
return -EINVAL;
srq->wqe_shift = long_log2(ds);
@@ -684,7 +684,7 @@ int __devinit mthca_init_srq_table(struct mthca_dev *dev)
return err;
}
-void __devexit mthca_cleanup_srq_table(struct mthca_dev *dev)
+void mthca_cleanup_srq_table(struct mthca_dev *dev)
{
if (!(dev->mthca_flags & MTHCA_FLAG_SRQ))
return;
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
index 53a32f65788d..9b0bd7c746ca 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
@@ -723,7 +723,7 @@ 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->dst->neighbour) {
+ if ((!skb->dst || !skb->dst->neighbour) && daddr) {
struct ipoib_pseudoheader *phdr =
(struct ipoib_pseudoheader *) skb_push(skb, sizeof *phdr);
memcpy(phdr->hwaddr, daddr, INFINIBAND_ALEN);
diff --git a/drivers/infiniband/ulp/srp/ib_srp.c b/drivers/infiniband/ulp/srp/ib_srp.c
index 61924cc30e55..fd8a95a9c5d3 100644
--- a/drivers/infiniband/ulp/srp/ib_srp.c
+++ b/drivers/infiniband/ulp/srp/ib_srp.c
@@ -607,10 +607,10 @@ static void srp_unmap_data(struct scsi_cmnd *scmnd,
*/
if (likely(scmnd->use_sg)) {
nents = scmnd->use_sg;
- scat = (struct scatterlist *) scmnd->request_buffer;
+ scat = scmnd->request_buffer;
} else {
nents = 1;
- scat = (struct scatterlist *) scmnd->request_buffer;
+ scat = &req->fake_sg;
}
dma_unmap_sg(target->srp_host->dev->dma_device, scat, nents,
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
index 0a90962c38e7..63f387e4b783 100644
--- a/drivers/input/keyboard/hil_kbd.c
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -66,7 +66,7 @@ static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] =
static char hil_language[][16] = { HIL_LOCALE_MAP };
struct hil_kbd {
- struct input_dev dev;
+ struct input_dev *dev;
struct serio *serio;
/* Input buffer and index for packets from HIL bus. */
@@ -86,7 +86,7 @@ struct hil_kbd {
/* Process a complete packet after transfer from the HIL */
static void hil_kbd_process_record(struct hil_kbd *kbd)
{
- struct input_dev *dev = &kbd->dev;
+ struct input_dev *dev = kbd->dev;
hil_packet *data = kbd->data;
hil_packet p;
int idx, i, cnt;
@@ -240,8 +240,8 @@ static void hil_kbd_disconnect(struct serio *serio)
return;
}
- input_unregister_device(&kbd->dev);
serio_close(serio);
+ input_unregister_device(kbd->dev);
kfree(kbd);
}
@@ -251,16 +251,18 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
uint8_t did, *idd;
int i;
- kbd = kmalloc(sizeof(*kbd), GFP_KERNEL);
+ kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
if (!kbd)
return -ENOMEM;
- memset(kbd, 0, sizeof(struct hil_kbd));
+
+ kbd->dev = input_allocate_device();
+ if (!kbd->dev) goto bail1;
+ kbd->dev->private = kbd;
if (serio_open(serio, drv)) goto bail0;
serio_set_drvdata(serio, kbd);
kbd->serio = serio;
- kbd->dev.private = kbd;
init_MUTEX_LOCKED(&(kbd->sem));
@@ -302,38 +304,38 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
break;
default:
- goto bail1;
+ goto bail2;
}
if(HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
- goto bail1;
+ goto bail2;
}
- kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
- kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
- kbd->dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
- kbd->dev.keycodesize = sizeof(hil_kbd_set1[0]);
- kbd->dev.keycode = hil_kbd_set1;
- kbd->dev.name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
- kbd->dev.phys = "hpkbd/input0"; /* XXX */
+ kbd->dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ kbd->dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ kbd->dev->keycodesize = sizeof(hil_kbd_set1[0]);
+ kbd->dev->keycode = hil_kbd_set1;
+ kbd->dev->name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
+ kbd->dev->phys = "hpkbd/input0"; /* XXX */
- kbd->dev.id.bustype = BUS_HIL;
- kbd->dev.id.vendor = PCI_VENDOR_ID_HP;
- kbd->dev.id.product = 0x0001; /* TODO: get from kbd->rsc */
- kbd->dev.id.version = 0x0100; /* TODO: get from kbd->rsc */
- kbd->dev.dev = &serio->dev;
+ kbd->dev->id.bustype = BUS_HIL;
+ kbd->dev->id.vendor = PCI_VENDOR_ID_HP;
+ kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
+ kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
+ kbd->dev->dev = &serio->dev;
for (i = 0; i < 128; i++) {
- set_bit(hil_kbd_set1[i], kbd->dev.keybit);
- set_bit(hil_kbd_set3[i], kbd->dev.keybit);
+ set_bit(hil_kbd_set1[i], kbd->dev->keybit);
+ set_bit(hil_kbd_set3[i], kbd->dev->keybit);
}
- clear_bit(0, kbd->dev.keybit);
+ clear_bit(0, kbd->dev->keybit);
- input_register_device(&kbd->dev);
+ input_register_device(kbd->dev);
printk(KERN_INFO "input: %s, ID: %d\n",
- kbd->dev.name, did);
+ kbd->dev->name, did);
serio->write(serio, 0);
serio->write(serio, 0);
@@ -343,8 +345,10 @@ static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
up(&(kbd->sem));
return 0;
- bail1:
+ bail2:
serio_close(serio);
+ bail1:
+ input_free_device(kbd->dev);
bail0:
kfree(kbd);
serio_set_drvdata(serio, NULL);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
index e95bc052e32a..33edd030aa75 100644
--- a/drivers/input/keyboard/hilkbd.c
+++ b/drivers/input/keyboard/hilkbd.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 1998 Philip Blundell <philb@gnu.org>
* Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
- * Copyright (C) 1999-2003 Helge Deller <deller@gmx.de>
+ * Copyright (C) 1999-2006 Helge Deller <deller@gmx.de>
*
* Very basic HP Human Interface Loop (HIL) driver.
* This driver handles the keyboard on HP300 (m68k) and on some
@@ -90,7 +90,7 @@ static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] =
/* HIL structure */
static struct {
- struct input_dev dev;
+ struct input_dev *dev;
unsigned int curdev;
@@ -117,7 +117,7 @@ static void poll_finished(void)
down = (hil_dev.data[1] & 1) == 0;
scode = hil_dev.data[1] >> 1;
key = hphilkeyb_keycode[scode];
- input_report_key(&hil_dev.dev, key, down);
+ input_report_key(hil_dev.dev, key, down);
break;
}
hil_dev.curdev = 0;
@@ -207,9 +207,14 @@ hil_keyb_init(void)
unsigned int i, kbid;
wait_queue_head_t hil_wait;
- if (hil_dev.dev.id.bustype) {
+ if (hil_dev.dev) {
return -ENODEV; /* already initialized */
}
+
+ hil_dev.dev = input_allocate_device();
+ if (!hil_dev.dev)
+ return -ENOMEM;
+ hil_dev.dev->private = &hil_dev;
#if defined(CONFIG_HP300)
if (!hwreg_present((void *)(HILBASE + HIL_DATA)))
@@ -247,28 +252,26 @@ hil_keyb_init(void)
c = 0;
hil_do(HIL_WRITEKBDSADR, &c, 1);
- init_input_dev(&hil_dev.dev);
-
for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
if (hphilkeyb_keycode[i] != KEY_RESERVED)
- set_bit(hphilkeyb_keycode[i], hil_dev.dev.keybit);
-
- hil_dev.dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
- hil_dev.dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
- hil_dev.dev.keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
- hil_dev.dev.keycodesize = sizeof(hphilkeyb_keycode[0]);
- hil_dev.dev.keycode = hphilkeyb_keycode;
- hil_dev.dev.name = "HIL keyboard";
- hil_dev.dev.phys = "hpkbd/input0";
-
- hil_dev.dev.id.bustype = BUS_HIL;
- hil_dev.dev.id.vendor = PCI_VENDOR_ID_HP;
- hil_dev.dev.id.product = 0x0001;
- hil_dev.dev.id.version = 0x0010;
-
- input_register_device(&hil_dev.dev);
+ set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
+
+ hil_dev.dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ hil_dev.dev->ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL);
+ hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ hil_dev.dev->keycodesize = sizeof(hphilkeyb_keycode[0]);
+ hil_dev.dev->keycode = hphilkeyb_keycode;
+ hil_dev.dev->name = "HIL keyboard";
+ hil_dev.dev->phys = "hpkbd/input0";
+
+ hil_dev.dev->id.bustype = BUS_HIL;
+ hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP;
+ hil_dev.dev->id.product = 0x0001;
+ hil_dev.dev->id.version = 0x0010;
+
+ input_register_device(hil_dev.dev);
printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
- hil_dev.dev.name, kbid, HILBASE, HIL_IRQ);
+ hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
return 0;
}
@@ -329,7 +332,9 @@ static void __exit hil_exit(void)
/* Turn off interrupts */
hil_do(HIL_INTOFF, NULL, 0);
- input_unregister_device(&hil_dev.dev);
+ input_unregister_device(hil_dev.dev);
+
+ hil_dev.dev = NULL;
#if defined(CONFIG_PARISC)
unregister_parisc_driver(&hil_driver);
diff --git a/drivers/input/mouse/hil_ptr.c b/drivers/input/mouse/hil_ptr.c
index c2bf2ed07dc6..bfb564fd8fe2 100644
--- a/drivers/input/mouse/hil_ptr.c
+++ b/drivers/input/mouse/hil_ptr.c
@@ -55,7 +55,7 @@ MODULE_LICENSE("Dual BSD/GPL");
#define HIL_PTR_MAX_LENGTH 16
struct hil_ptr {
- struct input_dev dev;
+ struct input_dev *dev;
struct serio *serio;
/* Input buffer and index for packets from HIL bus. */
@@ -79,7 +79,7 @@ struct hil_ptr {
/* Process a complete packet after transfer from the HIL */
static void hil_ptr_process_record(struct hil_ptr *ptr)
{
- struct input_dev *dev = &ptr->dev;
+ struct input_dev *dev = ptr->dev;
hil_packet *data = ptr->data;
hil_packet p;
int idx, i, cnt, laxis;
@@ -148,12 +148,12 @@ static void hil_ptr_process_record(struct hil_ptr *ptr)
if (absdev) {
val = lo + (hi<<8);
#ifdef TABLET_AUTOADJUST
- if (val < ptr->dev.absmin[ABS_X + i])
- ptr->dev.absmin[ABS_X + i] = val;
- if (val > ptr->dev.absmax[ABS_X + i])
- ptr->dev.absmax[ABS_X + i] = val;
+ if (val < dev->absmin[ABS_X + i])
+ dev->absmin[ABS_X + i] = val;
+ if (val > dev->absmax[ABS_X + i])
+ dev->absmax[ABS_X + i] = val;
#endif
- if (i%3) val = ptr->dev.absmax[ABS_X + i] - val;
+ if (i%3) val = dev->absmax[ABS_X + i] - val;
input_report_abs(dev, ABS_X + i, val);
} else {
val = (int) (((int8_t)lo) | ((int8_t)hi<<8));
@@ -233,26 +233,29 @@ static void hil_ptr_disconnect(struct serio *serio)
return;
}
- input_unregister_device(&ptr->dev);
serio_close(serio);
+ input_unregister_device(ptr->dev);
kfree(ptr);
}
static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
{
- struct hil_ptr *ptr;
- char *txt;
- unsigned int i, naxsets, btntype;
- uint8_t did, *idd;
+ struct hil_ptr *ptr;
+ char *txt;
+ unsigned int i, naxsets, btntype;
+ uint8_t did, *idd;
- if (!(ptr = kmalloc(sizeof(struct hil_ptr), GFP_KERNEL))) return -ENOMEM;
- memset(ptr, 0, sizeof(struct hil_ptr));
+ if (!(ptr = kzalloc(sizeof(struct hil_ptr), GFP_KERNEL)))
+ return -ENOMEM;
- if (serio_open(serio, driver)) goto bail0;
+ ptr->dev = input_allocate_device();
+ if (!ptr->dev) goto bail0;
+ ptr->dev->private = ptr;
+
+ if (serio_open(serio, driver)) goto bail1;
serio_set_drvdata(serio, ptr);
ptr->serio = serio;
- ptr->dev.private = ptr;
init_MUTEX_LOCKED(&(ptr->sem));
@@ -283,25 +286,24 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
up(&(ptr->sem));
- init_input_dev(&ptr->dev);
did = ptr->idd[0];
idd = ptr->idd + 1;
txt = "unknown";
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
- ptr->dev.evbit[0] = BIT(EV_REL);
+ ptr->dev->evbit[0] = BIT(EV_REL);
txt = "relative";
}
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_ABS) {
- ptr->dev.evbit[0] = BIT(EV_ABS);
+ ptr->dev->evbit[0] = BIT(EV_ABS);
txt = "absolute";
}
- if (!ptr->dev.evbit[0]) {
- goto bail1;
+ if (!ptr->dev->evbit[0]) {
+ goto bail2;
}
ptr->nbtn = HIL_IDD_NUM_BUTTONS(idd);
- if (ptr->nbtn) ptr->dev.evbit[0] |= BIT(EV_KEY);
+ if (ptr->nbtn) ptr->dev->evbit[0] |= BIT(EV_KEY);
naxsets = HIL_IDD_NUM_AXSETS(*idd);
ptr->naxes = HIL_IDD_NUM_AXES_PER_SET(*idd);
@@ -325,7 +327,7 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
btntype = BTN_MOUSE;
for (i = 0; i < ptr->nbtn; i++) {
- set_bit(btntype | i, ptr->dev.keybit);
+ set_bit(btntype | i, ptr->dev->keybit);
ptr->btnmap[i] = btntype | i;
}
@@ -337,50 +339,52 @@ static int hil_ptr_connect(struct serio *serio, struct serio_driver *driver)
if ((did & HIL_IDD_DID_TYPE_MASK) == HIL_IDD_DID_TYPE_REL) {
for (i = 0; i < ptr->naxes; i++) {
- set_bit(REL_X + i, ptr->dev.relbit);
+ set_bit(REL_X + i, ptr->dev->relbit);
}
for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
- set_bit(REL_X + i, ptr->dev.relbit);
+ set_bit(REL_X + i, ptr->dev->relbit);
}
} else {
for (i = 0; i < ptr->naxes; i++) {
- set_bit(ABS_X + i, ptr->dev.absbit);
- ptr->dev.absmin[ABS_X + i] = 0;
- ptr->dev.absmax[ABS_X + i] =
+ set_bit(ABS_X + i, ptr->dev->absbit);
+ ptr->dev->absmin[ABS_X + i] = 0;
+ ptr->dev->absmax[ABS_X + i] =
HIL_IDD_AXIS_MAX((ptr->idd + 1), i);
}
for (i = 3; (i < ptr->naxes + 3) && (naxsets > 1); i++) {
- set_bit(ABS_X + i, ptr->dev.absbit);
- ptr->dev.absmin[ABS_X + i] = 0;
- ptr->dev.absmax[ABS_X + i] =
+ set_bit(ABS_X + i, ptr->dev->absbit);
+ ptr->dev->absmin[ABS_X + i] = 0;
+ ptr->dev->absmax[ABS_X + i] =
HIL_IDD_AXIS_MAX((ptr->idd + 1), (i - 3));
}
#ifdef TABLET_AUTOADJUST
for (i = 0; i < ABS_MAX; i++) {
- int diff = ptr->dev.absmax[ABS_X + i] / 10;
- ptr->dev.absmin[ABS_X + i] += diff;
- ptr->dev.absmax[ABS_X + i] -= diff;
+ int diff = ptr->dev->absmax[ABS_X + i] / 10;
+ ptr->dev->absmin[ABS_X + i] += diff;
+ ptr->dev->absmax[ABS_X + i] -= diff;
}
#endif
}
- ptr->dev.name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
+ ptr->dev->name = strlen(ptr->rnm) ? ptr->rnm : HIL_GENERIC_NAME;
- ptr->dev.id.bustype = BUS_HIL;
- ptr->dev.id.vendor = PCI_VENDOR_ID_HP;
- ptr->dev.id.product = 0x0001; /* TODO: get from ptr->rsc */
- ptr->dev.id.version = 0x0100; /* TODO: get from ptr->rsc */
- ptr->dev.dev = &serio->dev;
+ ptr->dev->id.bustype = BUS_HIL;
+ ptr->dev->id.vendor = PCI_VENDOR_ID_HP;
+ ptr->dev->id.product = 0x0001; /* TODO: get from ptr->rsc */
+ ptr->dev->id.version = 0x0100; /* TODO: get from ptr->rsc */
+ ptr->dev->dev = &serio->dev;
- input_register_device(&ptr->dev);
+ input_register_device(ptr->dev);
printk(KERN_INFO "input: %s (%s), ID: %d\n",
- ptr->dev.name,
+ ptr->dev->name,
(btntype == BTN_MOUSE) ? "HIL mouse":"HIL tablet or touchpad",
did);
return 0;
- bail1:
+ bail2:
serio_close(serio);
+ bail1:
+ input_free_device(ptr->dev);
bail0:
kfree(ptr);
serio_set_drvdata(serio, NULL);
diff --git a/drivers/input/serio/gscps2.c b/drivers/input/serio/gscps2.c
index a7b0de0f92b2..c0b1e4becad3 100644
--- a/drivers/input/serio/gscps2.c
+++ b/drivers/input/serio/gscps2.c
@@ -1,7 +1,7 @@
/*
* drivers/input/serio/gscps2.c
*
- * Copyright (c) 2004 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2004-2006 Helge Deller <deller@gmx.de>
* Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
* Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
*
@@ -354,7 +354,7 @@ static int __init gscps2_probe(struct parisc_device *dev)
memset(serio, 0, sizeof(struct serio));
ps2port->port = serio;
ps2port->padev = dev;
- ps2port->addr = ioremap(hpa, GSC_STATUS + 4);
+ ps2port->addr = ioremap_nocache(hpa, GSC_STATUS + 4);
spin_lock_init(&ps2port->lock);
gscps2_reset(ps2port);
diff --git a/drivers/isdn/sc/ioctl.c b/drivers/isdn/sc/ioctl.c
index 94c9afb7017c..f4f71226a078 100644
--- a/drivers/isdn/sc/ioctl.c
+++ b/drivers/isdn/sc/ioctl.c
@@ -46,7 +46,8 @@ int sc_ioctl(int card, scs_ioctl *data)
pr_debug("%s: SCIOCRESET: ioctl received\n",
sc_adapter[card]->devicename);
sc_adapter[card]->StartOnReset = 0;
- return (reset(card));
+ kfree(rcvmsg);
+ return reset(card);
}
case SCIOCLOAD:
@@ -183,7 +184,7 @@ int sc_ioctl(int card, scs_ioctl *data)
sc_adapter[card]->devicename);
spid = kmalloc(SCIOC_SPIDSIZE, GFP_KERNEL);
- if(!spid) {
+ if (!spid) {
kfree(rcvmsg);
return -ENOMEM;
}
@@ -195,10 +196,10 @@ int sc_ioctl(int card, scs_ioctl *data)
if (!status) {
pr_debug("%s: SCIOCGETSPID: command successful\n",
sc_adapter[card]->devicename);
- }
- else {
+ } else {
pr_debug("%s: SCIOCGETSPID: command failed (status = %d)\n",
sc_adapter[card]->devicename, status);
+ kfree(spid);
kfree(rcvmsg);
return status;
}
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
new file mode 100644
index 000000000000..2c4f20b7f021
--- /dev/null
+++ b/drivers/leds/Kconfig
@@ -0,0 +1,77 @@
+
+menu "LED devices"
+
+config NEW_LEDS
+ bool "LED Support"
+ help
+ Say Y to enable Linux LED support. This is not related to standard
+ keyboard LEDs which are controlled via the input system.
+
+config LEDS_CLASS
+ tristate "LED Class Support"
+ depends NEW_LEDS
+ help
+ This option enables the led sysfs class in /sys/class/leds. You'll
+ need this to do anything useful with LEDs. If unsure, say N.
+
+config LEDS_TRIGGERS
+ bool "LED Trigger support"
+ depends NEW_LEDS
+ help
+ This option enables trigger support for the leds class.
+ These triggers allow kernel events to drive the LEDs and can
+ be configured via sysfs. If unsure, say Y.
+
+config LEDS_CORGI
+ tristate "LED Support for the Sharp SL-C7x0 series"
+ depends LEDS_CLASS && PXA_SHARP_C7xx
+ help
+ This option enables support for the LEDs on Sharp Zaurus
+ SL-C7x0 series (C700, C750, C760, C860).
+
+config LEDS_LOCOMO
+ tristate "LED Support for Locomo device"
+ depends LEDS_CLASS && SHARP_LOCOMO
+ help
+ This option enables support for the LEDs on Sharp Locomo.
+ Zaurus models SL-5500 and SL-5600.
+
+config LEDS_SPITZ
+ tristate "LED Support for the Sharp SL-Cxx00 series"
+ depends LEDS_CLASS && PXA_SHARP_Cxx00
+ help
+ This option enables support for the LEDs on Sharp Zaurus
+ SL-Cxx00 series (C1000, C3000, C3100).
+
+config LEDS_IXP4XX
+ tristate "LED Support for GPIO connected LEDs on IXP4XX processors"
+ depends LEDS_CLASS && ARCH_IXP4XX
+ help
+ This option enables support for the LEDs connected to GPIO
+ outputs of the Intel IXP4XX processors. To be useful the
+ particular board must have LEDs and they must be connected
+ to the GPIO lines. If unsure, say Y.
+
+config LEDS_TOSA
+ tristate "LED Support for the Sharp SL-6000 series"
+ depends LEDS_CLASS && PXA_SHARPSL
+ help
+ This option enables support for the LEDs on Sharp Zaurus
+ SL-6000 series.
+
+config LEDS_TRIGGER_TIMER
+ tristate "LED Timer Trigger"
+ depends LEDS_TRIGGERS
+ help
+ This allows LEDs to be controlled by a programmable timer
+ via sysfs. If unsure, say Y.
+
+config LEDS_TRIGGER_IDE_DISK
+ bool "LED Timer Trigger"
+ depends LEDS_TRIGGERS && BLK_DEV_IDEDISK
+ help
+ This allows LEDs to be controlled by IDE disk activity.
+ If unsure, say Y.
+
+endmenu
+
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
new file mode 100644
index 000000000000..40699d3cabbf
--- /dev/null
+++ b/drivers/leds/Makefile
@@ -0,0 +1,16 @@
+
+# LED Core
+obj-$(CONFIG_NEW_LEDS) += led-core.o
+obj-$(CONFIG_LEDS_CLASS) += led-class.o
+obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o
+
+# LED Platform Drivers
+obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o
+obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
+obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o
+obj-$(CONFIG_LEDS_IXP4XX) += leds-ixp4xx-gpio.o
+obj-$(CONFIG_LEDS_TOSA) += leds-tosa.o
+
+# LED Triggers
+obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o
+obj-$(CONFIG_LEDS_TRIGGER_IDE_DISK) += ledtrig-ide-disk.o
diff --git a/drivers/leds/led-class.c b/drivers/leds/led-class.c
new file mode 100644
index 000000000000..b0b5d05fadd6
--- /dev/null
+++ b/drivers/leds/led-class.c
@@ -0,0 +1,167 @@
+/*
+ * LED Class Core
+ *
+ * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
+ * Copyright (C) 2005-2006 Richard Purdie <rpurdie@openedhand.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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+static struct class *leds_class;
+
+static ssize_t led_brightness_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ ssize_t ret = 0;
+
+ /* no lock needed for this */
+ sprintf(buf, "%u\n", led_cdev->brightness);
+ ret = strlen(buf) + 1;
+
+ return ret;
+}
+
+static ssize_t led_brightness_store(struct class_device *dev,
+ const char *buf, size_t size)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ ssize_t ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+
+ if (after - buf > 0) {
+ ret = after - buf;
+ led_set_brightness(led_cdev, state);
+ }
+
+ return ret;
+}
+
+static CLASS_DEVICE_ATTR(brightness, 0644, led_brightness_show,
+ led_brightness_store);
+#ifdef CONFIG_LEDS_TRIGGERS
+static CLASS_DEVICE_ATTR(trigger, 0644, led_trigger_show, led_trigger_store);
+#endif
+
+/**
+ * led_classdev_suspend - suspend an led_classdev.
+ * @led_cdev: the led_classdev to suspend.
+ */
+void led_classdev_suspend(struct led_classdev *led_cdev)
+{
+ led_cdev->flags |= LED_SUSPENDED;
+ led_cdev->brightness_set(led_cdev, 0);
+}
+EXPORT_SYMBOL_GPL(led_classdev_suspend);
+
+/**
+ * led_classdev_resume - resume an led_classdev.
+ * @led_cdev: the led_classdev to resume.
+ */
+void led_classdev_resume(struct led_classdev *led_cdev)
+{
+ led_cdev->brightness_set(led_cdev, led_cdev->brightness);
+ led_cdev->flags &= ~LED_SUSPENDED;
+}
+EXPORT_SYMBOL_GPL(led_classdev_resume);
+
+/**
+ * led_classdev_register - register a new object of led_classdev class.
+ * @dev: The device to register.
+ * @led_cdev: the led_classdev structure for this device.
+ */
+int led_classdev_register(struct device *parent, struct led_classdev *led_cdev)
+{
+ led_cdev->class_dev = class_device_create(leds_class, NULL, 0,
+ parent, "%s", led_cdev->name);
+ if (unlikely(IS_ERR(led_cdev->class_dev)))
+ return PTR_ERR(led_cdev->class_dev);
+
+ class_set_devdata(led_cdev->class_dev, led_cdev);
+
+ /* register the attributes */
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_brightness);
+
+ /* add to the list of leds */
+ write_lock(&leds_list_lock);
+ list_add_tail(&led_cdev->node, &leds_list);
+ write_unlock(&leds_list_lock);
+
+#ifdef CONFIG_LEDS_TRIGGERS
+ rwlock_init(&led_cdev->trigger_lock);
+
+ led_trigger_set_default(led_cdev);
+
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_trigger);
+#endif
+
+ printk(KERN_INFO "Registered led device: %s\n",
+ led_cdev->class_dev->class_id);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(led_classdev_register);
+
+/**
+ * led_classdev_unregister - unregisters a object of led_properties class.
+ * @led_cdev: the led device to unreigister
+ *
+ * Unregisters a previously registered via led_classdev_register object.
+ */
+void led_classdev_unregister(struct led_classdev *led_cdev)
+{
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_brightness);
+#ifdef CONFIG_LEDS_TRIGGERS
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_trigger);
+ write_lock(&led_cdev->trigger_lock);
+ if (led_cdev->trigger)
+ led_trigger_set(led_cdev, NULL);
+ write_unlock(&led_cdev->trigger_lock);
+#endif
+
+ class_device_unregister(led_cdev->class_dev);
+
+ write_lock(&leds_list_lock);
+ list_del(&led_cdev->node);
+ write_unlock(&leds_list_lock);
+}
+EXPORT_SYMBOL_GPL(led_classdev_unregister);
+
+static int __init leds_init(void)
+{
+ leds_class = class_create(THIS_MODULE, "leds");
+ if (IS_ERR(leds_class))
+ return PTR_ERR(leds_class);
+ return 0;
+}
+
+static void __exit leds_exit(void)
+{
+ class_destroy(leds_class);
+}
+
+subsys_initcall(leds_init);
+module_exit(leds_exit);
+
+MODULE_AUTHOR("John Lenz, Richard Purdie");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Class Interface");
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
new file mode 100644
index 000000000000..fe6541326c71
--- /dev/null
+++ b/drivers/leds/led-core.c
@@ -0,0 +1,25 @@
+/*
+ * LED Class Core
+ *
+ * Copyright 2005-2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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/list.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+rwlock_t leds_list_lock = RW_LOCK_UNLOCKED;
+LIST_HEAD(leds_list);
+
+EXPORT_SYMBOL_GPL(leds_list);
+EXPORT_SYMBOL_GPL(leds_list_lock);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
new file mode 100644
index 000000000000..5e2cd8be1191
--- /dev/null
+++ b/drivers/leds/led-triggers.c
@@ -0,0 +1,239 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2005-2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+/*
+ * Nests outside led_cdev->trigger_lock
+ */
+static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED;
+static LIST_HEAD(trigger_list);
+
+ssize_t led_trigger_store(struct class_device *dev, const char *buf,
+ size_t count)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ char trigger_name[TRIG_NAME_MAX];
+ struct led_trigger *trig;
+ size_t len;
+
+ trigger_name[sizeof(trigger_name) - 1] = '\0';
+ strncpy(trigger_name, buf, sizeof(trigger_name) - 1);
+ len = strlen(trigger_name);
+
+ if (len && trigger_name[len - 1] == '\n')
+ trigger_name[len - 1] = '\0';
+
+ if (!strcmp(trigger_name, "none")) {
+ write_lock(&led_cdev->trigger_lock);
+ led_trigger_set(led_cdev, NULL);
+ write_unlock(&led_cdev->trigger_lock);
+ return count;
+ }
+
+ read_lock(&triggers_list_lock);
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(trigger_name, trig->name)) {
+ write_lock(&led_cdev->trigger_lock);
+ led_trigger_set(led_cdev, trig);
+ write_unlock(&led_cdev->trigger_lock);
+
+ read_unlock(&triggers_list_lock);
+ return count;
+ }
+ }
+ read_unlock(&triggers_list_lock);
+
+ return -EINVAL;
+}
+
+
+ssize_t led_trigger_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct led_trigger *trig;
+ int len = 0;
+
+ read_lock(&triggers_list_lock);
+ read_lock(&led_cdev->trigger_lock);
+
+ if (!led_cdev->trigger)
+ len += sprintf(buf+len, "[none] ");
+ else
+ len += sprintf(buf+len, "none ");
+
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (led_cdev->trigger && !strcmp(led_cdev->trigger->name,
+ trig->name))
+ len += sprintf(buf+len, "[%s] ", trig->name);
+ else
+ len += sprintf(buf+len, "%s ", trig->name);
+ }
+ read_unlock(&led_cdev->trigger_lock);
+ read_unlock(&triggers_list_lock);
+
+ len += sprintf(len+buf, "\n");
+ return len;
+}
+
+void led_trigger_event(struct led_trigger *trigger,
+ enum led_brightness brightness)
+{
+ struct list_head *entry;
+
+ if (!trigger)
+ return;
+
+ read_lock(&trigger->leddev_list_lock);
+ list_for_each(entry, &trigger->led_cdevs) {
+ struct led_classdev *led_cdev;
+
+ led_cdev = list_entry(entry, struct led_classdev, trig_list);
+ led_set_brightness(led_cdev, brightness);
+ }
+ read_unlock(&trigger->leddev_list_lock);
+}
+
+/* Caller must ensure led_cdev->trigger_lock held */
+void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
+{
+ unsigned long flags;
+
+ /* Remove any existing trigger */
+ if (led_cdev->trigger) {
+ write_lock_irqsave(&led_cdev->trigger->leddev_list_lock, flags);
+ list_del(&led_cdev->trig_list);
+ write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
+ if (led_cdev->trigger->deactivate)
+ led_cdev->trigger->deactivate(led_cdev);
+ }
+ if (trigger) {
+ write_lock_irqsave(&trigger->leddev_list_lock, flags);
+ list_add_tail(&led_cdev->trig_list, &trigger->led_cdevs);
+ write_unlock_irqrestore(&trigger->leddev_list_lock, flags);
+ if (trigger->activate)
+ trigger->activate(led_cdev);
+ }
+ led_cdev->trigger = trigger;
+}
+
+void led_trigger_set_default(struct led_classdev *led_cdev)
+{
+ struct led_trigger *trig;
+
+ if (!led_cdev->default_trigger)
+ return;
+
+ read_lock(&triggers_list_lock);
+ write_lock(&led_cdev->trigger_lock);
+ list_for_each_entry(trig, &trigger_list, next_trig) {
+ if (!strcmp(led_cdev->default_trigger, trig->name))
+ led_trigger_set(led_cdev, trig);
+ }
+ write_unlock(&led_cdev->trigger_lock);
+ read_unlock(&triggers_list_lock);
+}
+
+int led_trigger_register(struct led_trigger *trigger)
+{
+ struct led_classdev *led_cdev;
+
+ rwlock_init(&trigger->leddev_list_lock);
+ INIT_LIST_HEAD(&trigger->led_cdevs);
+
+ /* Add to the list of led triggers */
+ write_lock(&triggers_list_lock);
+ list_add_tail(&trigger->next_trig, &trigger_list);
+ write_unlock(&triggers_list_lock);
+
+ /* Register with any LEDs that have this as a default trigger */
+ read_lock(&leds_list_lock);
+ list_for_each_entry(led_cdev, &leds_list, node) {
+ write_lock(&led_cdev->trigger_lock);
+ if (!led_cdev->trigger && led_cdev->default_trigger &&
+ !strcmp(led_cdev->default_trigger, trigger->name))
+ led_trigger_set(led_cdev, trigger);
+ write_unlock(&led_cdev->trigger_lock);
+ }
+ read_unlock(&leds_list_lock);
+
+ return 0;
+}
+
+void led_trigger_register_simple(const char *name, struct led_trigger **tp)
+{
+ struct led_trigger *trigger;
+
+ trigger = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
+
+ if (trigger) {
+ trigger->name = name;
+ led_trigger_register(trigger);
+ }
+ *tp = trigger;
+}
+
+void led_trigger_unregister(struct led_trigger *trigger)
+{
+ struct led_classdev *led_cdev;
+
+ /* Remove from the list of led triggers */
+ write_lock(&triggers_list_lock);
+ list_del(&trigger->next_trig);
+ write_unlock(&triggers_list_lock);
+
+ /* Remove anyone actively using this trigger */
+ read_lock(&leds_list_lock);
+ list_for_each_entry(led_cdev, &leds_list, node) {
+ write_lock(&led_cdev->trigger_lock);
+ if (led_cdev->trigger == trigger)
+ led_trigger_set(led_cdev, NULL);
+ write_unlock(&led_cdev->trigger_lock);
+ }
+ read_unlock(&leds_list_lock);
+}
+
+void led_trigger_unregister_simple(struct led_trigger *trigger)
+{
+ led_trigger_unregister(trigger);
+ kfree(trigger);
+}
+
+/* Used by LED Class */
+EXPORT_SYMBOL_GPL(led_trigger_set);
+EXPORT_SYMBOL_GPL(led_trigger_set_default);
+EXPORT_SYMBOL_GPL(led_trigger_show);
+EXPORT_SYMBOL_GPL(led_trigger_store);
+
+/* LED Trigger Interface */
+EXPORT_SYMBOL_GPL(led_trigger_register);
+EXPORT_SYMBOL_GPL(led_trigger_unregister);
+
+/* Simple LED Tigger Interface */
+EXPORT_SYMBOL_GPL(led_trigger_register_simple);
+EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);
+EXPORT_SYMBOL_GPL(led_trigger_event);
+
+MODULE_AUTHOR("Richard Purdie");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LED Triggers Core");
diff --git a/drivers/leds/leds-corgi.c b/drivers/leds/leds-corgi.c
new file mode 100644
index 000000000000..bb7d84df0121
--- /dev/null
+++ b/drivers/leds/leds-corgi.c
@@ -0,0 +1,121 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2005-2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <asm/mach-types.h>
+#include <asm/arch/corgi.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/hardware/scoop.h>
+
+static void corgiled_amber_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ if (value)
+ GPSR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE);
+ else
+ GPCR0 = GPIO_bit(CORGI_GPIO_LED_ORANGE);
+}
+
+static void corgiled_green_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN);
+ else
+ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_LED_GREEN);
+}
+
+static struct led_classdev corgi_amber_led = {
+ .name = "corgi:amber",
+ .default_trigger = "sharpsl-charge",
+ .brightness_set = corgiled_amber_set,
+};
+
+static struct led_classdev corgi_green_led = {
+ .name = "corgi:green",
+ .default_trigger = "nand-disk",
+ .brightness_set = corgiled_green_set,
+};
+
+#ifdef CONFIG_PM
+static int corgiled_suspend(struct platform_device *dev, pm_message_t state)
+{
+#ifdef CONFIG_LEDS_TRIGGERS
+ if (corgi_amber_led.trigger && strcmp(corgi_amber_led.trigger->name, "sharpsl-charge"))
+#endif
+ led_classdev_suspend(&corgi_amber_led);
+ led_classdev_suspend(&corgi_green_led);
+ return 0;
+}
+
+static int corgiled_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&corgi_amber_led);
+ led_classdev_resume(&corgi_green_led);
+ return 0;
+}
+#endif
+
+static int corgiled_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = led_classdev_register(&pdev->dev, &corgi_amber_led);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&pdev->dev, &corgi_green_led);
+ if (ret < 0)
+ led_classdev_unregister(&corgi_amber_led);
+
+ return ret;
+}
+
+static int corgiled_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&corgi_amber_led);
+ led_classdev_unregister(&corgi_green_led);
+ return 0;
+}
+
+static struct platform_driver corgiled_driver = {
+ .probe = corgiled_probe,
+ .remove = corgiled_remove,
+#ifdef CONFIG_PM
+ .suspend = corgiled_suspend,
+ .resume = corgiled_resume,
+#endif
+ .driver = {
+ .name = "corgi-led",
+ },
+};
+
+static int __init corgiled_init(void)
+{
+ return platform_driver_register(&corgiled_driver);
+}
+
+static void __exit corgiled_exit(void)
+{
+ platform_driver_unregister(&corgiled_driver);
+}
+
+module_init(corgiled_init);
+module_exit(corgiled_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("Corgi LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-ixp4xx-gpio.c b/drivers/leds/leds-ixp4xx-gpio.c
new file mode 100644
index 000000000000..30ced150e4cf
--- /dev/null
+++ b/drivers/leds/leds-ixp4xx-gpio.c
@@ -0,0 +1,215 @@
+/*
+ * IXP4XX GPIO driver LED driver
+ *
+ * Author: John Bowler <jbowler@acm.org>
+ *
+ * Copyright (c) 2006 John Bowler
+ *
+ * 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 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/leds.h>
+#include <asm/arch/hardware.h>
+
+extern spinlock_t gpio_lock;
+
+/* Up to 16 gpio lines are possible. */
+#define GPIO_MAX 16
+static struct ixp4xxgpioled_device {
+ struct led_classdev ancestor;
+ int flags;
+} ixp4xxgpioled_devices[GPIO_MAX];
+
+void ixp4xxgpioled_brightness_set(struct led_classdev *pled,
+ enum led_brightness value)
+{
+ const struct ixp4xxgpioled_device *const ixp4xx_dev =
+ container_of(pled, struct ixp4xxgpioled_device, ancestor);
+ const u32 gpio_pin = ixp4xx_dev - ixp4xxgpioled_devices;
+
+ if (gpio_pin < GPIO_MAX && ixp4xx_dev->ancestor.name != 0) {
+ /* Set or clear the 'gpio_pin' bit according to the style
+ * and the required setting (value > 0 == on)
+ */
+ const int gpio_value =
+ (value > 0) == (ixp4xx_dev->flags != IXP4XX_GPIO_LOW) ?
+ IXP4XX_GPIO_HIGH : IXP4XX_GPIO_LOW;
+
+ {
+ unsigned long flags;
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_line_set(gpio_pin, gpio_value);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+ }
+ }
+}
+
+/* LEDs are described in resources, the following iterates over the valid
+ * LED resources.
+ */
+#define for_all_leds(i, pdev) \
+ for (i=0; i<pdev->num_resources; ++i) \
+ if (pdev->resource[i].start < GPIO_MAX && \
+ pdev->resource[i].name != 0)
+
+/* The following applies 'operation' to each LED from the given platform,
+ * the function always returns 0 to allow tail call elimination.
+ */
+static int apply_to_all_leds(struct platform_device *pdev,
+ void (*operation)(struct led_classdev *pled))
+{
+ int i;
+
+ for_all_leds(i, pdev)
+ operation(&ixp4xxgpioled_devices[pdev->resource[i].start].ancestor);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int ixp4xxgpioled_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ return apply_to_all_leds(pdev, led_classdev_suspend);
+}
+
+static int ixp4xxgpioled_resume(struct platform_device *pdev)
+{
+ return apply_to_all_leds(pdev, led_classdev_resume);
+}
+#endif
+
+static void ixp4xxgpioled_remove_one_led(struct led_classdev *pled)
+{
+ led_classdev_unregister(pled);
+ pled->name = 0;
+}
+
+static int ixp4xxgpioled_remove(struct platform_device *pdev)
+{
+ return apply_to_all_leds(pdev, ixp4xxgpioled_remove_one_led);
+}
+
+static int ixp4xxgpioled_probe(struct platform_device *pdev)
+{
+ /* The board level has to tell the driver where the
+ * LEDs are connected - there is no way to find out
+ * electrically. It must also say whether the GPIO
+ * lines are active high or active low.
+ *
+ * To do this read the num_resources (the number of
+ * LEDs) and the struct resource (the data for each
+ * LED). The name comes from the resource, and it
+ * isn't copied.
+ */
+ int i;
+
+ for_all_leds(i, pdev) {
+ const u8 gpio_pin = pdev->resource[i].start;
+ int rc;
+
+ if (ixp4xxgpioled_devices[gpio_pin].ancestor.name == 0) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&gpio_lock, flags);
+ gpio_line_config(gpio_pin, IXP4XX_GPIO_OUT);
+ /* The config can, apparently, reset the state,
+ * I suspect the gpio line may be an input and
+ * the config may cause the line to be latched,
+ * so the setting depends on how the LED is
+ * connected to the line (which affects how it
+ * floats if not driven).
+ */
+ gpio_line_set(gpio_pin, IXP4XX_GPIO_HIGH);
+ spin_unlock_irqrestore(&gpio_lock, flags);
+
+ ixp4xxgpioled_devices[gpio_pin].flags =
+ pdev->resource[i].flags & IORESOURCE_BITS;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.name =
+ pdev->resource[i].name;
+
+ /* This is how a board manufacturer makes the LED
+ * come on on reset - the GPIO line will be high, so
+ * make the LED light when the line is low...
+ */
+ if (ixp4xxgpioled_devices[gpio_pin].flags != IXP4XX_GPIO_LOW)
+ ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 100;
+ else
+ ixp4xxgpioled_devices[gpio_pin].ancestor.brightness = 0;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.flags = 0;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.brightness_set =
+ ixp4xxgpioled_brightness_set;
+
+ ixp4xxgpioled_devices[gpio_pin].ancestor.default_trigger = 0;
+ }
+
+ rc = led_classdev_register(&pdev->dev,
+ &ixp4xxgpioled_devices[gpio_pin].ancestor);
+ if (rc < 0) {
+ ixp4xxgpioled_devices[gpio_pin].ancestor.name = 0;
+ ixp4xxgpioled_remove(pdev);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver ixp4xxgpioled_driver = {
+ .probe = ixp4xxgpioled_probe,
+ .remove = ixp4xxgpioled_remove,
+#ifdef CONFIG_PM
+ .suspend = ixp4xxgpioled_suspend,
+ .resume = ixp4xxgpioled_resume,
+#endif
+ .driver = {
+ .name = "IXP4XX-GPIO-LED",
+ },
+};
+
+static int __init ixp4xxgpioled_init(void)
+{
+ return platform_driver_register(&ixp4xxgpioled_driver);
+}
+
+static void __exit ixp4xxgpioled_exit(void)
+{
+ platform_driver_unregister(&ixp4xxgpioled_driver);
+}
+
+module_init(ixp4xxgpioled_init);
+module_exit(ixp4xxgpioled_exit);
+
+MODULE_AUTHOR("John Bowler <jbowler@acm.org>");
+MODULE_DESCRIPTION("IXP4XX GPIO LED driver");
+MODULE_LICENSE("Dual MIT/GPL");
diff --git a/drivers/leds/leds-locomo.c b/drivers/leds/leds-locomo.c
new file mode 100644
index 000000000000..749a86c2adb6
--- /dev/null
+++ b/drivers/leds/leds-locomo.c
@@ -0,0 +1,95 @@
+/*
+ * linux/drivers/leds/locomo.c
+ *
+ * Copyright (C) 2005 John Lenz <lenz@cs.wisc.edu>
+ *
+ * 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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/leds.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/locomo.h>
+
+static void locomoled_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value, int offset)
+{
+ struct locomo_dev *locomo_dev = LOCOMO_DEV(led_cdev->class_dev->dev);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ if (value)
+ locomo_writel(LOCOMO_LPT_TOFH, locomo_dev->mapbase + offset);
+ else
+ locomo_writel(LOCOMO_LPT_TOFL, locomo_dev->mapbase + offset);
+ local_irq_restore(flags);
+}
+
+static void locomoled_brightness_set0(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ locomoled_brightness_set(led_cdev, value, LOCOMO_LPT0);
+}
+
+static void locomoled_brightness_set1(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ locomoled_brightness_set(led_cdev, value, LOCOMO_LPT1);
+}
+
+static struct led_classdev locomo_led0 = {
+ .name = "locomo:amber",
+ .brightness_set = locomoled_brightness_set0,
+};
+
+static struct led_classdev locomo_led1 = {
+ .name = "locomo:green",
+ .brightness_set = locomoled_brightness_set1,
+};
+
+static int locomoled_probe(struct locomo_dev *ldev)
+{
+ int ret;
+
+ ret = led_classdev_register(&ldev->dev, &locomo_led0);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&ldev->dev, &locomo_led1);
+ if (ret < 0)
+ led_classdev_unregister(&locomo_led0);
+
+ return ret;
+}
+
+static int locomoled_remove(struct locomo_dev *dev)
+{
+ led_classdev_unregister(&locomo_led0);
+ led_classdev_unregister(&locomo_led1);
+ return 0;
+}
+
+static struct locomo_driver locomoled_driver = {
+ .drv = {
+ .name = "locomoled"
+ },
+ .devid = LOCOMO_DEVID_LED,
+ .probe = locomoled_probe,
+ .remove = locomoled_remove,
+};
+
+static int __init locomoled_init(void)
+{
+ return locomo_driver_register(&locomoled_driver);
+}
+module_init(locomoled_init);
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+MODULE_DESCRIPTION("Locomo LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-spitz.c b/drivers/leds/leds-spitz.c
new file mode 100644
index 000000000000..65bbef4a5e09
--- /dev/null
+++ b/drivers/leds/leds-spitz.c
@@ -0,0 +1,125 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2005-2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <asm/hardware/scoop.h>
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/spitz.h>
+
+static void spitzled_amber_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE);
+ else
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_ORANGE);
+}
+
+static void spitzled_green_set(struct led_classdev *led_cdev, enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN);
+ else
+ reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_LED_GREEN);
+}
+
+static struct led_classdev spitz_amber_led = {
+ .name = "spitz:amber",
+ .default_trigger = "sharpsl-charge",
+ .brightness_set = spitzled_amber_set,
+};
+
+static struct led_classdev spitz_green_led = {
+ .name = "spitz:green",
+ .default_trigger = "ide-disk",
+ .brightness_set = spitzled_green_set,
+};
+
+#ifdef CONFIG_PM
+static int spitzled_suspend(struct platform_device *dev, pm_message_t state)
+{
+#ifdef CONFIG_LEDS_TRIGGERS
+ if (spitz_amber_led.trigger && strcmp(spitz_amber_led.trigger->name, "sharpsl-charge"))
+#endif
+ led_classdev_suspend(&spitz_amber_led);
+ led_classdev_suspend(&spitz_green_led);
+ return 0;
+}
+
+static int spitzled_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&spitz_amber_led);
+ led_classdev_resume(&spitz_green_led);
+ return 0;
+}
+#endif
+
+static int spitzled_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (machine_is_akita())
+ spitz_green_led.default_trigger = "nand-disk";
+
+ ret = led_classdev_register(&pdev->dev, &spitz_amber_led);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&pdev->dev, &spitz_green_led);
+ if (ret < 0)
+ led_classdev_unregister(&spitz_amber_led);
+
+ return ret;
+}
+
+static int spitzled_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&spitz_amber_led);
+ led_classdev_unregister(&spitz_green_led);
+
+ return 0;
+}
+
+static struct platform_driver spitzled_driver = {
+ .probe = spitzled_probe,
+ .remove = spitzled_remove,
+#ifdef CONFIG_PM
+ .suspend = spitzled_suspend,
+ .resume = spitzled_resume,
+#endif
+ .driver = {
+ .name = "spitz-led",
+ },
+};
+
+static int __init spitzled_init(void)
+{
+ return platform_driver_register(&spitzled_driver);
+}
+
+static void __exit spitzled_exit(void)
+{
+ platform_driver_unregister(&spitzled_driver);
+}
+
+module_init(spitzled_init);
+module_exit(spitzled_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("Spitz LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c
new file mode 100644
index 000000000000..c9e8cc1ec481
--- /dev/null
+++ b/drivers/leds/leds-tosa.c
@@ -0,0 +1,131 @@
+/*
+ * LED Triggers Core
+ *
+ * Copyright 2005 Dirk Opfer
+ *
+ * Author: Dirk Opfer <Dirk@Opfer-Online.de>
+ * based on spitz.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.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <asm/hardware/scoop.h>
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/arch/tosa.h>
+
+static void tosaled_amber_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_CHRG_ERR_LED);
+ else
+ reset_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_CHRG_ERR_LED);
+}
+
+static void tosaled_green_set(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value)
+ set_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_NOTE_LED);
+ else
+ reset_scoop_gpio(&tosascoop_jc_device.dev,
+ TOSA_SCOOP_JC_NOTE_LED);
+}
+
+static struct led_classdev tosa_amber_led = {
+ .name = "tosa:amber",
+ .default_trigger = "sharpsl-charge",
+ .brightness_set = tosaled_amber_set,
+};
+
+static struct led_classdev tosa_green_led = {
+ .name = "tosa:green",
+ .default_trigger = "nand-disk",
+ .brightness_set = tosaled_green_set,
+};
+
+#ifdef CONFIG_PM
+static int tosaled_suspend(struct platform_device *dev, pm_message_t state)
+{
+#ifdef CONFIG_LEDS_TRIGGERS
+ if (tosa_amber_led.trigger && strcmp(tosa_amber_led.trigger->name,
+ "sharpsl-charge"))
+#endif
+ led_classdev_suspend(&tosa_amber_led);
+ led_classdev_suspend(&tosa_green_led);
+ return 0;
+}
+
+static int tosaled_resume(struct platform_device *dev)
+{
+ led_classdev_resume(&tosa_amber_led);
+ led_classdev_resume(&tosa_green_led);
+ return 0;
+}
+#else
+#define tosaled_suspend NULL
+#define tosaled_resume NULL
+#endif
+
+static int tosaled_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ ret = led_classdev_register(&pdev->dev, &tosa_amber_led);
+ if (ret < 0)
+ return ret;
+
+ ret = led_classdev_register(&pdev->dev, &tosa_green_led);
+ if (ret < 0)
+ led_classdev_unregister(&tosa_amber_led);
+
+ return ret;
+}
+
+static int tosaled_remove(struct platform_device *pdev)
+{
+ led_classdev_unregister(&tosa_amber_led);
+ led_classdev_unregister(&tosa_green_led);
+
+ return 0;
+}
+
+static struct platform_driver tosaled_driver = {
+ .probe = tosaled_probe,
+ .remove = tosaled_remove,
+ .suspend = tosaled_suspend,
+ .resume = tosaled_resume,
+ .driver = {
+ .name = "tosa-led",
+ },
+};
+
+static int __init tosaled_init(void)
+{
+ return platform_driver_register(&tosaled_driver);
+}
+
+static void __exit tosaled_exit(void)
+{
+ platform_driver_unregister(&tosaled_driver);
+}
+
+module_init(tosaled_init);
+module_exit(tosaled_exit);
+
+MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
+MODULE_DESCRIPTION("Tosa LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds.h b/drivers/leds/leds.h
new file mode 100644
index 000000000000..a715c4ed93ff
--- /dev/null
+++ b/drivers/leds/leds.h
@@ -0,0 +1,44 @@
+/*
+ * LED Core
+ *
+ * Copyright 2005 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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 __LEDS_H_INCLUDED
+#define __LEDS_H_INCLUDED
+
+#include <linux/leds.h>
+
+static inline void led_set_brightness(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ if (value > LED_FULL)
+ value = LED_FULL;
+ led_cdev->brightness = value;
+ if (!(led_cdev->flags & LED_SUSPENDED))
+ led_cdev->brightness_set(led_cdev, value);
+}
+
+extern rwlock_t leds_list_lock;
+extern struct list_head leds_list;
+
+#ifdef CONFIG_LEDS_TRIGGERS
+void led_trigger_set_default(struct led_classdev *led_cdev);
+void led_trigger_set(struct led_classdev *led_cdev,
+ struct led_trigger *trigger);
+#else
+#define led_trigger_set_default(x) do {} while(0)
+#define led_trigger_set(x, y) do {} while(0)
+#endif
+
+ssize_t led_trigger_store(struct class_device *dev, const char *buf,
+ size_t count);
+ssize_t led_trigger_show(struct class_device *dev, char *buf);
+
+#endif /* __LEDS_H_INCLUDED */
diff --git a/drivers/leds/ledtrig-ide-disk.c b/drivers/leds/ledtrig-ide-disk.c
new file mode 100644
index 000000000000..fa651886ab4f
--- /dev/null
+++ b/drivers/leds/ledtrig-ide-disk.c
@@ -0,0 +1,62 @@
+/*
+ * LED IDE-Disk Activity Trigger
+ *
+ * Copyright 2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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/kernel.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+
+static void ledtrig_ide_timerfunc(unsigned long data);
+
+DEFINE_LED_TRIGGER(ledtrig_ide);
+static DEFINE_TIMER(ledtrig_ide_timer, ledtrig_ide_timerfunc, 0, 0);
+static int ide_activity;
+static int ide_lastactivity;
+
+void ledtrig_ide_activity(void)
+{
+ ide_activity++;
+ if (!timer_pending(&ledtrig_ide_timer))
+ mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
+}
+EXPORT_SYMBOL(ledtrig_ide_activity);
+
+static void ledtrig_ide_timerfunc(unsigned long data)
+{
+ if (ide_lastactivity != ide_activity) {
+ ide_lastactivity = ide_activity;
+ led_trigger_event(ledtrig_ide, LED_FULL);
+ mod_timer(&ledtrig_ide_timer, jiffies + msecs_to_jiffies(10));
+ } else {
+ led_trigger_event(ledtrig_ide, LED_OFF);
+ }
+}
+
+static int __init ledtrig_ide_init(void)
+{
+ led_trigger_register_simple("ide-disk", &ledtrig_ide);
+ return 0;
+}
+
+static void __exit ledtrig_ide_exit(void)
+{
+ led_trigger_unregister_simple(ledtrig_ide);
+}
+
+module_init(ledtrig_ide_init);
+module_exit(ledtrig_ide_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("LED IDE Disk Activity Trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/ledtrig-timer.c b/drivers/leds/ledtrig-timer.c
new file mode 100644
index 000000000000..f484b5d6dbf8
--- /dev/null
+++ b/drivers/leds/ledtrig-timer.c
@@ -0,0 +1,170 @@
+/*
+ * LED Kernel Timer Trigger
+ *
+ * Copyright 2005-2006 Openedhand Ltd.
+ *
+ * Author: Richard Purdie <rpurdie@openedhand.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/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/timer.h>
+#include <linux/leds.h>
+#include "leds.h"
+
+struct timer_trig_data {
+ unsigned long delay_on; /* milliseconds on */
+ unsigned long delay_off; /* milliseconds off */
+ struct timer_list timer;
+};
+
+static void led_timer_function(unsigned long data)
+{
+ struct led_classdev *led_cdev = (struct led_classdev *) data;
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ unsigned long brightness = LED_OFF;
+ unsigned long delay = timer_data->delay_off;
+
+ if (!timer_data->delay_on || !timer_data->delay_off) {
+ led_set_brightness(led_cdev, LED_OFF);
+ return;
+ }
+
+ if (!led_cdev->brightness) {
+ brightness = LED_FULL;
+ delay = timer_data->delay_on;
+ }
+
+ led_set_brightness(led_cdev, brightness);
+
+ mod_timer(&timer_data->timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static ssize_t led_delay_on_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ sprintf(buf, "%lu\n", timer_data->delay_on);
+
+ return strlen(buf) + 1;
+}
+
+static ssize_t led_delay_on_store(struct class_device *dev, const char *buf,
+ size_t size)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ int ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+
+ if (after - buf > 0) {
+ timer_data->delay_on = state;
+ mod_timer(&timer_data->timer, jiffies + 1);
+ ret = after - buf;
+ }
+
+ return ret;
+}
+
+static ssize_t led_delay_off_show(struct class_device *dev, char *buf)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ sprintf(buf, "%lu\n", timer_data->delay_off);
+
+ return strlen(buf) + 1;
+}
+
+static ssize_t led_delay_off_store(struct class_device *dev, const char *buf,
+ size_t size)
+{
+ struct led_classdev *led_cdev = class_get_devdata(dev);
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+ int ret = -EINVAL;
+ char *after;
+ unsigned long state = simple_strtoul(buf, &after, 10);
+
+ if (after - buf > 0) {
+ timer_data->delay_off = state;
+ mod_timer(&timer_data->timer, jiffies + 1);
+ ret = after - buf;
+ }
+
+ return ret;
+}
+
+static CLASS_DEVICE_ATTR(delay_on, 0644, led_delay_on_show,
+ led_delay_on_store);
+static CLASS_DEVICE_ATTR(delay_off, 0644, led_delay_off_show,
+ led_delay_off_store);
+
+static void timer_trig_activate(struct led_classdev *led_cdev)
+{
+ struct timer_trig_data *timer_data;
+
+ timer_data = kzalloc(sizeof(struct timer_trig_data), GFP_KERNEL);
+ if (!timer_data)
+ return;
+
+ led_cdev->trigger_data = timer_data;
+
+ init_timer(&timer_data->timer);
+ timer_data->timer.function = led_timer_function;
+ timer_data->timer.data = (unsigned long) led_cdev;
+
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_delay_on);
+ class_device_create_file(led_cdev->class_dev,
+ &class_device_attr_delay_off);
+}
+
+static void timer_trig_deactivate(struct led_classdev *led_cdev)
+{
+ struct timer_trig_data *timer_data = led_cdev->trigger_data;
+
+ if (timer_data) {
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_delay_on);
+ class_device_remove_file(led_cdev->class_dev,
+ &class_device_attr_delay_off);
+ del_timer_sync(&timer_data->timer);
+ kfree(timer_data);
+ }
+}
+
+static struct led_trigger timer_led_trigger = {
+ .name = "timer",
+ .activate = timer_trig_activate,
+ .deactivate = timer_trig_deactivate,
+};
+
+static int __init timer_trig_init(void)
+{
+ return led_trigger_register(&timer_led_trigger);
+}
+
+static void __exit timer_trig_exit(void)
+{
+ led_trigger_unregister(&timer_led_trigger);
+}
+
+module_init(timer_trig_init);
+module_exit(timer_trig_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>");
+MODULE_DESCRIPTION("Timer LED trigger");
+MODULE_LICENSE("GPL");
diff --git a/drivers/macintosh/adb.c b/drivers/macintosh/adb.c
index 34fcabac5fdb..259fd8973ce9 100644
--- a/drivers/macintosh/adb.c
+++ b/drivers/macintosh/adb.c
@@ -42,6 +42,7 @@
#include <asm/semaphore.h>
#ifdef CONFIG_PPC
#include <asm/prom.h>
+#include <asm/machdep.h>
#endif
@@ -294,7 +295,7 @@ int __init adb_init(void)
int i;
#ifdef CONFIG_PPC32
- if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
+ if (!machine_is(chrp) && !machine_is(powermac))
return 0;
#endif
#ifdef CONFIG_MAC
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
index f5779a73184d..394334ec5765 100644
--- a/drivers/macintosh/adbhid.c
+++ b/drivers/macintosh/adbhid.c
@@ -1206,8 +1206,8 @@ init_ms_a3(int id)
static int __init adbhid_init(void)
{
#ifndef CONFIG_MAC
- if ( (_machine != _MACH_chrp) && (_machine != _MACH_Pmac) )
- return 0;
+ if (!machine_is(chrp) && !machine_is(powermac))
+ return 0;
#endif
led_request.complete = 1;
diff --git a/drivers/macintosh/mediabay.c b/drivers/macintosh/mediabay.c
index 8dbf2852bae0..53c1c7909413 100644
--- a/drivers/macintosh/mediabay.c
+++ b/drivers/macintosh/mediabay.c
@@ -839,8 +839,8 @@ static int __init media_bay_init(void)
media_bays[i].cd_index = -1;
#endif
}
- if (_machine != _MACH_Pmac)
- return -ENODEV;
+ if (!machine_is(powermac))
+ return 0;
macio_register_driver(&media_bay_driver);
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 039e071c1007..1ed5152db450 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -215,13 +215,11 @@ static void mddev_put(mddev_t *mddev)
return;
if (!mddev->raid_disks && list_empty(&mddev->disks)) {
list_del(&mddev->all_mddevs);
- /* that blocks */
+ spin_unlock(&all_mddevs_lock);
blk_cleanup_queue(mddev->queue);
- /* that also blocks */
kobject_unregister(&mddev->kobj);
- /* result blows... */
- }
- spin_unlock(&all_mddevs_lock);
+ } else
+ spin_unlock(&all_mddevs_lock);
}
static mddev_t * mddev_find(dev_t unit)
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 3cb0872a845d..9b374c91db66 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -1135,8 +1135,19 @@ static int end_sync_write(struct bio *bio, unsigned int bytes_done, int error)
mirror = i;
break;
}
- if (!uptodate)
+ if (!uptodate) {
+ int sync_blocks = 0;
+ sector_t s = r1_bio->sector;
+ long sectors_to_go = r1_bio->sectors;
+ /* make sure these bits doesn't get cleared. */
+ do {
+ bitmap_end_sync(mddev->bitmap, r1_bio->sector,
+ &sync_blocks, 1);
+ 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);
diff --git a/drivers/md/raid6main.c b/drivers/md/raid6main.c
index 6df4930fddec..ab64b37e4996 100644
--- a/drivers/md/raid6main.c
+++ b/drivers/md/raid6main.c
@@ -2151,6 +2151,8 @@ static int run(mddev_t *mddev)
}
/* Ok, everything is just fine now */
+ sysfs_create_group(&mddev->kobj, &raid6_attrs_group);
+
mddev->array_size = mddev->size * (mddev->raid_disks - 2);
mddev->queue->unplug_fn = raid6_unplug_device;
diff --git a/drivers/media/video/cpia_pp.c b/drivers/media/video/cpia_pp.c
index 3021f21aae36..0b00e6027dfb 100644
--- a/drivers/media/video/cpia_pp.c
+++ b/drivers/media/video/cpia_pp.c
@@ -873,7 +873,7 @@ static int __init cpia_pp_setup(char *str)
parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE;
}
- return 0;
+ return 1;
}
__setup("cpia_pp=", cpia_pp_setup);
diff --git a/drivers/media/video/planb.c b/drivers/media/video/planb.c
index 522e9ddeb089..d9e3cada52f4 100644
--- a/drivers/media/video/planb.c
+++ b/drivers/media/video/planb.c
@@ -2156,7 +2156,7 @@ static int find_planb(void)
struct pci_dev *pdev;
int rc;
- if (_machine != _MACH_Pmac)
+ if (!machine_is(powermac))
return 0;
planb_devices = find_devices("planb");
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 3f5d77f633fa..7cc162e8978b 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -60,6 +60,17 @@ config MMC_SDHCI
If unsure, say N.
+config MMC_OMAP
+ tristate "TI OMAP Multimedia Card Interface support"
+ depends on ARCH_OMAP && MMC
+ select TPS65010 if MACH_OMAP_H2
+ help
+ This selects the TI OMAP Multimedia card Interface.
+ If you have an OMAP board with a Multimedia Card slot,
+ say Y or M here.
+
+ If unsure, say N.
+
config MMC_WBSD
tristate "Winbond W83L51xD SD/MMC Card Interface support"
depends on MMC && ISA_DMA_API
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 769d545284a4..c7c34aadfc92 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -20,5 +20,10 @@ obj-$(CONFIG_MMC_PXA) += pxamci.o
obj-$(CONFIG_MMC_SDHCI) += sdhci.o
obj-$(CONFIG_MMC_WBSD) += wbsd.o
obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
+obj-$(CONFIG_MMC_OMAP) += omap.o
mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
+
+ifeq ($(CONFIG_MMC_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c
index 85e89c77bdea..c0326bbc5f28 100644
--- a/drivers/mmc/au1xmmc.c
+++ b/drivers/mmc/au1xmmc.c
@@ -56,12 +56,11 @@
#define DRIVER_NAME "au1xxx-mmc"
/* Set this to enable special debugging macros */
-/* #define MMC_DEBUG */
-#ifdef MMC_DEBUG
-#define DEBUG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args)
+#ifdef DEBUG
+#define DBG(fmt, idx, args...) printk("au1xx(%d): DEBUG: " fmt, idx, ##args)
#else
-#define DEBUG(fmt, idx, args...)
+#define DBG(fmt, idx, args...)
#endif
const struct {
@@ -424,18 +423,18 @@ static void au1xmmc_receive_pio(struct au1xmmc_host *host)
break;
if (status & SD_STATUS_RC) {
- DEBUG("RX CRC Error [%d + %d].\n", host->id,
+ DBG("RX CRC Error [%d + %d].\n", host->id,
host->pio.len, count);
break;
}
if (status & SD_STATUS_RO) {
- DEBUG("RX Overrun [%d + %d]\n", host->id,
+ DBG("RX Overrun [%d + %d]\n", host->id,
host->pio.len, count);
break;
}
else if (status & SD_STATUS_RU) {
- DEBUG("RX Underrun [%d + %d]\n", host->id,
+ DBG("RX Underrun [%d + %d]\n", host->id,
host->pio.len, count);
break;
}
@@ -721,7 +720,7 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
{
struct au1xmmc_host *host = mmc_priv(mmc);
- DEBUG("set_ios (power=%u, clock=%uHz, vdd=%u, mode=%u)\n",
+ DBG("set_ios (power=%u, clock=%uHz, vdd=%u, mode=%u)\n",
host->id, ios->power_mode, ios->clock, ios->vdd,
ios->bus_mode);
@@ -810,7 +809,7 @@ static irqreturn_t au1xmmc_irq(int irq, void *dev_id, struct pt_regs *regs)
au1xmmc_receive_pio(host);
}
else if (status & 0x203FBC70) {
- DEBUG("Unhandled status %8.8x\n", host->id, status);
+ DBG("Unhandled status %8.8x\n", host->id, status);
handled = 0;
}
@@ -839,7 +838,7 @@ static void au1xmmc_poll_event(unsigned long arg)
if (host->mrq != NULL) {
u32 status = au_readl(HOST_STATUS(host));
- DEBUG("PENDING - %8.8x\n", host->id, status);
+ DBG("PENDING - %8.8x\n", host->id, status);
}
mod_timer(&host->timer, jiffies + AU1XMMC_DETECT_TIMEOUT);
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 1888060c5e0c..da6ddd910fc5 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -27,12 +27,6 @@
#include "mmc.h"
-#ifdef CONFIG_MMC_DEBUG
-#define DBG(x...) printk(KERN_DEBUG x)
-#else
-#define DBG(x...) do { } while (0)
-#endif
-
#define CMD_RETRIES 3
/*
@@ -77,8 +71,9 @@ void mmc_request_done(struct mmc_host *host, struct mmc_request *mrq)
{
struct mmc_command *cmd = mrq->cmd;
int err = mrq->cmd->error;
- DBG("MMC: req done (%02x): %d: %08x %08x %08x %08x\n", cmd->opcode,
- err, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]);
+ pr_debug("MMC: req done (%02x): %d: %08x %08x %08x %08x\n",
+ cmd->opcode, err, cmd->resp[0], cmd->resp[1],
+ cmd->resp[2], cmd->resp[3]);
if (err && cmd->retries) {
cmd->retries--;
@@ -102,8 +97,8 @@ EXPORT_SYMBOL(mmc_request_done);
void
mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
- DBG("MMC: starting cmd %02x arg %08x flags %08x\n",
- mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags);
+ pr_debug("MMC: starting cmd %02x arg %08x flags %08x\n",
+ mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags);
WARN_ON(host->card_busy == NULL);
@@ -976,8 +971,8 @@ static unsigned int mmc_calculate_clock(struct mmc_host *host)
if (!mmc_card_dead(card) && max_dtr > card->csd.max_dtr)
max_dtr = card->csd.max_dtr;
- DBG("MMC: selected %d.%03dMHz transfer rate\n",
- max_dtr / 1000000, (max_dtr / 1000) % 1000);
+ pr_debug("MMC: selected %d.%03dMHz transfer rate\n",
+ max_dtr / 1000000, (max_dtr / 1000) % 1000);
return max_dtr;
}
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index 9fef29d978b5..df7e861e2fc7 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -33,12 +33,8 @@
#define DRIVER_NAME "mmci-pl18x"
-#ifdef CONFIG_MMC_DEBUG
#define DBG(host,fmt,args...) \
pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args)
-#else
-#define DBG(host,fmt,args...) do { } while (0)
-#endif
static unsigned int fmax = 515633;
diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c
new file mode 100644
index 000000000000..becb3c68c34d
--- /dev/null
+++ b/drivers/mmc/omap.c
@@ -0,0 +1,1226 @@
+/*
+ * linux/drivers/media/mmc/omap.c
+ *
+ * Copyright (C) 2004 Nokia Corporation
+ * Written by Tuukka Tikkanen and Juha Yrjölä<juha.yrjola@nokia.com>
+ * Misc hacks here and there by Tony Lindgren <tony@atomide.com>
+ * Other hacks (DMA, SD, etc) by David Brownell
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/protocol.h>
+#include <linux/mmc/card.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/scatterlist.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/board.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/dma.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/fpga.h>
+#include <asm/arch/tps65010.h>
+
+#include "omap.h"
+
+#define DRIVER_NAME "mmci-omap"
+#define RSP_TYPE(x) ((x) & ~(MMC_RSP_BUSY|MMC_RSP_OPCODE))
+
+/* Specifies how often in millisecs to poll for card status changes
+ * when the cover switch is open */
+#define OMAP_MMC_SWITCH_POLL_DELAY 500
+
+static int mmc_omap_enable_poll = 1;
+
+struct mmc_omap_host {
+ int initialized;
+ int suspended;
+ struct mmc_request * mrq;
+ struct mmc_command * cmd;
+ struct mmc_data * data;
+ struct mmc_host * mmc;
+ struct device * dev;
+ unsigned char id; /* 16xx chips have 2 MMC blocks */
+ struct clk * iclk;
+ struct clk * fclk;
+ void __iomem *base;
+ int irq;
+ unsigned char bus_mode;
+ unsigned char hw_bus_mode;
+
+ unsigned int sg_len;
+ int sg_idx;
+ u16 * buffer;
+ u32 buffer_bytes_left;
+ u32 total_bytes_left;
+
+ unsigned use_dma:1;
+ unsigned brs_received:1, dma_done:1;
+ unsigned dma_is_read:1;
+ unsigned dma_in_use:1;
+ int dma_ch;
+ spinlock_t dma_lock;
+ struct timer_list dma_timer;
+ unsigned dma_len;
+
+ short power_pin;
+ short wp_pin;
+
+ int switch_pin;
+ struct work_struct switch_work;
+ struct timer_list switch_timer;
+ int switch_last_state;
+};
+
+static inline int
+mmc_omap_cover_is_open(struct mmc_omap_host *host)
+{
+ if (host->switch_pin < 0)
+ return 0;
+ return omap_get_gpio_datain(host->switch_pin);
+}
+
+static ssize_t
+mmc_omap_show_cover_switch(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mmc_omap_host *host = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" :
+ "closed");
+}
+
+static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL);
+
+static ssize_t
+mmc_omap_show_enable_poll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll);
+}
+
+static ssize_t
+mmc_omap_store_enable_poll(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t size)
+{
+ int enable_poll;
+
+ if (sscanf(buf, "%10d", &enable_poll) != 1)
+ return -EINVAL;
+
+ if (enable_poll != mmc_omap_enable_poll) {
+ struct mmc_omap_host *host = dev_get_drvdata(dev);
+
+ mmc_omap_enable_poll = enable_poll;
+ if (enable_poll && host->switch_pin >= 0)
+ schedule_work(&host->switch_work);
+ }
+ return size;
+}
+
+static DEVICE_ATTR(enable_poll, 0664,
+ mmc_omap_show_enable_poll, mmc_omap_store_enable_poll);
+
+static void
+mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+ u32 cmdreg;
+ u32 resptype;
+ u32 cmdtype;
+
+ host->cmd = cmd;
+
+ resptype = 0;
+ cmdtype = 0;
+
+ /* Our hardware needs to know exact type */
+ switch (RSP_TYPE(mmc_resp_type(cmd))) {
+ case RSP_TYPE(MMC_RSP_R1):
+ /* resp 1, resp 1b */
+ resptype = 1;
+ break;
+ case RSP_TYPE(MMC_RSP_R2):
+ resptype = 2;
+ break;
+ case RSP_TYPE(MMC_RSP_R3):
+ resptype = 3;
+ break;
+ default:
+ break;
+ }
+
+ if (mmc_cmd_type(cmd) == MMC_CMD_ADTC) {
+ cmdtype = OMAP_MMC_CMDTYPE_ADTC;
+ } else if (mmc_cmd_type(cmd) == MMC_CMD_BC) {
+ cmdtype = OMAP_MMC_CMDTYPE_BC;
+ } else if (mmc_cmd_type(cmd) == MMC_CMD_BCR) {
+ cmdtype = OMAP_MMC_CMDTYPE_BCR;
+ } else {
+ cmdtype = OMAP_MMC_CMDTYPE_AC;
+ }
+
+ cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12);
+
+ if (host->bus_mode == MMC_BUSMODE_OPENDRAIN)
+ cmdreg |= 1 << 6;
+
+ if (cmd->flags & MMC_RSP_BUSY)
+ cmdreg |= 1 << 11;
+
+ if (host->data && !(host->data->flags & MMC_DATA_WRITE))
+ cmdreg |= 1 << 15;
+
+ clk_enable(host->fclk);
+
+ OMAP_MMC_WRITE(host->base, CTO, 200);
+ OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff);
+ OMAP_MMC_WRITE(host->base, ARGH, cmd->arg >> 16);
+ OMAP_MMC_WRITE(host->base, IE,
+ OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL |
+ OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT |
+ OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT |
+ OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR |
+ OMAP_MMC_STAT_END_OF_DATA);
+ OMAP_MMC_WRITE(host->base, CMD, cmdreg);
+}
+
+static void
+mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ if (host->dma_in_use) {
+ enum dma_data_direction dma_data_dir;
+
+ BUG_ON(host->dma_ch < 0);
+ if (data->error != MMC_ERR_NONE)
+ omap_stop_dma(host->dma_ch);
+ /* Release DMA channel lazily */
+ mod_timer(&host->dma_timer, jiffies + HZ);
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+ dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len,
+ dma_data_dir);
+ }
+ host->data = NULL;
+ host->sg_len = 0;
+ clk_disable(host->fclk);
+
+ /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing
+ * dozens of requests until the card finishes writing data.
+ * It'd be cheaper to just wait till an EOFB interrupt arrives...
+ */
+
+ if (!data->stop) {
+ host->mrq = NULL;
+ mmc_request_done(host->mmc, data->mrq);
+ return;
+ }
+
+ mmc_omap_start_command(host, data->stop);
+}
+
+static void
+mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ unsigned long flags;
+ int done;
+
+ if (!host->dma_in_use) {
+ mmc_omap_xfer_done(host, data);
+ return;
+ }
+ done = 0;
+ spin_lock_irqsave(&host->dma_lock, flags);
+ if (host->dma_done)
+ done = 1;
+ else
+ host->brs_received = 1;
+ spin_unlock_irqrestore(&host->dma_lock, flags);
+ if (done)
+ mmc_omap_xfer_done(host, data);
+}
+
+static void
+mmc_omap_dma_timer(unsigned long data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+
+ BUG_ON(host->dma_ch < 0);
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+}
+
+static void
+mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ unsigned long flags;
+ int done;
+
+ done = 0;
+ spin_lock_irqsave(&host->dma_lock, flags);
+ if (host->brs_received)
+ done = 1;
+ else
+ host->dma_done = 1;
+ spin_unlock_irqrestore(&host->dma_lock, flags);
+ if (done)
+ mmc_omap_xfer_done(host, data);
+}
+
+static void
+mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd)
+{
+ host->cmd = NULL;
+
+ if (cmd->flags & MMC_RSP_PRESENT) {
+ if (cmd->flags & MMC_RSP_136) {
+ /* response type 2 */
+ cmd->resp[3] =
+ OMAP_MMC_READ(host->base, RSP0) |
+ (OMAP_MMC_READ(host->base, RSP1) << 16);
+ cmd->resp[2] =
+ OMAP_MMC_READ(host->base, RSP2) |
+ (OMAP_MMC_READ(host->base, RSP3) << 16);
+ cmd->resp[1] =
+ OMAP_MMC_READ(host->base, RSP4) |
+ (OMAP_MMC_READ(host->base, RSP5) << 16);
+ cmd->resp[0] =
+ OMAP_MMC_READ(host->base, RSP6) |
+ (OMAP_MMC_READ(host->base, RSP7) << 16);
+ } else {
+ /* response types 1, 1b, 3, 4, 5, 6 */
+ cmd->resp[0] =
+ OMAP_MMC_READ(host->base, RSP6) |
+ (OMAP_MMC_READ(host->base, RSP7) << 16);
+ }
+ }
+
+ if (host->data == NULL || cmd->error != MMC_ERR_NONE) {
+ host->mrq = NULL;
+ clk_disable(host->fclk);
+ mmc_request_done(host->mmc, cmd->mrq);
+ }
+}
+
+/* PIO only */
+static void
+mmc_omap_sg_to_buf(struct mmc_omap_host *host)
+{
+ struct scatterlist *sg;
+
+ sg = host->data->sg + host->sg_idx;
+ host->buffer_bytes_left = sg->length;
+ host->buffer = page_address(sg->page) + sg->offset;
+ if (host->buffer_bytes_left > host->total_bytes_left)
+ host->buffer_bytes_left = host->total_bytes_left;
+}
+
+/* PIO only */
+static void
+mmc_omap_xfer_data(struct mmc_omap_host *host, int write)
+{
+ int n;
+ void __iomem *reg;
+ u16 *p;
+
+ if (host->buffer_bytes_left == 0) {
+ host->sg_idx++;
+ BUG_ON(host->sg_idx == host->sg_len);
+ mmc_omap_sg_to_buf(host);
+ }
+ n = 64;
+ if (n > host->buffer_bytes_left)
+ n = host->buffer_bytes_left;
+ host->buffer_bytes_left -= n;
+ host->total_bytes_left -= n;
+ host->data->bytes_xfered += n;
+
+ if (write) {
+ __raw_writesw(host->base + OMAP_MMC_REG_DATA, host->buffer, n);
+ } else {
+ __raw_readsw(host->base + OMAP_MMC_REG_DATA, host->buffer, n);
+ }
+}
+
+static inline void mmc_omap_report_irq(u16 status)
+{
+ static const char *mmc_omap_status_bits[] = {
+ "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO",
+ "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR"
+ };
+ int i, c = 0;
+
+ for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++)
+ if (status & (1 << i)) {
+ if (c)
+ printk(" ");
+ printk("%s", mmc_omap_status_bits[i]);
+ c++;
+ }
+}
+
+static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id;
+ u16 status;
+ int end_command;
+ int end_transfer;
+ int transfer_error;
+
+ if (host->cmd == NULL && host->data == NULL) {
+ status = OMAP_MMC_READ(host->base, STAT);
+ dev_info(mmc_dev(host->mmc),"spurious irq 0x%04x\n", status);
+ if (status != 0) {
+ OMAP_MMC_WRITE(host->base, STAT, status);
+ OMAP_MMC_WRITE(host->base, IE, 0);
+ }
+ return IRQ_HANDLED;
+ }
+
+ end_command = 0;
+ end_transfer = 0;
+ transfer_error = 0;
+
+ while ((status = OMAP_MMC_READ(host->base, STAT)) != 0) {
+ OMAP_MMC_WRITE(host->base, STAT, status);
+#ifdef CONFIG_MMC_DEBUG
+ dev_dbg(mmc_dev(host->mmc), "MMC IRQ %04x (CMD %d): ",
+ status, host->cmd != NULL ? host->cmd->opcode : -1);
+ mmc_omap_report_irq(status);
+ printk("\n");
+#endif
+ if (host->total_bytes_left) {
+ if ((status & OMAP_MMC_STAT_A_FULL) ||
+ (status & OMAP_MMC_STAT_END_OF_DATA))
+ mmc_omap_xfer_data(host, 0);
+ if (status & OMAP_MMC_STAT_A_EMPTY)
+ mmc_omap_xfer_data(host, 1);
+ }
+
+ if (status & OMAP_MMC_STAT_END_OF_DATA) {
+ end_transfer = 1;
+ }
+
+ if (status & OMAP_MMC_STAT_DATA_TOUT) {
+ dev_dbg(mmc_dev(host->mmc), "data timeout\n");
+ if (host->data) {
+ host->data->error |= MMC_ERR_TIMEOUT;
+ transfer_error = 1;
+ }
+ }
+
+ if (status & OMAP_MMC_STAT_DATA_CRC) {
+ if (host->data) {
+ host->data->error |= MMC_ERR_BADCRC;
+ dev_dbg(mmc_dev(host->mmc),
+ "data CRC error, bytes left %d\n",
+ host->total_bytes_left);
+ transfer_error = 1;
+ } else {
+ dev_dbg(mmc_dev(host->mmc), "data CRC error\n");
+ }
+ }
+
+ if (status & OMAP_MMC_STAT_CMD_TOUT) {
+ /* Timeouts are routine with some commands */
+ if (host->cmd) {
+ if (host->cmd->opcode != MMC_ALL_SEND_CID &&
+ host->cmd->opcode !=
+ MMC_SEND_OP_COND &&
+ host->cmd->opcode !=
+ MMC_APP_CMD &&
+ !mmc_omap_cover_is_open(host))
+ dev_err(mmc_dev(host->mmc),
+ "command timeout, CMD %d\n",
+ host->cmd->opcode);
+ host->cmd->error = MMC_ERR_TIMEOUT;
+ end_command = 1;
+ }
+ }
+
+ if (status & OMAP_MMC_STAT_CMD_CRC) {
+ if (host->cmd) {
+ dev_err(mmc_dev(host->mmc),
+ "command CRC error (CMD%d, arg 0x%08x)\n",
+ host->cmd->opcode, host->cmd->arg);
+ host->cmd->error = MMC_ERR_BADCRC;
+ end_command = 1;
+ } else
+ dev_err(mmc_dev(host->mmc),
+ "command CRC error without cmd?\n");
+ }
+
+ if (status & OMAP_MMC_STAT_CARD_ERR) {
+ if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) {
+ u32 response = OMAP_MMC_READ(host->base, RSP6)
+ | (OMAP_MMC_READ(host->base, RSP7) << 16);
+ /* STOP sometimes sets must-ignore bits */
+ if (!(response & (R1_CC_ERROR
+ | R1_ILLEGAL_COMMAND
+ | R1_COM_CRC_ERROR))) {
+ end_command = 1;
+ continue;
+ }
+ }
+
+ dev_dbg(mmc_dev(host->mmc), "card status error (CMD%d)\n",
+ host->cmd->opcode);
+ if (host->cmd) {
+ host->cmd->error = MMC_ERR_FAILED;
+ end_command = 1;
+ }
+ if (host->data) {
+ host->data->error = MMC_ERR_FAILED;
+ transfer_error = 1;
+ }
+ }
+
+ /*
+ * NOTE: On 1610 the END_OF_CMD may come too early when
+ * starting a write
+ */
+ if ((status & OMAP_MMC_STAT_END_OF_CMD) &&
+ (!(status & OMAP_MMC_STAT_A_EMPTY))) {
+ end_command = 1;
+ }
+ }
+
+ if (end_command) {
+ mmc_omap_cmd_done(host, host->cmd);
+ }
+ if (transfer_error)
+ mmc_omap_xfer_done(host, host->data);
+ else if (end_transfer)
+ mmc_omap_end_of_data(host, host->data);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id;
+
+ schedule_work(&host->switch_work);
+
+ return IRQ_HANDLED;
+}
+
+static void mmc_omap_switch_timer(unsigned long arg)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) arg;
+
+ schedule_work(&host->switch_work);
+}
+
+/* FIXME: Handle card insertion and removal properly. Maybe use a mask
+ * for MMC state? */
+static void mmc_omap_switch_callback(unsigned long data, u8 mmc_mask)
+{
+}
+
+static void mmc_omap_switch_handler(void *data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+ struct mmc_card *card;
+ static int complained = 0;
+ int cards = 0, cover_open;
+
+ if (host->switch_pin == -1)
+ return;
+ cover_open = mmc_omap_cover_is_open(host);
+ if (cover_open != host->switch_last_state) {
+ kobject_uevent(&host->dev->kobj, KOBJ_CHANGE);
+ host->switch_last_state = cover_open;
+ }
+ mmc_detect_change(host->mmc, 0);
+ list_for_each_entry(card, &host->mmc->cards, node) {
+ if (mmc_card_present(card))
+ cards++;
+ }
+ if (mmc_omap_cover_is_open(host)) {
+ if (!complained) {
+ dev_info(mmc_dev(host->mmc), "cover is open");
+ complained = 1;
+ }
+ if (mmc_omap_enable_poll)
+ mod_timer(&host->switch_timer, jiffies +
+ msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY));
+ } else {
+ complained = 0;
+ }
+}
+
+/* Prepare to transfer the next segment of a scatterlist */
+static void
+mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ int dma_ch = host->dma_ch;
+ unsigned long data_addr;
+ u16 buf, frame;
+ u32 count;
+ struct scatterlist *sg = &data->sg[host->sg_idx];
+ int src_port = 0;
+ int dst_port = 0;
+ int sync_dev = 0;
+
+ data_addr = io_v2p((u32) host->base) + OMAP_MMC_REG_DATA;
+ frame = 1 << data->blksz_bits;
+ count = sg_dma_len(sg);
+
+ if ((data->blocks == 1) && (count > (1 << data->blksz_bits)))
+ count = frame;
+
+ host->dma_len = count;
+
+ /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx.
+ * Use 16 or 32 word frames when the blocksize is at least that large.
+ * Blocksize is usually 512 bytes; but not for some SD reads.
+ */
+ if (cpu_is_omap15xx() && frame > 32)
+ frame = 32;
+ else if (frame > 64)
+ frame = 64;
+ count /= frame;
+ frame >>= 1;
+
+ if (!(data->flags & MMC_DATA_WRITE)) {
+ buf = 0x800f | ((frame - 1) << 8);
+
+ if (cpu_class_is_omap1()) {
+ src_port = OMAP_DMA_PORT_TIPB;
+ dst_port = OMAP_DMA_PORT_EMIFF;
+ }
+ if (cpu_is_omap24xx())
+ sync_dev = OMAP24XX_DMA_MMC1_RX;
+
+ omap_set_dma_src_params(dma_ch, src_port,
+ OMAP_DMA_AMODE_CONSTANT,
+ data_addr, 0, 0);
+ omap_set_dma_dest_params(dma_ch, dst_port,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sg), 0, 0);
+ omap_set_dma_dest_data_pack(dma_ch, 1);
+ omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ } else {
+ buf = 0x0f80 | ((frame - 1) << 0);
+
+ if (cpu_class_is_omap1()) {
+ src_port = OMAP_DMA_PORT_EMIFF;
+ dst_port = OMAP_DMA_PORT_TIPB;
+ }
+ if (cpu_is_omap24xx())
+ sync_dev = OMAP24XX_DMA_MMC1_TX;
+
+ omap_set_dma_dest_params(dma_ch, dst_port,
+ OMAP_DMA_AMODE_CONSTANT,
+ data_addr, 0, 0);
+ omap_set_dma_src_params(dma_ch, src_port,
+ OMAP_DMA_AMODE_POST_INC,
+ sg_dma_address(sg), 0, 0);
+ omap_set_dma_src_data_pack(dma_ch, 1);
+ omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4);
+ }
+
+ /* Max limit for DMA frame count is 0xffff */
+ if (unlikely(count > 0xffff))
+ BUG();
+
+ OMAP_MMC_WRITE(host->base, BUF, buf);
+ omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16,
+ frame, count, OMAP_DMA_SYNC_FRAME,
+ sync_dev, 0);
+}
+
+/* A scatterlist segment completed */
+static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data)
+{
+ struct mmc_omap_host *host = (struct mmc_omap_host *) data;
+ struct mmc_data *mmcdat = host->data;
+
+ if (unlikely(host->dma_ch < 0)) {
+ dev_err(mmc_dev(host->mmc), "DMA callback while DMA not
+ enabled\n");
+ return;
+ }
+ /* FIXME: We really should do something to _handle_ the errors */
+ if (ch_status & OMAP_DMA_TOUT_IRQ) {
+ dev_err(mmc_dev(host->mmc),"DMA timeout\n");
+ return;
+ }
+ if (ch_status & OMAP_DMA_DROP_IRQ) {
+ dev_err(mmc_dev(host->mmc), "DMA sync error\n");
+ return;
+ }
+ if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) {
+ return;
+ }
+ mmcdat->bytes_xfered += host->dma_len;
+ host->sg_idx++;
+ if (host->sg_idx < host->sg_len) {
+ mmc_omap_prepare_dma(host, host->data);
+ omap_start_dma(host->dma_ch);
+ } else
+ mmc_omap_dma_done(host, host->data);
+}
+
+static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data)
+{
+ const char *dev_name;
+ int sync_dev, dma_ch, is_read, r;
+
+ is_read = !(data->flags & MMC_DATA_WRITE);
+ del_timer_sync(&host->dma_timer);
+ if (host->dma_ch >= 0) {
+ if (is_read == host->dma_is_read)
+ return 0;
+ omap_free_dma(host->dma_ch);
+ host->dma_ch = -1;
+ }
+
+ if (is_read) {
+ if (host->id == 1) {
+ sync_dev = OMAP_DMA_MMC_RX;
+ dev_name = "MMC1 read";
+ } else {
+ sync_dev = OMAP_DMA_MMC2_RX;
+ dev_name = "MMC2 read";
+ }
+ } else {
+ if (host->id == 1) {
+ sync_dev = OMAP_DMA_MMC_TX;
+ dev_name = "MMC1 write";
+ } else {
+ sync_dev = OMAP_DMA_MMC2_TX;
+ dev_name = "MMC2 write";
+ }
+ }
+ r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb,
+ host, &dma_ch);
+ if (r != 0) {
+ dev_dbg(mmc_dev(host->mmc), "omap_request_dma() failed with %d\n", r);
+ return r;
+ }
+ host->dma_ch = dma_ch;
+ host->dma_is_read = is_read;
+
+ return 0;
+}
+
+static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ u16 reg;
+
+ reg = OMAP_MMC_READ(host->base, SDIO);
+ reg &= ~(1 << 5);
+ OMAP_MMC_WRITE(host->base, SDIO, reg);
+ /* Set maximum timeout */
+ OMAP_MMC_WRITE(host->base, CTO, 0xff);
+}
+
+static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ int timeout;
+ u16 reg;
+
+ /* Convert ns to clock cycles by assuming 20MHz frequency
+ * 1 cycle at 20MHz = 500 ns
+ */
+ timeout = req->data->timeout_clks + req->data->timeout_ns / 500;
+
+ /* Check if we need to use timeout multiplier register */
+ reg = OMAP_MMC_READ(host->base, SDIO);
+ if (timeout > 0xffff) {
+ reg |= (1 << 5);
+ timeout /= 1024;
+ } else
+ reg &= ~(1 << 5);
+ OMAP_MMC_WRITE(host->base, SDIO, reg);
+ OMAP_MMC_WRITE(host->base, DTO, timeout);
+}
+
+static void
+mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req)
+{
+ struct mmc_data *data = req->data;
+ int i, use_dma, block_size;
+ unsigned sg_len;
+
+ host->data = data;
+ if (data == NULL) {
+ OMAP_MMC_WRITE(host->base, BLEN, 0);
+ OMAP_MMC_WRITE(host->base, NBLK, 0);
+ OMAP_MMC_WRITE(host->base, BUF, 0);
+ host->dma_in_use = 0;
+ set_cmd_timeout(host, req);
+ return;
+ }
+
+
+ block_size = 1 << data->blksz_bits;
+
+ OMAP_MMC_WRITE(host->base, NBLK, data->blocks - 1);
+ OMAP_MMC_WRITE(host->base, BLEN, block_size - 1);
+ set_data_timeout(host, req);
+
+ /* cope with calling layer confusion; it issues "single
+ * block" writes using multi-block scatterlists.
+ */
+ sg_len = (data->blocks == 1) ? 1 : data->sg_len;
+
+ /* Only do DMA for entire blocks */
+ use_dma = host->use_dma;
+ if (use_dma) {
+ for (i = 0; i < sg_len; i++) {
+ if ((data->sg[i].length % block_size) != 0) {
+ use_dma = 0;
+ break;
+ }
+ }
+ }
+
+ host->sg_idx = 0;
+ if (use_dma) {
+ if (mmc_omap_get_dma_channel(host, data) == 0) {
+ enum dma_data_direction dma_data_dir;
+
+ if (data->flags & MMC_DATA_WRITE)
+ dma_data_dir = DMA_TO_DEVICE;
+ else
+ dma_data_dir = DMA_FROM_DEVICE;
+
+ host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg,
+ sg_len, dma_data_dir);
+ host->total_bytes_left = 0;
+ mmc_omap_prepare_dma(host, req->data);
+ host->brs_received = 0;
+ host->dma_done = 0;
+ host->dma_in_use = 1;
+ } else
+ use_dma = 0;
+ }
+
+ /* Revert to PIO? */
+ if (!use_dma) {
+ OMAP_MMC_WRITE(host->base, BUF, 0x1f1f);
+ host->total_bytes_left = data->blocks * block_size;
+ host->sg_len = sg_len;
+ mmc_omap_sg_to_buf(host);
+ host->dma_in_use = 0;
+ }
+}
+
+static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ WARN_ON(host->mrq != NULL);
+
+ host->mrq = req;
+
+ /* only touch fifo AFTER the controller readies it */
+ mmc_omap_prepare_data(host, req);
+ mmc_omap_start_command(host, req->cmd);
+ if (host->dma_in_use)
+ omap_start_dma(host->dma_ch);
+}
+
+static void innovator_fpga_socket_power(int on)
+{
+#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX)
+
+ if (on) {
+ fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3),
+ OMAP1510_FPGA_POWER);
+ } else {
+ fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3),
+ OMAP1510_FPGA_POWER);
+ }
+#endif
+}
+
+/*
+ * Turn the socket power on/off. Innovator uses FPGA, most boards
+ * probably use GPIO.
+ */
+static void mmc_omap_power(struct mmc_omap_host *host, int on)
+{
+ if (on) {
+ if (machine_is_omap_innovator())
+ innovator_fpga_socket_power(1);
+ else if (machine_is_omap_h2())
+ tps65010_set_gpio_out_value(GPIO3, HIGH);
+ else if (machine_is_omap_h3())
+ /* GPIO 4 of TPS65010 sends SD_EN signal */
+ tps65010_set_gpio_out_value(GPIO4, HIGH);
+ else if (cpu_is_omap24xx()) {
+ u16 reg = OMAP_MMC_READ(host->base, CON);
+ OMAP_MMC_WRITE(host->base, CON, reg | (1 << 11));
+ } else
+ if (host->power_pin >= 0)
+ omap_set_gpio_dataout(host->power_pin, 1);
+ } else {
+ if (machine_is_omap_innovator())
+ innovator_fpga_socket_power(0);
+ else if (machine_is_omap_h2())
+ tps65010_set_gpio_out_value(GPIO3, LOW);
+ else if (machine_is_omap_h3())
+ tps65010_set_gpio_out_value(GPIO4, LOW);
+ else if (cpu_is_omap24xx()) {
+ u16 reg = OMAP_MMC_READ(host->base, CON);
+ OMAP_MMC_WRITE(host->base, CON, reg & ~(1 << 11));
+ } else
+ if (host->power_pin >= 0)
+ omap_set_gpio_dataout(host->power_pin, 0);
+ }
+}
+
+static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+ int dsor;
+ int realclock, i;
+
+ realclock = ios->clock;
+
+ if (ios->clock == 0)
+ dsor = 0;
+ else {
+ int func_clk_rate = clk_get_rate(host->fclk);
+
+ dsor = func_clk_rate / realclock;
+ if (dsor < 1)
+ dsor = 1;
+
+ if (func_clk_rate / dsor > realclock)
+ dsor++;
+
+ if (dsor > 250)
+ dsor = 250;
+ dsor++;
+
+ if (ios->bus_width == MMC_BUS_WIDTH_4)
+ dsor |= 1 << 15;
+ }
+
+ switch (ios->power_mode) {
+ case MMC_POWER_OFF:
+ mmc_omap_power(host, 0);
+ break;
+ case MMC_POWER_UP:
+ case MMC_POWER_ON:
+ mmc_omap_power(host, 1);
+ dsor |= 1<<11;
+ break;
+ }
+
+ host->bus_mode = ios->bus_mode;
+ host->hw_bus_mode = host->bus_mode;
+
+ clk_enable(host->fclk);
+
+ /* On insanely high arm_per frequencies something sometimes
+ * goes somehow out of sync, and the POW bit is not being set,
+ * which results in the while loop below getting stuck.
+ * Writing to the CON register twice seems to do the trick. */
+ for (i = 0; i < 2; i++)
+ OMAP_MMC_WRITE(host->base, CON, dsor);
+ if (ios->power_mode == MMC_POWER_UP) {
+ /* Send clock cycles, poll completion */
+ OMAP_MMC_WRITE(host->base, IE, 0);
+ OMAP_MMC_WRITE(host->base, STAT, 0xffff);
+ OMAP_MMC_WRITE(host->base, CMD, 1<<7);
+ while (0 == (OMAP_MMC_READ(host->base, STAT) & 1));
+ OMAP_MMC_WRITE(host->base, STAT, 1);
+ }
+ clk_disable(host->fclk);
+}
+
+static int mmc_omap_get_ro(struct mmc_host *mmc)
+{
+ struct mmc_omap_host *host = mmc_priv(mmc);
+
+ return host->wp_pin && omap_get_gpio_datain(host->wp_pin);
+}
+
+static struct mmc_host_ops mmc_omap_ops = {
+ .request = mmc_omap_request,
+ .set_ios = mmc_omap_set_ios,
+ .get_ro = mmc_omap_get_ro,
+};
+
+static int __init mmc_omap_probe(struct platform_device *pdev)
+{
+ struct omap_mmc_conf *minfo = pdev->dev.platform_data;
+ struct mmc_host *mmc;
+ struct mmc_omap_host *host = NULL;
+ int ret = 0;
+
+ if (platform_get_resource(pdev, IORESOURCE_MEM, 0) ||
+ platform_get_irq(pdev, IORESOURCE_IRQ, 0)) {
+ dev_err(&pdev->dev, "mmc_omap_probe: invalid resource type\n");
+ return -ENODEV;
+ }
+
+ if (!request_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1,
+ pdev->name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ return -EBUSY;
+ }
+
+ mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev);
+ if (!mmc) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ host = mmc_priv(mmc);
+ host->mmc = mmc;
+
+ spin_lock_init(&host->dma_lock);
+ init_timer(&host->dma_timer);
+ host->dma_timer.function = mmc_omap_dma_timer;
+ host->dma_timer.data = (unsigned long) host;
+
+ host->id = pdev->id;
+
+ if (cpu_is_omap24xx()) {
+ host->iclk = clk_get(&pdev->dev, "mmc_ick");
+ if (IS_ERR(host->iclk))
+ goto out;
+ clk_enable(host->iclk);
+ }
+
+ if (!cpu_is_omap24xx())
+ host->fclk = clk_get(&pdev->dev, "mmc_ck");
+ else
+ host->fclk = clk_get(&pdev->dev, "mmc_fck");
+
+ if (IS_ERR(host->fclk)) {
+ ret = PTR_ERR(host->fclk);
+ goto out;
+ }
+
+ /* REVISIT:
+ * Also, use minfo->cover to decide how to manage
+ * the card detect sensing.
+ */
+ host->power_pin = minfo->power_pin;
+ host->switch_pin = minfo->switch_pin;
+ host->wp_pin = minfo->wp_pin;
+ host->use_dma = 1;
+ host->dma_ch = -1;
+
+ host->irq = pdev->resource[1].start;
+ host->base = ioremap(pdev->res.start, SZ_4K);
+ if (!host->base) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ if (minfo->wire4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
+
+ mmc->ops = &mmc_omap_ops;
+ mmc->f_min = 400000;
+ mmc->f_max = 24000000;
+ mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+
+ /* Use scatterlist DMA to reduce per-transfer costs.
+ * NOTE max_seg_size assumption that small blocks aren't
+ * normally used (except e.g. for reading SD registers).
+ */
+ mmc->max_phys_segs = 32;
+ mmc->max_hw_segs = 32;
+ mmc->max_sectors = 256; /* NBLK max 11-bits, OMAP also limited by DMA */
+ mmc->max_seg_size = mmc->max_sectors * 512;
+
+ if (host->power_pin >= 0) {
+ if ((ret = omap_request_gpio(host->power_pin)) != 0) {
+ dev_err(mmc_dev(host->mmc), "Unable to get GPIO
+ pin for MMC power\n");
+ goto out;
+ }
+ omap_set_gpio_direction(host->power_pin, 0);
+ }
+
+ ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host);
+ if (ret)
+ goto out;
+
+ host->dev = &pdev->dev;
+ platform_set_drvdata(pdev, host);
+
+ mmc_add_host(mmc);
+
+ if (host->switch_pin >= 0) {
+ INIT_WORK(&host->switch_work, mmc_omap_switch_handler, host);
+ init_timer(&host->switch_timer);
+ host->switch_timer.function = mmc_omap_switch_timer;
+ host->switch_timer.data = (unsigned long) host;
+ if (omap_request_gpio(host->switch_pin) != 0) {
+ dev_warn(mmc_dev(host->mmc), "Unable to get GPIO pin for MMC cover switch\n");
+ host->switch_pin = -1;
+ goto no_switch;
+ }
+
+ omap_set_gpio_direction(host->switch_pin, 1);
+ ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin),
+ mmc_omap_switch_irq, SA_TRIGGER_RISING, DRIVER_NAME, host);
+ if (ret) {
+ dev_warn(mmc_dev(host->mmc), "Unable to get IRQ for MMC cover switch\n");
+ omap_free_gpio(host->switch_pin);
+ host->switch_pin = -1;
+ goto no_switch;
+ }
+ ret = device_create_file(&pdev->dev, &dev_attr_cover_switch);
+ if (ret == 0) {
+ ret = device_create_file(&pdev->dev, &dev_attr_enable_poll);
+ if (ret != 0)
+ device_remove_file(&pdev->dev, &dev_attr_cover_switch);
+ }
+ if (ret) {
+ dev_wan(mmc_dev(host->mmc), "Unable to create sysfs attributes\n");
+ free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
+ omap_free_gpio(host->switch_pin);
+ host->switch_pin = -1;
+ goto no_switch;
+ }
+ if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host))
+ schedule_work(&host->switch_work);
+ }
+
+no_switch:
+ return 0;
+
+out:
+ /* FIXME: Free other resources too. */
+ if (host) {
+ if (host->iclk && !IS_ERR(host->iclk))
+ clk_put(host->iclk);
+ if (host->fclk && !IS_ERR(host->fclk))
+ clk_put(host->fclk);
+ mmc_free_host(host->mmc);
+ }
+ return ret;
+}
+
+static int mmc_omap_remove(struct platform_device *pdev)
+{
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ if (host) {
+ mmc_remove_host(host->mmc);
+ free_irq(host->irq, host);
+
+ if (host->power_pin >= 0)
+ omap_free_gpio(host->power_pin);
+ if (host->switch_pin >= 0) {
+ device_remove_file(&pdev->dev, &dev_attr_enable_poll);
+ device_remove_file(&pdev->dev, &dev_attr_cover_switch);
+ free_irq(OMAP_GPIO_IRQ(host->switch_pin), host);
+ omap_free_gpio(host->switch_pin);
+ host->switch_pin = -1;
+ del_timer_sync(&host->switch_timer);
+ flush_scheduled_work();
+ }
+ if (host->iclk && !IS_ERR(host->iclk))
+ clk_put(host->iclk);
+ if (host->fclk && !IS_ERR(host->fclk))
+ clk_put(host->fclk);
+ mmc_free_host(host->mmc);
+ }
+
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end - pdev->resource[0].start + 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+ int ret = 0;
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ if (host && host->suspended)
+ return 0;
+
+ if (host) {
+ ret = mmc_suspend_host(host->mmc, mesg);
+ if (ret == 0)
+ host->suspended = 1;
+ }
+ return ret;
+}
+
+static int mmc_omap_resume(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct mmc_omap_host *host = platform_get_drvdata(pdev);
+
+ if (host && !host->suspended)
+ return 0;
+
+ if (host) {
+ ret = mmc_resume_host(host->mmc);
+ if (ret == 0)
+ host->suspended = 0;
+ }
+
+ return ret;
+}
+#else
+#define mmc_omap_suspend NULL
+#define mmc_omap_resume NULL
+#endif
+
+static struct platform_driver mmc_omap_driver = {
+ .probe = mmc_omap_probe,
+ .remove = mmc_omap_remove,
+ .suspend = mmc_omap_suspend,
+ .resume = mmc_omap_resume,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static int __init mmc_omap_init(void)
+{
+ return platform_driver_register(&mmc_omap_driver);
+}
+
+static void __exit mmc_omap_exit(void)
+{
+ platform_driver_unregister(&mmc_omap_driver);
+}
+
+module_init(mmc_omap_init);
+module_exit(mmc_omap_exit);
+
+MODULE_DESCRIPTION("OMAP Multimedia Card driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(DRIVER_NAME);
+MODULE_AUTHOR("Juha Yrjölä");
diff --git a/drivers/mmc/omap.h b/drivers/mmc/omap.h
new file mode 100644
index 000000000000..c954d355a5e3
--- /dev/null
+++ b/drivers/mmc/omap.h
@@ -0,0 +1,55 @@
+#ifndef DRIVERS_MEDIA_MMC_OMAP_H
+#define DRIVERS_MEDIA_MMC_OMAP_H
+
+#define OMAP_MMC_REG_CMD 0x00
+#define OMAP_MMC_REG_ARGL 0x04
+#define OMAP_MMC_REG_ARGH 0x08
+#define OMAP_MMC_REG_CON 0x0c
+#define OMAP_MMC_REG_STAT 0x10
+#define OMAP_MMC_REG_IE 0x14
+#define OMAP_MMC_REG_CTO 0x18
+#define OMAP_MMC_REG_DTO 0x1c
+#define OMAP_MMC_REG_DATA 0x20
+#define OMAP_MMC_REG_BLEN 0x24
+#define OMAP_MMC_REG_NBLK 0x28
+#define OMAP_MMC_REG_BUF 0x2c
+#define OMAP_MMC_REG_SDIO 0x34
+#define OMAP_MMC_REG_REV 0x3c
+#define OMAP_MMC_REG_RSP0 0x40
+#define OMAP_MMC_REG_RSP1 0x44
+#define OMAP_MMC_REG_RSP2 0x48
+#define OMAP_MMC_REG_RSP3 0x4c
+#define OMAP_MMC_REG_RSP4 0x50
+#define OMAP_MMC_REG_RSP5 0x54
+#define OMAP_MMC_REG_RSP6 0x58
+#define OMAP_MMC_REG_RSP7 0x5c
+#define OMAP_MMC_REG_IOSR 0x60
+#define OMAP_MMC_REG_SYSC 0x64
+#define OMAP_MMC_REG_SYSS 0x68
+
+#define OMAP_MMC_STAT_CARD_ERR (1 << 14)
+#define OMAP_MMC_STAT_CARD_IRQ (1 << 13)
+#define OMAP_MMC_STAT_OCR_BUSY (1 << 12)
+#define OMAP_MMC_STAT_A_EMPTY (1 << 11)
+#define OMAP_MMC_STAT_A_FULL (1 << 10)
+#define OMAP_MMC_STAT_CMD_CRC (1 << 8)
+#define OMAP_MMC_STAT_CMD_TOUT (1 << 7)
+#define OMAP_MMC_STAT_DATA_CRC (1 << 6)
+#define OMAP_MMC_STAT_DATA_TOUT (1 << 5)
+#define OMAP_MMC_STAT_END_BUSY (1 << 4)
+#define OMAP_MMC_STAT_END_OF_DATA (1 << 3)
+#define OMAP_MMC_STAT_CARD_BUSY (1 << 2)
+#define OMAP_MMC_STAT_END_OF_CMD (1 << 0)
+
+#define OMAP_MMC_READ(base, reg) __raw_readw((base) + OMAP_MMC_REG_##reg)
+#define OMAP_MMC_WRITE(base, reg, val) __raw_writew((val), (base) + OMAP_MMC_REG_##reg)
+
+/*
+ * Command types
+ */
+#define OMAP_MMC_CMDTYPE_BC 0
+#define OMAP_MMC_CMDTYPE_BCR 1
+#define OMAP_MMC_CMDTYPE_AC 2
+#define OMAP_MMC_CMDTYPE_ADTC 3
+
+#endif
diff --git a/drivers/mmc/pxamci.c b/drivers/mmc/pxamci.c
index c32fad1ce51c..eb9a8826e9b5 100644
--- a/drivers/mmc/pxamci.c
+++ b/drivers/mmc/pxamci.c
@@ -37,12 +37,6 @@
#include "pxamci.h"
-#ifdef CONFIG_MMC_DEBUG
-#define DBG(x...) printk(KERN_DEBUG x)
-#else
-#define DBG(x...) do { } while (0)
-#endif
-
#define DRIVER_NAME "pxa2xx-mci"
#define NR_SG 1
@@ -206,7 +200,7 @@ static void pxamci_start_cmd(struct pxamci_host *host, struct mmc_command *cmd,
static void pxamci_finish_request(struct pxamci_host *host, struct mmc_request *mrq)
{
- DBG("PXAMCI: request done\n");
+ pr_debug("PXAMCI: request done\n");
host->mrq = NULL;
host->cmd = NULL;
host->data = NULL;
@@ -252,7 +246,7 @@ static int pxamci_cmd_done(struct pxamci_host *host, unsigned int stat)
if ((cmd->resp[0] & 0x80000000) == 0)
cmd->error = MMC_ERR_BADCRC;
} else {
- DBG("ignoring CRC from command %d - *risky*\n",cmd->opcode);
+ pr_debug("ignoring CRC from command %d - *risky*\n",cmd->opcode);
}
#else
cmd->error = MMC_ERR_BADCRC;
@@ -317,12 +311,12 @@ static irqreturn_t pxamci_irq(int irq, void *devid, struct pt_regs *regs)
ireg = readl(host->base + MMC_I_REG);
- DBG("PXAMCI: irq %08x\n", ireg);
+ pr_debug("PXAMCI: irq %08x\n", ireg);
if (ireg) {
unsigned stat = readl(host->base + MMC_STAT);
- DBG("PXAMCI: stat %08x\n", stat);
+ pr_debug("PXAMCI: stat %08x\n", stat);
if (ireg & END_CMD_RES)
handled |= pxamci_cmd_done(host, stat);
@@ -376,9 +370,9 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct pxamci_host *host = mmc_priv(mmc);
- DBG("pxamci_set_ios: clock %u power %u vdd %u.%02u\n",
- ios->clock, ios->power_mode, ios->vdd / 100,
- ios->vdd % 100);
+ pr_debug("pxamci_set_ios: clock %u power %u vdd %u.%02u\n",
+ ios->clock, ios->power_mode, ios->vdd / 100,
+ ios->vdd % 100);
if (ios->clock) {
unsigned int clk = CLOCKRATE / ios->clock;
@@ -405,8 +399,8 @@ static void pxamci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
host->cmdat |= CMDAT_INIT;
}
- DBG("pxamci_set_ios: clkrt = %x cmdat = %x\n",
- host->clkrt, host->cmdat);
+ pr_debug("pxamci_set_ios: clkrt = %x cmdat = %x\n",
+ host->clkrt, host->cmdat);
}
static struct mmc_host_ops pxamci_ops = {
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 8b811d94371c..bdbfca050029 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -31,12 +31,8 @@
#define BUGMAIL "<sdhci-devel@list.drzeus.cx>"
-#ifdef CONFIG_MMC_DEBUG
#define DBG(f, x...) \
- printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__,## x)
-#else
-#define DBG(f, x...) do { } while (0)
-#endif
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x)
static const struct pci_device_id pci_ids[] __devinitdata = {
/* handle any SD host controller */
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c
index 3be397d436fa..511f7b0b31d2 100644
--- a/drivers/mmc/wbsd.c
+++ b/drivers/mmc/wbsd.c
@@ -44,15 +44,10 @@
#define DRIVER_NAME "wbsd"
#define DRIVER_VERSION "1.5"
-#ifdef CONFIG_MMC_DEBUG
#define DBG(x...) \
- printk(KERN_DEBUG DRIVER_NAME ": " x)
+ pr_debug(DRIVER_NAME ": " x)
#define DBGF(f, x...) \
- printk(KERN_DEBUG DRIVER_NAME " [%s()]: " f, __func__ , ##x)
-#else
-#define DBG(x...) do { } while (0)
-#define DBGF(x...) do { } while (0)
-#endif
+ pr_debug(DRIVER_NAME " [%s()]: " f, __func__ , ##x)
/*
* Device resources
diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c
index fdb91b6f1d97..57115618c496 100644
--- a/drivers/mtd/chips/amd_flash.c
+++ b/drivers/mtd/chips/amd_flash.c
@@ -664,7 +664,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
printk("%s: Probing for AMD compatible flash...\n", map->name);
if ((table_pos[0] = probe_new_chip(mtd, 0, NULL, &temp, table,
- sizeof(table)/sizeof(table[0])))
+ ARRAY_SIZE(table)))
== -1) {
printk(KERN_WARNING
"%s: Found no AMD compatible device at location zero\n",
@@ -696,7 +696,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map)
base += (1 << temp.chipshift)) {
int numchips = temp.numchips;
table_pos[numchips] = probe_new_chip(mtd, base, chips,
- &temp, table, sizeof(table)/sizeof(table[0]));
+ &temp, table, ARRAY_SIZE(table));
}
mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) *
diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c
index edb306c03c0a..517ea33e7260 100644
--- a/drivers/mtd/chips/jedec_probe.c
+++ b/drivers/mtd/chips/jedec_probe.c
@@ -34,6 +34,7 @@
#define MANUFACTURER_MACRONIX 0x00C2
#define MANUFACTURER_NEC 0x0010
#define MANUFACTURER_PMC 0x009D
+#define MANUFACTURER_SHARP 0x00b0
#define MANUFACTURER_SST 0x00BF
#define MANUFACTURER_ST 0x0020
#define MANUFACTURER_TOSHIBA 0x0098
@@ -124,6 +125,9 @@
#define PM49FL004 0x006E
#define PM49FL008 0x006A
+/* Sharp */
+#define LH28F640BF 0x00b0
+
/* ST - www.st.com */
#define M29W800DT 0x00D7
#define M29W800DB 0x005B
@@ -1267,6 +1271,19 @@ static const struct amd_flash_info jedec_table[] = {
.regions = {
ERASEINFO( 0x01000, 256 )
}
+ }, {
+ .mfr_id = MANUFACTURER_SHARP,
+ .dev_id = LH28F640BF,
+ .name = "LH28F640BF",
+ .uaddr = {
+ [0] = MTD_UADDR_UNNECESSARY, /* x8 */
+ },
+ .DevSize = SIZE_4MiB,
+ .CmdSet = P_ID_INTEL_STD,
+ .NumEraseRegions= 1,
+ .regions = {
+ ERASEINFO(0x40000,16),
+ }
}, {
.mfr_id = MANUFACTURER_SST,
.dev_id = SST39LF512,
@@ -2035,7 +2052,7 @@ static int jedec_probe_chip(struct map_info *map, __u32 base,
DEBUG(MTD_DEBUG_LEVEL3,
"Search for id:(%02x %02x) interleave(%d) type(%d)\n",
cfi->mfr, cfi->id, cfi_interleave(cfi), cfi->device_type);
- for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
+ 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",
diff --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c
index 36f61a6a766e..3cc0b23c5865 100644
--- a/drivers/mtd/chips/sharp.c
+++ b/drivers/mtd/chips/sharp.c
@@ -64,7 +64,7 @@
#undef AUTOUNLOCK /* automatically unlocks blocks before erasing */
-struct mtd_info *sharp_probe(struct map_info *);
+static struct mtd_info *sharp_probe(struct map_info *);
static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd);
@@ -96,7 +96,6 @@ struct sharp_info{
struct flchip chips[1];
};
-struct mtd_info *sharp_probe(struct map_info *map);
static void sharp_destroy(struct mtd_info *mtd);
static struct mtd_chip_driver sharp_chipdrv = {
@@ -107,7 +106,7 @@ static struct mtd_chip_driver sharp_chipdrv = {
};
-struct mtd_info *sharp_probe(struct map_info *map)
+static struct mtd_info *sharp_probe(struct map_info *map)
{
struct mtd_info *mtd = NULL;
struct sharp_info *sharp = NULL;
@@ -581,7 +580,7 @@ static void sharp_destroy(struct mtd_info *mtd)
}
-int __init sharp_probe_init(void)
+static int __init sharp_probe_init(void)
{
printk("MTD Sharp chip driver <ds@lineo.com>\n");
diff --git a/drivers/mtd/cmdlinepart.c b/drivers/mtd/cmdlinepart.c
index 6b8bb2e4dcfd..a7a7bfe33879 100644
--- a/drivers/mtd/cmdlinepart.c
+++ b/drivers/mtd/cmdlinepart.c
@@ -42,7 +42,8 @@
/* special size referring to all the remaining space in a partition */
-#define SIZE_REMAINING 0xffffffff
+#define SIZE_REMAINING UINT_MAX
+#define OFFSET_CONTINUOUS UINT_MAX
struct cmdline_mtd_partition {
struct cmdline_mtd_partition *next;
@@ -75,7 +76,7 @@ static struct mtd_partition * newpart(char *s,
{
struct mtd_partition *parts;
unsigned long size;
- unsigned long offset = 0;
+ unsigned long offset = OFFSET_CONTINUOUS;
char *name;
int name_len;
unsigned char *extra_mem;
@@ -314,7 +315,7 @@ static int parse_cmdline_partitions(struct mtd_info *master,
{
for(i = 0, offset = 0; i < part->num_parts; i++)
{
- if (!part->parts[i].offset)
+ if (part->parts[i].offset == OFFSET_CONTINUOUS)
part->parts[i].offset = offset;
else
offset = part->parts[i].offset;
diff --git a/drivers/mtd/devices/blkmtd.c b/drivers/mtd/devices/blkmtd.c
index 04f864d238db..79f2e1f23ebd 100644
--- a/drivers/mtd/devices/blkmtd.c
+++ b/drivers/mtd/devices/blkmtd.c
@@ -28,8 +28,9 @@
#include <linux/pagemap.h>
#include <linux/list.h>
#include <linux/init.h>
+#include <linux/mount.h>
#include <linux/mtd/mtd.h>
-
+#include <linux/mutex.h>
#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg)
#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg)
@@ -46,7 +47,7 @@ struct blkmtd_dev {
struct list_head list;
struct block_device *blkdev;
struct mtd_info mtd_info;
- struct semaphore wrbuf_mutex;
+ struct mutex wrbuf_mutex;
};
@@ -268,7 +269,7 @@ static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to,
if(end_len)
pagecnt++;
- down(&dev->wrbuf_mutex);
+ mutex_lock(&dev->wrbuf_mutex);
DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n",
start_len, len, end_len, pagecnt);
@@ -376,7 +377,7 @@ static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to,
blkmtd_write_out(bio);
DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err);
- up(&dev->wrbuf_mutex);
+ mutex_unlock(&dev->wrbuf_mutex);
if(retlen)
*retlen = thislen;
@@ -614,8 +615,6 @@ static struct mtd_erase_region_info *calc_erase_regions(
}
-extern dev_t __init name_to_dev_t(const char *line);
-
static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size)
{
struct block_device *bdev;
@@ -659,7 +658,7 @@ static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size
memset(dev, 0, sizeof(struct blkmtd_dev));
dev->blkdev = bdev;
if(!readonly) {
- init_MUTEX(&dev->wrbuf_mutex);
+ mutex_init(&dev->wrbuf_mutex);
}
dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 7ff403b2a0a0..4160b8334c53 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -18,6 +18,7 @@
#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/buffer_head.h>
+#include <linux/mutex.h>
#define VERSION "$Revision: 1.30 $"
@@ -31,7 +32,7 @@ struct block2mtd_dev {
struct list_head list;
struct block_device *blkdev;
struct mtd_info mtd;
- struct semaphore write_mutex;
+ struct mutex write_mutex;
};
@@ -134,9 +135,9 @@ static int block2mtd_erase(struct mtd_info *mtd, struct erase_info *instr)
int err;
instr->state = MTD_ERASING;
- down(&dev->write_mutex);
+ mutex_lock(&dev->write_mutex);
err = _block2mtd_erase(dev, from, len);
- up(&dev->write_mutex);
+ mutex_unlock(&dev->write_mutex);
if (err) {
ERROR("erase failed err = %d", err);
instr->state = MTD_ERASE_FAILED;
@@ -249,9 +250,9 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
if (to + len > mtd->size)
len = mtd->size - to;
- down(&dev->write_mutex);
+ mutex_lock(&dev->write_mutex);
err = _block2mtd_write(dev, buf, to, len, retlen);
- up(&dev->write_mutex);
+ mutex_unlock(&dev->write_mutex);
if (err > 0)
err = 0;
return err;
@@ -310,7 +311,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
goto devinit_err;
}
- init_MUTEX(&dev->write_mutex);
+ mutex_init(&dev->write_mutex);
/* Setup the MTD structure */
/* make the name contain the block device in */
diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c
index e4345cf744a2..23e7a5c7d2c1 100644
--- a/drivers/mtd/devices/doc2000.c
+++ b/drivers/mtd/devices/doc2000.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/bitops.h>
+#include <linux/mutex.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
@@ -605,7 +606,7 @@ static void DoC2k_init(struct mtd_info *mtd)
this->curfloor = -1;
this->curchip = -1;
- init_MUTEX(&this->lock);
+ mutex_init(&this->lock);
/* Ident all the chips present. */
DoC_ScanChips(this, maxchips);
@@ -645,7 +646,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
if (from >= this->totlen)
return -EINVAL;
- down(&this->lock);
+ mutex_lock(&this->lock);
*retlen = 0;
while (left) {
@@ -774,7 +775,7 @@ static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
buf += len;
}
- up(&this->lock);
+ mutex_unlock(&this->lock);
return ret;
}
@@ -803,7 +804,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
if (to >= this->totlen)
return -EINVAL;
- down(&this->lock);
+ mutex_lock(&this->lock);
*retlen = 0;
while (left) {
@@ -873,7 +874,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
printk(KERN_ERR "Error programming flash\n");
/* Error in programming */
*retlen = 0;
- up(&this->lock);
+ mutex_unlock(&this->lock);
return -EIO;
}
@@ -935,7 +936,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
printk(KERN_ERR "Error programming flash\n");
/* Error in programming */
*retlen = 0;
- up(&this->lock);
+ mutex_unlock(&this->lock);
return -EIO;
}
@@ -956,7 +957,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
if (ret) {
- up(&this->lock);
+ mutex_unlock(&this->lock);
return ret;
}
}
@@ -966,7 +967,7 @@ static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
buf += len;
}
- up(&this->lock);
+ mutex_unlock(&this->lock);
return 0;
}
@@ -975,13 +976,13 @@ static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
u_char *eccbuf, struct nand_oobinfo *oobsel)
{
static char static_buf[512];
- static DECLARE_MUTEX(writev_buf_sem);
+ static DEFINE_MUTEX(writev_buf_mutex);
size_t totretlen = 0;
size_t thisvecofs = 0;
int ret= 0;
- down(&writev_buf_sem);
+ mutex_lock(&writev_buf_mutex);
while(count) {
size_t thislen, thisretlen;
@@ -1024,7 +1025,7 @@ static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs,
to += thislen;
}
- up(&writev_buf_sem);
+ mutex_unlock(&writev_buf_mutex);
*retlen = totretlen;
return ret;
}
@@ -1037,7 +1038,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
int len256 = 0, ret;
struct Nand *mychip;
- down(&this->lock);
+ mutex_lock(&this->lock);
mychip = &this->chips[ofs >> this->chipshift];
@@ -1083,7 +1084,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
ret = DoC_WaitReady(this);
- up(&this->lock);
+ mutex_unlock(&this->lock);
return ret;
}
@@ -1197,10 +1198,10 @@ static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
struct DiskOnChip *this = mtd->priv;
int ret;
- down(&this->lock);
+ mutex_lock(&this->lock);
ret = doc_write_oob_nolock(mtd, ofs, len, retlen, buf);
- up(&this->lock);
+ mutex_unlock(&this->lock);
return ret;
}
@@ -1214,10 +1215,10 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
struct Nand *mychip;
int status;
- down(&this->lock);
+ mutex_lock(&this->lock);
if (ofs & (mtd->erasesize-1) || len & (mtd->erasesize-1)) {
- up(&this->lock);
+ mutex_unlock(&this->lock);
return -EINVAL;
}
@@ -1265,7 +1266,7 @@ static int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
callback:
mtd_erase_callback(instr);
- up(&this->lock);
+ mutex_unlock(&this->lock);
return 0;
}
diff --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c
index 1e876fcb0408..29b0ddaa324e 100644
--- a/drivers/mtd/devices/lart.c
+++ b/drivers/mtd/devices/lart.c
@@ -581,8 +581,6 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen
/***************************************************************************************************/
-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
-
static struct mtd_info mtd;
static struct mtd_erase_region_info erase_regions[] = {
@@ -640,7 +638,7 @@ int __init lart_flash_init (void)
mtd.flags = MTD_CAP_NORFLASH;
mtd.size = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM + FLASH_BLOCKSIZE_MAIN * FLASH_NUMBLOCKS_16m_MAIN;
mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
- mtd.numeraseregions = NB_OF (erase_regions);
+ mtd.numeraseregions = ARRAY_SIZE(erase_regions);
mtd.eraseregions = erase_regions;
mtd.erase = flash_erase;
mtd.read = flash_read;
@@ -670,9 +668,9 @@ int __init lart_flash_init (void)
result,mtd.eraseregions[result].numblocks);
#ifdef HAVE_PARTITIONS
- printk ("\npartitions = %d\n",NB_OF (lart_partitions));
+ printk ("\npartitions = %d\n", ARRAY_SIZE(lart_partitions));
- for (result = 0; result < NB_OF (lart_partitions); result++)
+ for (result = 0; result < ARRAY_SIZE(lart_partitions); result++)
printk (KERN_DEBUG
"\n\n"
"lart_partitions[%d].name = %s\n"
@@ -687,7 +685,7 @@ int __init lart_flash_init (void)
#ifndef HAVE_PARTITIONS
result = add_mtd_device (&mtd);
#else
- result = add_mtd_partitions (&mtd,lart_partitions,NB_OF (lart_partitions));
+ result = add_mtd_partitions (&mtd,lart_partitions, ARRAY_SIZE(lart_partitions));
#endif
return (result);
diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c
index d5f24089be71..04e65d5dae00 100644
--- a/drivers/mtd/devices/m25p80.c
+++ b/drivers/mtd/devices/m25p80.c
@@ -186,7 +186,7 @@ static int m25p80_erase(struct mtd_info *mtd, struct erase_info *instr)
struct m25p *flash = mtd_to_m25p(mtd);
u32 addr,len;
- DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %zd\n",
+ DEBUG(MTD_DEBUG_LEVEL2, "%s: %s %s 0x%08x, len %d\n",
flash->spi->dev.bus_id, __FUNCTION__, "at",
(u32)instr->addr, instr->len);
diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c
index 0ff2e4378244..485f663493d2 100644
--- a/drivers/mtd/devices/ms02-nv.c
+++ b/drivers/mtd/devices/ms02-nv.c
@@ -308,7 +308,7 @@ static int __init ms02nv_init(void)
break;
}
- for (i = 0; i < (sizeof(ms02nv_addrs) / sizeof(*ms02nv_addrs)); i++)
+ for (i = 0; i < ARRAY_SIZE(ms02nv_addrs); i++)
if (!ms02nv_init_one(ms02nv_addrs[i] << stride))
count++;
diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c
index 8a544890173d..a3b92479719d 100644
--- a/drivers/mtd/inftlcore.c
+++ b/drivers/mtd/inftlcore.c
@@ -47,9 +47,6 @@
*/
#define MAX_LOOPS 10000
-extern void INFTL_dumptables(struct INFTLrecord *inftl);
-extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
-
static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
{
struct INFTLrecord *inftl;
@@ -132,7 +129,7 @@ static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
return;
}
#ifdef PSYCHO_DEBUG
- printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
+ printk(KERN_INFO "INFTL: Found new inftl%c\n", inftl->mbd.devnum + 'a');
#endif
return;
}
@@ -885,8 +882,6 @@ static struct mtd_blktrans_ops inftl_tr = {
.owner = THIS_MODULE,
};
-extern char inftlmountrev[];
-
static int __init init_inftl(void)
{
printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.19 $, "
diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c
index a57791a6ce40..b933a2a27b18 100644
--- a/drivers/mtd/maps/alchemy-flash.c
+++ b/drivers/mtd/maps/alchemy-flash.c
@@ -126,8 +126,6 @@ static struct mtd_partition alchemy_partitions[] = {
}
};
-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
-
static struct mtd_info *mymtd;
int __init alchemy_mtd_init(void)
@@ -154,7 +152,7 @@ int __init alchemy_mtd_init(void)
* Static partition definition selection
*/
parts = alchemy_partitions;
- nb_parts = NB_OF(alchemy_partitions);
+ nb_parts = ARRAY_SIZE(alchemy_partitions);
alchemy_map.size = window_size;
/*
diff --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c
index 6a8c0415bde8..fd0f0d3187de 100644
--- a/drivers/mtd/maps/cfi_flagadm.c
+++ b/drivers/mtd/maps/cfi_flagadm.c
@@ -86,7 +86,7 @@ struct mtd_partition flagadm_parts[] = {
}
};
-#define PARTITION_COUNT (sizeof(flagadm_parts)/sizeof(struct mtd_partition))
+#define PARTITION_COUNT ARRAY_SIZE(flagadm_parts)
static struct mtd_info *mymtd;
diff --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c
index 49d90542fc75..652813cd6c2d 100644
--- a/drivers/mtd/maps/dbox2-flash.c
+++ b/drivers/mtd/maps/dbox2-flash.c
@@ -57,7 +57,7 @@ static struct mtd_partition partition_info[]= {
}
};
-#define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0]))
+#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
#define WINDOW_ADDR 0x10000000
#define WINDOW_SIZE 0x800000
diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c
index efb221692641..c299d10b33e6 100644
--- a/drivers/mtd/maps/dilnetpc.c
+++ b/drivers/mtd/maps/dilnetpc.c
@@ -300,7 +300,7 @@ static struct mtd_partition partition_info[]=
},
};
-#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
static struct mtd_info *mymtd;
static struct mtd_info *lowlvl_parts[NUM_PARTITIONS];
@@ -345,7 +345,7 @@ static struct mtd_partition higlvl_partition_info[]=
},
};
-#define NUM_HIGHLVL_PARTITIONS (sizeof(higlvl_partition_info)/sizeof(partition_info[0]))
+#define NUM_HIGHLVL_PARTITIONS ARRAY_SIZE(higlvl_partition_info)
static int dnp_adnp_probe(void)
diff --git a/drivers/mtd/maps/dmv182.c b/drivers/mtd/maps/dmv182.c
index b993ac01a9a5..2bb3c0f0f970 100644
--- a/drivers/mtd/maps/dmv182.c
+++ b/drivers/mtd/maps/dmv182.c
@@ -99,7 +99,7 @@ static struct mtd_info *this_mtd;
static int __init init_svme182(void)
{
struct mtd_partition *partitions;
- int num_parts = sizeof(svme182_partitions) / sizeof(struct mtd_partition);
+ int num_parts = ARRAY_SIZE(svme182_partitions);
partitions = svme182_partitions;
diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c
index 319094821101..0667101ccbe1 100644
--- a/drivers/mtd/maps/h720x-flash.c
+++ b/drivers/mtd/maps/h720x-flash.c
@@ -59,7 +59,7 @@ static struct mtd_partition h720x_partitions[] = {
}
};
-#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0]))
+#define NUM_PARTITIONS ARRAY_SIZE(h720x_partitions)
static int nr_mtd_parts;
static struct mtd_partition *mtd_parts;
diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c
index 33060a315722..ed215470158b 100644
--- a/drivers/mtd/maps/netsc520.c
+++ b/drivers/mtd/maps/netsc520.c
@@ -76,7 +76,7 @@ static struct mtd_partition partition_info[]={
.size = 0x80000
},
};
-#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
#define WINDOW_SIZE 0x00100000
#define WINDOW_ADDR 0x00200000
@@ -88,7 +88,7 @@ static struct map_info netsc520_map = {
.phys = WINDOW_ADDR,
};
-#define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info))
+#define NUM_FLASH_BANKS ARRAY_SIZE(netsc520_map)
static struct mtd_info *mymtd;
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c
index 632eb2aa968f..54a3102ab19a 100644
--- a/drivers/mtd/maps/nettel.c
+++ b/drivers/mtd/maps/nettel.c
@@ -128,8 +128,7 @@ static struct mtd_partition nettel_amd_partitions[] = {
}
};
-#define NUM_AMD_PARTITIONS \
- (sizeof(nettel_amd_partitions)/sizeof(nettel_amd_partitions[0]))
+#define NUM_AMD_PARTITIONS ARRAY_SIZE(nettel_amd_partitions)
/****************************************************************************/
diff --git a/drivers/mtd/maps/ocotea.c b/drivers/mtd/maps/ocotea.c
index c223514ca2eb..a21fcd195ab4 100644
--- a/drivers/mtd/maps/ocotea.c
+++ b/drivers/mtd/maps/ocotea.c
@@ -58,8 +58,6 @@ static struct mtd_partition ocotea_large_partitions[] = {
}
};
-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
-
int __init init_ocotea(void)
{
u8 fpga0_reg;
@@ -97,7 +95,7 @@ int __init init_ocotea(void)
if (flash) {
flash->owner = THIS_MODULE;
add_mtd_partitions(flash, ocotea_small_partitions,
- NB_OF(ocotea_small_partitions));
+ ARRAY_SIZE(ocotea_small_partitions));
} else {
printk("map probe failed for flash\n");
return -ENXIO;
@@ -118,7 +116,7 @@ int __init init_ocotea(void)
if (flash) {
flash->owner = THIS_MODULE;
add_mtd_partitions(flash, ocotea_large_partitions,
- NB_OF(ocotea_large_partitions));
+ ARRAY_SIZE(ocotea_large_partitions));
} else {
printk("map probe failed for flash\n");
return -ENXIO;
diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c
index 21822c2edbe4..d2ab1bae9c34 100644
--- a/drivers/mtd/maps/pci.c
+++ b/drivers/mtd/maps/pci.c
@@ -334,9 +334,6 @@ mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
return 0;
release:
- if (mtd)
- map_destroy(mtd);
-
if (map) {
map->exit(dev, map);
kfree(map);
diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c
index f988c817e196..8bbc751a6021 100644
--- a/drivers/mtd/maps/pcmciamtd.c
+++ b/drivers/mtd/maps/pcmciamtd.c
@@ -616,7 +616,7 @@ static void pcmciamtd_config(dev_link_t *link)
} else if(mem_type == 2) {
mtd = do_map_probe("map_rom", &dev->pcmcia_map);
} else {
- for(i = 0; i < sizeof(probes) / sizeof(char *); i++) {
+ for(i = 0; i < ARRAY_SIZE(probes); i++) {
DEBUG(1, "Trying %s", probes[i]);
mtd = do_map_probe(probes[i], &dev->pcmcia_map);
if(mtd)
diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c
index 5b76ed886185..50b14033613f 100644
--- a/drivers/mtd/maps/redwood.c
+++ b/drivers/mtd/maps/redwood.c
@@ -121,8 +121,7 @@ struct map_info redwood_flash_map = {
};
-#define NUM_REDWOOD_FLASH_PARTITIONS \
- (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0]))
+#define NUM_REDWOOD_FLASH_PARTITIONS ARRAY_SIZE(redwood_flash_partitions)
static struct mtd_info *redwood_mtd;
diff --git a/drivers/mtd/maps/sbc8240.c b/drivers/mtd/maps/sbc8240.c
index 225cdd9ba5b2..350286dc1d2e 100644
--- a/drivers/mtd/maps/sbc8240.c
+++ b/drivers/mtd/maps/sbc8240.c
@@ -66,7 +66,7 @@ static struct map_info sbc8240_map[2] = {
}
};
-#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info))
+#define NUM_FLASH_BANKS ARRAY_SIZE(sbc8240_map)
/*
* The following defines the partition layout of SBC8240 boards.
@@ -125,8 +125,6 @@ static struct mtd_partition sbc8240_fs_partitions [] = {
}
};
-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
-
/* trivial struct to describe partition information */
struct mtd_part_def
{
@@ -190,10 +188,10 @@ int __init init_sbc8240_mtd (void)
#ifdef CONFIG_MTD_PARTITIONS
sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions;
sbc8240_part_banks[0].type = "static image";
- sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions);
+ sbc8240_part_banks[0].nums = ARRAY_SIZE(sbc8240_uboot_partitions);
sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions;
sbc8240_part_banks[1].type = "static file system";
- sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions);
+ sbc8240_part_banks[1].nums = ARRAY_SIZE(sbc8240_fs_partitions);
for (i = 0; i < NUM_FLASH_BANKS; i++) {
diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c
index ed92afadd8a9..e8c130e1efd3 100644
--- a/drivers/mtd/maps/sc520cdp.c
+++ b/drivers/mtd/maps/sc520cdp.c
@@ -107,7 +107,7 @@ static struct map_info sc520cdp_map[] = {
},
};
-#define NUM_FLASH_BANKS (sizeof(sc520cdp_map)/sizeof(struct map_info))
+#define NUM_FLASH_BANKS ARRAY_SIZE(sc520cdp_map)
static struct mtd_info *mymtd[NUM_FLASH_BANKS];
static struct mtd_info *merged_mtd;
diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c
index 2c91dff8bb60..28b8a571a91a 100644
--- a/drivers/mtd/maps/scx200_docflash.c
+++ b/drivers/mtd/maps/scx200_docflash.c
@@ -70,7 +70,7 @@ static struct mtd_partition partition_info[] = {
.size = 0x80000
},
};
-#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
+#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
#endif
diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c
index 999f4bb3d845..12fe53c0d2fc 100644
--- a/drivers/mtd/maps/sharpsl-flash.c
+++ b/drivers/mtd/maps/sharpsl-flash.c
@@ -49,8 +49,6 @@ static struct mtd_partition sharpsl_partitions[1] = {
}
};
-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
-
int __init init_sharpsl(void)
{
struct mtd_partition *parts;
@@ -92,7 +90,7 @@ int __init init_sharpsl(void)
}
parts = sharpsl_partitions;
- nb_parts = NB_OF(sharpsl_partitions);
+ nb_parts = ARRAY_SIZE(sharpsl_partitions);
printk(KERN_NOTICE "Using %s partision definition\n", part_type);
add_mtd_partitions(mymtd, parts, nb_parts);
diff --git a/drivers/mtd/maps/ts5500_flash.c b/drivers/mtd/maps/ts5500_flash.c
index 4b372bcb17f1..a7422c200567 100644
--- a/drivers/mtd/maps/ts5500_flash.c
+++ b/drivers/mtd/maps/ts5500_flash.c
@@ -64,7 +64,7 @@ static struct mtd_partition ts5500_partitions[] = {
}
};
-#define NUM_PARTITIONS (sizeof(ts5500_partitions)/sizeof(struct mtd_partition))
+#define NUM_PARTITIONS ARRAY_SIZE(ts5500_partitions)
static struct mtd_info *mymtd;
diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c
index 79d92808b766..f7264dc2ac9b 100644
--- a/drivers/mtd/maps/uclinux.c
+++ b/drivers/mtd/maps/uclinux.c
@@ -37,7 +37,7 @@ struct mtd_partition uclinux_romfs[] = {
{ .name = "ROMfs" }
};
-#define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0]))
+#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs)
/****************************************************************************/
diff --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c
index e0063941c0df..b3e487395435 100644
--- a/drivers/mtd/maps/vmax301.c
+++ b/drivers/mtd/maps/vmax301.c
@@ -182,7 +182,7 @@ int __init init_vmax301(void)
}
}
- if (!vmax_mtd[1] && !vmax_mtd[2]) {
+ if (!vmax_mtd[0] && !vmax_mtd[1]) {
iounmap((void *)iomapadr);
return -ENXIO;
}
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 840dd66ce2dc..458d3c8ae1ee 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -19,12 +19,12 @@
#include <linux/spinlock.h>
#include <linux/hdreg.h>
#include <linux/init.h>
-#include <asm/semaphore.h>
+#include <linux/mutex.h>
#include <asm/uaccess.h>
static LIST_HEAD(blktrans_majors);
-extern struct semaphore mtd_table_mutex;
+extern struct mutex mtd_table_mutex;
extern struct mtd_info *mtd_table[];
struct mtd_blkcore_priv {
@@ -122,9 +122,9 @@ static int mtd_blktrans_thread(void *arg)
spin_unlock_irq(rq->queue_lock);
- down(&dev->sem);
+ mutex_lock(&dev->lock);
res = do_blktrans_request(tr, dev, req);
- up(&dev->sem);
+ mutex_unlock(&dev->lock);
spin_lock_irq(rq->queue_lock);
@@ -235,8 +235,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
int last_devnum = -1;
struct gendisk *gd;
- if (!down_trylock(&mtd_table_mutex)) {
- up(&mtd_table_mutex);
+ if (!!mutex_trylock(&mtd_table_mutex)) {
+ mutex_unlock(&mtd_table_mutex);
BUG();
}
@@ -267,7 +267,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
return -EBUSY;
}
- init_MUTEX(&new->sem);
+ mutex_init(&new->lock);
list_add_tail(&new->list, &tr->devs);
added:
if (!tr->writesect)
@@ -313,8 +313,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
{
- if (!down_trylock(&mtd_table_mutex)) {
- up(&mtd_table_mutex);
+ if (!!mutex_trylock(&mtd_table_mutex)) {
+ mutex_unlock(&mtd_table_mutex);
BUG();
}
@@ -378,14 +378,14 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
ret = register_blkdev(tr->major, tr->name);
if (ret) {
printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
tr->name, tr->major, ret);
kfree(tr->blkcore_priv);
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return ret;
}
spin_lock_init(&tr->blkcore_priv->queue_lock);
@@ -396,7 +396,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
if (!tr->blkcore_priv->rq) {
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return -ENOMEM;
}
@@ -407,7 +407,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
blk_cleanup_queue(tr->blkcore_priv->rq);
unregister_blkdev(tr->major, tr->name);
kfree(tr->blkcore_priv);
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return ret;
}
@@ -419,7 +419,7 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
tr->add_mtd(tr, mtd_table[i]);
}
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return 0;
}
@@ -428,7 +428,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
{
struct list_head *this, *next;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
/* Clean up the kernel thread */
tr->blkcore_priv->exiting = 1;
@@ -446,7 +446,7 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
blk_cleanup_queue(tr->blkcore_priv->rq);
unregister_blkdev(tr->major, tr->name);
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
kfree(tr->blkcore_priv);
diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c
index e84756644fd1..2cef280e388c 100644
--- a/drivers/mtd/mtdblock.c
+++ b/drivers/mtd/mtdblock.c
@@ -19,11 +19,13 @@
#include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h>
+#include <linux/mutex.h>
+
static struct mtdblk_dev {
struct mtd_info *mtd;
int count;
- struct semaphore cache_sem;
+ struct mutex cache_mutex;
unsigned char *cache_data;
unsigned long cache_offset;
unsigned int cache_size;
@@ -284,7 +286,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd)
mtdblk->count = 1;
mtdblk->mtd = mtd;
- init_MUTEX (&mtdblk->cache_sem);
+ mutex_init(&mtdblk->cache_mutex);
mtdblk->cache_state = STATE_EMPTY;
if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&
mtdblk->mtd->erasesize) {
@@ -306,9 +308,9 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd)
DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
- down(&mtdblk->cache_sem);
+ mutex_lock(&mtdblk->cache_mutex);
write_cached_data(mtdblk);
- up(&mtdblk->cache_sem);
+ mutex_unlock(&mtdblk->cache_mutex);
if (!--mtdblk->count) {
/* It was the last usage. Free the device */
@@ -327,9 +329,9 @@ static int mtdblock_flush(struct mtd_blktrans_dev *dev)
{
struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
- down(&mtdblk->cache_sem);
+ mutex_lock(&mtdblk->cache_mutex);
write_cached_data(mtdblk);
- up(&mtdblk->cache_sem);
+ mutex_unlock(&mtdblk->cache_mutex);
if (mtdblk->mtd->sync)
mtdblk->mtd->sync(mtdblk->mtd);
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c
index dade02ab0687..9905870f56e5 100644
--- a/drivers/mtd/mtdcore.c
+++ b/drivers/mtd/mtdcore.c
@@ -19,15 +19,13 @@
#include <linux/ioctl.h>
#include <linux/init.h>
#include <linux/mtd/compatmac.h>
-#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
-#endif
#include <linux/mtd/mtd.h>
/* These are exported solely for the purpose of mtd_blkdevs.c. You
should not use them for _anything_ else */
-DECLARE_MUTEX(mtd_table_mutex);
+DEFINE_MUTEX(mtd_table_mutex);
struct mtd_info *mtd_table[MAX_MTD_DEVICES];
EXPORT_SYMBOL_GPL(mtd_table_mutex);
@@ -49,7 +47,7 @@ int add_mtd_device(struct mtd_info *mtd)
{
int i;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
for (i=0; i < MAX_MTD_DEVICES; i++)
if (!mtd_table[i]) {
@@ -67,7 +65,7 @@ int add_mtd_device(struct mtd_info *mtd)
not->add(mtd);
}
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
/* We _know_ we aren't being removed, because
our caller is still holding us here. So none
of this try_ nonsense, and no bitching about it
@@ -76,7 +74,7 @@ int add_mtd_device(struct mtd_info *mtd)
return 0;
}
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return 1;
}
@@ -94,7 +92,7 @@ int del_mtd_device (struct mtd_info *mtd)
{
int ret;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
if (mtd_table[mtd->index] != mtd) {
ret = -ENODEV;
@@ -118,7 +116,7 @@ int del_mtd_device (struct mtd_info *mtd)
ret = 0;
}
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return ret;
}
@@ -135,7 +133,7 @@ void register_mtd_user (struct mtd_notifier *new)
{
int i;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
list_add(&new->list, &mtd_notifiers);
@@ -145,7 +143,7 @@ void register_mtd_user (struct mtd_notifier *new)
if (mtd_table[i])
new->add(mtd_table[i]);
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
}
/**
@@ -162,7 +160,7 @@ int unregister_mtd_user (struct mtd_notifier *old)
{
int i;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
module_put(THIS_MODULE);
@@ -171,7 +169,7 @@ int unregister_mtd_user (struct mtd_notifier *old)
old->remove(mtd_table[i]);
list_del(&old->list);
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return 0;
}
@@ -193,7 +191,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
struct mtd_info *ret = NULL;
int i;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
if (num == -1) {
for (i=0; i< MAX_MTD_DEVICES; i++)
@@ -211,7 +209,7 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
if (ret)
ret->usecount++;
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
return ret;
}
@@ -219,9 +217,9 @@ void put_mtd_device(struct mtd_info *mtd)
{
int c;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
c = --mtd->usecount;
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
BUG_ON(c < 0);
module_put(mtd->owner);
@@ -296,10 +294,11 @@ EXPORT_SYMBOL(unregister_mtd_user);
EXPORT_SYMBOL(default_mtd_writev);
EXPORT_SYMBOL(default_mtd_readv);
+#ifdef CONFIG_PROC_FS
+
/*====================================================================*/
/* Support for /proc/mtd */
-#ifdef CONFIG_PROC_FS
static struct proc_dir_entry *proc_mtd;
static inline int mtd_proc_info (char *buf, int i)
@@ -319,7 +318,7 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count,
int len, l, i;
off_t begin = 0;
- down(&mtd_table_mutex);
+ mutex_lock(&mtd_table_mutex);
len = sprintf(page, "dev: size erasesize name\n");
for (i=0; i< MAX_MTD_DEVICES; i++) {
@@ -337,38 +336,34 @@ static int mtd_read_proc (char *page, char **start, off_t off, int count,
*eof = 1;
done:
- up(&mtd_table_mutex);
+ mutex_unlock(&mtd_table_mutex);
if (off >= len+begin)
return 0;
*start = page + (off-begin);
return ((count < begin+len-off) ? count : begin+len-off);
}
-#endif /* CONFIG_PROC_FS */
-
/*====================================================================*/
/* Init code */
static int __init init_mtd(void)
{
-#ifdef CONFIG_PROC_FS
if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
proc_mtd->read_proc = mtd_read_proc;
-#endif
return 0;
}
static void __exit cleanup_mtd(void)
{
-#ifdef CONFIG_PROC_FS
if (proc_mtd)
remove_proc_entry( "mtd", NULL);
-#endif
}
module_init(init_mtd);
module_exit(cleanup_mtd);
+#endif /* CONFIG_PROC_FS */
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig
index 1fc4c134d939..cfe288a6e853 100644
--- a/drivers/mtd/nand/Kconfig
+++ b/drivers/mtd/nand/Kconfig
@@ -178,17 +178,16 @@ config MTD_NAND_DISKONCHIP_BBTWRITE
Even if you leave this disabled, you can enable BBT writes at module
load time (assuming you build diskonchip as a module) with the module
parameter "inftl_bbt_write=1".
-
- config MTD_NAND_SHARPSL
- bool "Support for NAND Flash on Sharp SL Series (C7xx + others)"
- depends on MTD_NAND && ARCH_PXA
-
- config MTD_NAND_NANDSIM
- bool "Support for NAND Flash Simulator"
- depends on MTD_NAND && MTD_PARTITIONS
+config MTD_NAND_SHARPSL
+ tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)"
+ depends on MTD_NAND && ARCH_PXA
+
+config MTD_NAND_NANDSIM
+ tristate "Support for NAND Flash Simulator"
+ depends on MTD_NAND && MTD_PARTITIONS
help
The simulator may simulate verious NAND flash chips for the
MTD nand layer.
-
+
endmenu
diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c
index 201e1362da14..bde3550910a2 100644
--- a/drivers/mtd/nand/au1550nd.c
+++ b/drivers/mtd/nand/au1550nd.c
@@ -55,8 +55,6 @@ static const struct mtd_partition partition_info[] = {
.size = MTDPART_SIZ_FULL
}
};
-#define NB_OF(x) (sizeof(x)/sizeof(x[0]))
-
/**
* au_read_byte - read one byte from the chip
@@ -462,7 +460,7 @@ int __init au1xxx_nand_init (void)
}
/* Register the partitions */
- add_mtd_partitions(au1550_mtd, partition_info, NB_OF(partition_info));
+ add_mtd_partitions(au1550_mtd, partition_info, ARRAY_SIZE(partition_info));
return 0;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 5d222460b42a..95e96fa1fceb 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -80,6 +80,7 @@
#include <linux/mtd/compatmac.h>
#include <linux/interrupt.h>
#include <linux/bitops.h>
+#include <linux/leds.h>
#include <asm/io.h>
#ifdef CONFIG_MTD_PARTITIONS
@@ -515,6 +516,8 @@ static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, i
return nand_isbad_bbt (mtd, ofs, allowbbt);
}
+DEFINE_LED_TRIGGER(nand_led_trigger);
+
/*
* Wait for the ready pin, after a command
* The timeout is catched later.
@@ -524,12 +527,14 @@ static void nand_wait_ready(struct mtd_info *mtd)
struct nand_chip *this = mtd->priv;
unsigned long timeo = jiffies + 2;
+ led_trigger_event(nand_led_trigger, LED_FULL);
/* wait until command is processed or timeout occures */
do {
if (this->dev_ready(mtd))
- return;
+ break;
touch_softlockup_watchdog();
} while (time_before(jiffies, timeo));
+ led_trigger_event(nand_led_trigger, LED_OFF);
}
/**
@@ -817,6 +822,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
else
timeo += (HZ * 20) / 1000;
+ 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. */
ndelay (100);
@@ -840,6 +847,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
}
cond_resched();
}
+ led_trigger_event(nand_led_trigger, LED_OFF);
+
status = (int) this->read_byte(mtd);
return status;
}
@@ -2724,6 +2733,21 @@ void nand_release (struct mtd_info *mtd)
EXPORT_SYMBOL_GPL (nand_scan);
EXPORT_SYMBOL_GPL (nand_release);
+
+static int __init nand_base_init(void)
+{
+ led_trigger_register_simple("nand-disk", &nand_led_trigger);
+ return 0;
+}
+
+static void __exit nand_base_exit(void)
+{
+ led_trigger_unregister_simple(nand_led_trigger);
+}
+
+module_init(nand_base_init);
+module_exit(nand_base_exit);
+
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION ("Generic NAND flash driver code");
diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c
index 8815c8dbef2d..c077d2ec9cdd 100644
--- a/drivers/mtd/redboot.c
+++ b/drivers/mtd/redboot.c
@@ -85,10 +85,6 @@ static int parse_redboot_partitions(struct mtd_info *master,
numslots = (master->erasesize / sizeof(struct fis_image_desc));
for (i = 0; i < numslots; i++) {
- if (buf[i].name[0] == 0xff) {
- i = numslots;
- break;
- }
if (!memcmp(buf[i].name, "FIS directory", 14)) {
/* This is apparently the FIS directory entry for the
* FIS directory itself. The FIS directory size is
@@ -128,7 +124,7 @@ static int parse_redboot_partitions(struct mtd_info *master,
struct fis_list *new_fl, **prev;
if (buf[i].name[0] == 0xff)
- break;
+ continue;
if (!redboot_checksum(&buf[i]))
break;
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index 70f63891b19c..274b0138d442 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -788,7 +788,7 @@ struct vortex_private {
int options; /* User-settable misc. driver options. */
unsigned int media_override:4, /* Passed-in media type. */
default_media:4, /* Read from the EEPROM/Wn3_Config. */
- full_duplex:1, force_fd:1, autoselect:1,
+ full_duplex:1, autoselect:1,
bus_master:1, /* Vortex can only do a fragment bus-m. */
full_bus_master_tx:1, full_bus_master_rx:2, /* Boomerang */
flow_ctrl:1, /* Use 802.3x flow control (PAUSE only) */
@@ -1633,12 +1633,6 @@ vortex_set_duplex(struct net_device *dev)
((vp->full_duplex && vp->flow_ctrl && vp->partner_flow_ctrl) ?
0x100 : 0),
ioaddr + Wn3_MAC_Ctrl);
-
- issue_and_wait(dev, TxReset);
- /*
- * Don't reset the PHY - that upsets autonegotiation during DHCP operations.
- */
- issue_and_wait(dev, RxReset|0x04);
}
static void vortex_check_media(struct net_device *dev, unsigned int init)
@@ -1663,7 +1657,7 @@ vortex_up(struct net_device *dev)
struct vortex_private *vp = netdev_priv(dev);
void __iomem *ioaddr = vp->ioaddr;
unsigned int config;
- int i;
+ int i, mii_reg1, mii_reg5;
if (VORTEX_PCI(vp)) {
pci_set_power_state(VORTEX_PCI(vp), PCI_D0); /* Go active */
@@ -1723,14 +1717,23 @@ vortex_up(struct net_device *dev)
printk(KERN_DEBUG "vortex_up(): writing 0x%x to InternalConfig\n", config);
iowrite32(config, ioaddr + Wn3_Config);
- netif_carrier_off(dev);
if (dev->if_port == XCVR_MII || dev->if_port == XCVR_NWAY) {
EL3WINDOW(4);
+ mii_reg1 = mdio_read(dev, vp->phys[0], MII_BMSR);
+ mii_reg5 = mdio_read(dev, vp->phys[0], MII_LPA);
+ vp->partner_flow_ctrl = ((mii_reg5 & 0x0400) != 0);
+
vortex_check_media(dev, 1);
}
else
vortex_set_duplex(dev);
+ issue_and_wait(dev, TxReset);
+ /*
+ * Don't reset the PHY - that upsets autonegotiation during DHCP operations.
+ */
+ issue_and_wait(dev, RxReset|0x04);
+
iowrite16(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
@@ -2083,16 +2086,14 @@ vortex_error(struct net_device *dev, int status)
}
if (tx_status & 0x14) vp->stats.tx_fifo_errors++;
if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
+ if (tx_status & 0x08) vp->xstats.tx_max_collisions++;
iowrite8(0, ioaddr + TxStatus);
if (tx_status & 0x30) { /* txJabber or txUnderrun */
do_tx_reset = 1;
- } else if (tx_status & 0x08) { /* maxCollisions */
- vp->xstats.tx_max_collisions++;
- if (vp->drv_flags & MAX_COLLISION_RESET) {
- do_tx_reset = 1;
- reset_mask = 0x0108; /* Reset interface logic, but not download logic */
- }
- } else { /* Merely re-enable the transmitter. */
+ } else if ((tx_status & 0x08) && (vp->drv_flags & MAX_COLLISION_RESET)) { /* maxCollisions */
+ do_tx_reset = 1;
+ reset_mask = 0x0108; /* Reset interface logic, but not download logic */
+ } else { /* Merely re-enable the transmitter. */
iowrite16(TxEnable, ioaddr + EL3_CMD);
}
}
diff --git a/drivers/net/8390.h b/drivers/net/8390.h
index 599b68d8c45f..51e39dcd0603 100644
--- a/drivers/net/8390.h
+++ b/drivers/net/8390.h
@@ -134,7 +134,7 @@ struct ei_device {
#define inb_p(_p) inb(_p)
#define outb_p(_v,_p) outb(_v,_p)
-#elif defined(CONFIG_NET_CBUS) || defined(CONFIG_NE_H8300) || defined(CONFIG_NE_H8300_MODULE)
+#elif defined(CONFIG_NE_H8300) || defined(CONFIG_NE_H8300_MODULE)
#define EI_SHIFT(x) (ei_local->reg_offset[x])
#else
#define EI_SHIFT(x) (x)
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index e20b849a22e8..bdaaad8f2123 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2313,13 +2313,11 @@ config S2IO_NAPI
endmenu
-if !UML
source "drivers/net/tokenring/Kconfig"
source "drivers/net/wireless/Kconfig"
source "drivers/net/pcmcia/Kconfig"
-endif
source "drivers/net/wan/Kconfig"
diff --git a/drivers/net/acenic_firmware.h b/drivers/net/acenic_firmware.h
index 6d625d595622..d7882dd783c8 100644
--- a/drivers/net/acenic_firmware.h
+++ b/drivers/net/acenic_firmware.h
@@ -4397,7 +4397,7 @@ static u32 tigonFwText[(MAX_TEXT_LEN/4) + 1] __devinitdata = {
0x3c010001, 0x220821, 0xac317e30, 0x8fbf0024,
0x8fb40020, 0x8fb3001c, 0x8fb20018, 0x8fb10014,
0x8fb00010, 0x3e00008, 0x27bd0028, 0x0 };
-static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = {
+static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __devinitdata = {
0x24486561, 0x6465723a, 0x202f7072,
0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765,
0x2f2e2f6e, 0x69632f66, 0x772f636f, 0x6d6d6f6e,
@@ -4571,7 +4571,7 @@ static u32 tigonFwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = {
0x0, 0x14c38, 0x14c38, 0x14b80,
0x14bc4, 0x14c38, 0x14c38, 0x0,
0x0, 0x0 };
-static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __initdata = {
+static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __devinitdata = {
0x416c7465,
0x6f6e2041, 0x63654e49, 0x43205600, 0x416c7465,
0x6f6e2041, 0x63654e49, 0x43205600, 0x42424242,
@@ -4612,7 +4612,7 @@ static u32 tigonFwData[(MAX_DATA_LEN/4) + 1] __initdata = {
#define tigon2FwSbssLen 0xcc
#define tigon2FwBssAddr 0x00016f50
#define tigon2FwBssLen 0x20c0
-static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __initdata = {
+static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __devinitdata = {
0x0,
0x10000003, 0x0, 0xd, 0xd,
0x3c1d0001, 0x8fbd6d20, 0x3a0f021, 0x3c100000,
@@ -9154,7 +9154,7 @@ static u32 tigon2FwText[(MAX_TEXT_LEN/4) + 1] __initdata = {
0x24020001, 0x8f430328, 0x1021, 0x24630001,
0x3e00008, 0xaf430328, 0x3e00008, 0x0,
0x0, 0x0, 0x0, 0x0 };
-static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = {
+static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __devinitdata = {
0x24486561, 0x6465723a, 0x202f7072,
0x6f6a6563, 0x74732f72, 0x63732f73, 0x772f6765,
0x2f2e2f6e, 0x69632f66, 0x77322f63, 0x6f6d6d6f,
@@ -9425,7 +9425,7 @@ static u32 tigon2FwRodata[(MAX_RODATA_LEN/4) + 1] __initdata = {
0x14ed8, 0x14b8c, 0x14bd8, 0x14c24,
0x14ed8, 0x7365746d, 0x61636163, 0x74000000,
0x0, 0x0 };
-static u32 tigon2FwData[(MAX_DATA_LEN/4) + 1] __initdata = {
+static u32 tigon2FwData[(MAX_DATA_LEN/4) + 1] __devinitdata = {
0x1,
0x1, 0x1, 0xc001fc, 0x3ffc,
0xc00000, 0x416c7465, 0x6f6e2041, 0x63654e49,
diff --git a/drivers/net/b44.c b/drivers/net/b44.c
index c3267e4e1bb0..15032f2c7817 100644
--- a/drivers/net/b44.c
+++ b/drivers/net/b44.c
@@ -1339,6 +1339,9 @@ static int b44_set_mac_addr(struct net_device *dev, void *p)
if (netif_running(dev))
return -EBUSY;
+ if (!is_valid_ether_addr(addr->sa_data))
+ return -EINVAL;
+
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
spin_lock_irq(&bp->lock);
@@ -1876,6 +1879,12 @@ static int __devinit b44_get_invariants(struct b44 *bp)
bp->dev->dev_addr[3] = eeprom[80];
bp->dev->dev_addr[4] = eeprom[83];
bp->dev->dev_addr[5] = eeprom[82];
+
+ if (!is_valid_ether_addr(&bp->dev->dev_addr[0])){
+ printk(KERN_ERR PFX "Invalid MAC address found in EEPROM\n");
+ return -EINVAL;
+ }
+
memcpy(bp->dev->perm_addr, bp->dev->dev_addr, bp->dev->addr_len);
bp->phy_addr = eeprom[90] & 0x1f;
@@ -2033,6 +2042,11 @@ static int __devinit b44_init_one(struct pci_dev *pdev,
pci_save_state(bp->pdev);
+ /* Chip reset provides power to the b44 MAC & PCI cores, which
+ * is necessary for MAC register access.
+ */
+ b44_chip_reset(bp);
+
printk(KERN_INFO "%s: Broadcom 4400 10/100BaseT Ethernet ", dev->name);
for (i = 0; i < 6; i++)
printk("%2.2x%c", dev->dev_addr[i],
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index f3f5825469d6..6a407070c2e8 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -2294,6 +2294,34 @@ void bond_3ad_handle_link_change(struct slave *slave, char link)
port->sm_vars |= AD_PORT_BEGIN;
}
+/*
+ * set link state for bonding master: if we have an active partnered
+ * aggregator, we're up, if not, we're down. Presumes that we cannot
+ * have an active aggregator if there are no slaves with link up.
+ *
+ * Called by bond_set_carrier(). Return zero if carrier state does not
+ * change, nonzero if it does.
+ */
+int bond_3ad_set_carrier(struct bonding *bond)
+{
+ struct aggregator *agg;
+
+ agg = __get_active_agg(&(SLAVE_AD_INFO(bond->first_slave).aggregator));
+ if (agg && MAC_ADDRESS_COMPARE(&agg->partner_system, &null_mac_addr)) {
+ if (!netif_carrier_ok(bond->dev)) {
+ netif_carrier_on(bond->dev);
+ return 1;
+ }
+ return 0;
+ }
+
+ if (netif_carrier_ok(bond->dev)) {
+ netif_carrier_off(bond->dev);
+ return 1;
+ }
+ return 0;
+}
+
/**
* bond_3ad_get_active_agg_info - get information of the active aggregator
* @bond: bonding struct to work on
diff --git a/drivers/net/bonding/bond_3ad.h b/drivers/net/bonding/bond_3ad.h
index 5ee2cef5b037..6ad5ad6e65d5 100644
--- a/drivers/net/bonding/bond_3ad.h
+++ b/drivers/net/bonding/bond_3ad.h
@@ -283,5 +283,6 @@ void bond_3ad_handle_link_change(struct slave *slave, char link);
int bond_3ad_get_active_agg_info(struct bonding *bond, struct ad_info *ad_info);
int bond_3ad_xmit_xor(struct sk_buff *skb, struct net_device *dev);
int bond_3ad_lacpdu_recv(struct sk_buff *skb, struct net_device *dev, struct packet_type* ptype, struct net_device *orig_dev);
+int bond_3ad_set_carrier(struct bonding *bond);
#endif //__BOND_3AD_H__
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index f13a539dc169..55d236726d11 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -559,6 +559,42 @@ out:
/*------------------------------- Link status -------------------------------*/
/*
+ * Set the carrier state for the master according to the state of its
+ * slaves. If any slaves are up, the master is up. In 802.3ad mode,
+ * do special 802.3ad magic.
+ *
+ * Returns zero if carrier state does not change, nonzero if it does.
+ */
+static int bond_set_carrier(struct bonding *bond)
+{
+ struct slave *slave;
+ int i;
+
+ if (bond->slave_cnt == 0)
+ goto down;
+
+ if (bond->params.mode == BOND_MODE_8023AD)
+ return bond_3ad_set_carrier(bond);
+
+ bond_for_each_slave(bond, slave, i) {
+ if (slave->link == BOND_LINK_UP) {
+ if (!netif_carrier_ok(bond->dev)) {
+ netif_carrier_on(bond->dev);
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+down:
+ if (netif_carrier_ok(bond->dev)) {
+ netif_carrier_off(bond->dev);
+ return 1;
+ }
+ return 0;
+}
+
+/*
* 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
@@ -1074,10 +1110,24 @@ void bond_change_active_slave(struct bonding *bond, struct slave *new_active)
void bond_select_active_slave(struct bonding *bond)
{
struct slave *best_slave;
+ int rv;
best_slave = bond_find_best_slave(bond);
if (best_slave != bond->curr_active_slave) {
bond_change_active_slave(bond, best_slave);
+ rv = bond_set_carrier(bond);
+ if (!rv)
+ return;
+
+ if (netif_carrier_ok(bond->dev)) {
+ printk(KERN_INFO DRV_NAME
+ ": %s: first active interface up!\n",
+ bond->dev->name);
+ } else {
+ printk(KERN_INFO DRV_NAME ": %s: "
+ "now running without any active interface !\n",
+ bond->dev->name);
+ }
}
}
@@ -1458,10 +1508,14 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
if (((!bond->curr_active_slave) ||
(bond->curr_active_slave->dev->priv_flags & IFF_SLAVE_INACTIVE)) &&
(new_slave->link != BOND_LINK_DOWN)) {
- dprintk("This is the first active slave\n");
/* first slave or no active slave yet, and this link
is OK, so make this interface the active one */
bond_change_active_slave(bond, new_slave);
+ printk(KERN_INFO DRV_NAME
+ ": %s: first active interface up!\n",
+ bond->dev->name);
+ netif_carrier_on(bond->dev);
+
} else {
dprintk("This is just a backup slave\n");
bond_set_slave_inactive_flags(new_slave);
@@ -1517,6 +1571,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
break;
} /* switch(bond_mode) */
+ bond_set_carrier(bond);
+
write_unlock_bh(&bond->lock);
res = bond_create_slave_symlinks(bond_dev, slave_dev);
@@ -1656,18 +1712,12 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
bond_alb_deinit_slave(bond, slave);
}
- if (oldcurrent == slave) {
+ if (oldcurrent == slave)
bond_select_active_slave(bond);
- if (!bond->curr_active_slave) {
- printk(KERN_INFO DRV_NAME
- ": %s: now running without any active "
- "interface !\n",
- bond_dev->name);
- }
- }
-
if (bond->slave_cnt == 0) {
+ bond_set_carrier(bond);
+
/* if the last slave was removed, zero the mac address
* of the master so it will be set by the application
* to the mac address of the first slave
@@ -1751,6 +1801,8 @@ static int bond_release_all(struct net_device *bond_dev)
write_lock_bh(&bond->lock);
+ netif_carrier_off(bond_dev);
+
if (bond->slave_cnt == 0) {
goto out;
}
@@ -2187,15 +2239,9 @@ void bond_mii_monitor(struct net_device *bond_dev)
bond_select_active_slave(bond);
- if (oldcurrent && !bond->curr_active_slave) {
- printk(KERN_INFO DRV_NAME
- ": %s: now running without any active "
- "interface !\n",
- bond_dev->name);
- }
-
write_unlock(&bond->curr_slave_lock);
- }
+ } else
+ bond_set_carrier(bond);
re_arm:
if (bond->params.miimon) {
@@ -2499,13 +2545,6 @@ void bond_loadbalance_arp_mon(struct net_device *bond_dev)
bond_select_active_slave(bond);
- if (oldcurrent && !bond->curr_active_slave) {
- printk(KERN_INFO DRV_NAME
- ": %s: now running without any active "
- "interface !\n",
- bond_dev->name);
- }
-
write_unlock(&bond->curr_slave_lock);
}
@@ -2579,12 +2618,15 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
bond->current_arp_slave = NULL;
}
+ bond_set_carrier(bond);
+
if (slave == bond->curr_active_slave) {
printk(KERN_INFO DRV_NAME
": %s: %s is up and now the "
"active interface\n",
bond_dev->name,
slave->dev->name);
+ netif_carrier_on(bond->dev);
} else {
printk(KERN_INFO DRV_NAME
": %s: backup interface %s is "
@@ -2844,7 +2886,8 @@ static void bond_info_show_master(struct seq_file *seq)
(curr) ? curr->dev->name : "None");
}
- seq_printf(seq, "MII Status: %s\n", (curr) ? "up" : "down");
+ seq_printf(seq, "MII Status: %s\n", netif_carrier_ok(bond->dev) ?
+ "up" : "down");
seq_printf(seq, "MII Polling Interval (ms): %d\n", bond->params.miimon);
seq_printf(seq, "Up Delay (ms): %d\n",
bond->params.updelay * bond->params.miimon);
@@ -4531,6 +4574,8 @@ int bond_create(char *name, struct bond_params *params, struct bonding **newbond
if (newbond)
*newbond = bond_dev->priv;
+ netif_carrier_off(bond_dev);
+
rtnl_unlock(); /* allows sysfs registration of net device */
res = bond_create_sysfs_entry(bond_dev->priv);
goto done;
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index ce9dc9b4e2dc..0bdfe2c71453 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -22,8 +22,8 @@
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "3.0.2"
-#define DRV_RELDATE "February 21, 2006"
+#define DRV_VERSION "3.0.3"
+#define DRV_RELDATE "March 23, 2006"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
diff --git a/drivers/net/ixp2000/ixpdev.c b/drivers/net/ixp2000/ixpdev.c
index 77f104a005f3..fbc2d21020f4 100644
--- a/drivers/net/ixp2000/ixpdev.c
+++ b/drivers/net/ixp2000/ixpdev.c
@@ -299,10 +299,7 @@ int ixpdev_init(int __nds_count, struct net_device **__nds,
int i;
int err;
- if (RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192) {
- static void __too_many_rx_or_tx_buffers(void);
- __too_many_rx_or_tx_buffers();
- }
+ BUILD_BUG_ON(RX_BUF_COUNT > 192 || TX_BUF_COUNT > 192);
printk(KERN_INFO "IXP2000 MSF ethernet driver %s\n", DRV_MODULE_VERSION);
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 8d4999837b65..7826afbb9db9 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -226,7 +226,7 @@ static int full_duplex[MAX_UNITS];
NATSEMI_PG1_NREGS)
#define NATSEMI_REGS_VER 1 /* v1 added RFDR registers */
#define NATSEMI_REGS_SIZE (NATSEMI_NREGS * sizeof(u32))
-#define NATSEMI_EEPROM_SIZE 24 /* 12 16-bit values */
+#define NATSEMI_DEF_EEPROM_SIZE 24 /* 12 16-bit values */
/* Buffer sizes:
* The nic writes 32-bit values, even if the upper bytes of
@@ -714,6 +714,8 @@ struct netdev_private {
unsigned int iosize;
spinlock_t lock;
u32 msg_enable;
+ /* EEPROM data */
+ int eeprom_size;
};
static void move_int_phy(struct net_device *dev, int addr);
@@ -890,6 +892,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;
np->hands_off = 0;
np->intr_status = 0;
+ np->eeprom_size = NATSEMI_DEF_EEPROM_SIZE;
/* Initial port:
* - If the nic was configured to use an external phy and if find_mii
@@ -2582,7 +2585,8 @@ static int get_regs_len(struct net_device *dev)
static int get_eeprom_len(struct net_device *dev)
{
- return NATSEMI_EEPROM_SIZE;
+ struct netdev_private *np = netdev_priv(dev);
+ return np->eeprom_size;
}
static int get_settings(struct net_device *dev, struct ethtool_cmd *ecmd)
@@ -2669,15 +2673,20 @@ static u32 get_link(struct net_device *dev)
static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, u8 *data)
{
struct netdev_private *np = netdev_priv(dev);
- u8 eebuf[NATSEMI_EEPROM_SIZE];
+ u8 *eebuf;
int res;
+ eebuf = kmalloc(np->eeprom_size, GFP_KERNEL);
+ if (!eebuf)
+ return -ENOMEM;
+
eeprom->magic = PCI_VENDOR_ID_NS | (PCI_DEVICE_ID_NS_83815<<16);
spin_lock_irq(&np->lock);
res = netdev_get_eeprom(dev, eebuf);
spin_unlock_irq(&np->lock);
if (!res)
memcpy(data, eebuf+eeprom->offset, eeprom->len);
+ kfree(eebuf);
return res;
}
@@ -3033,9 +3042,10 @@ static int netdev_get_eeprom(struct net_device *dev, u8 *buf)
int i;
u16 *ebuf = (u16 *)buf;
void __iomem * ioaddr = ns_ioaddr(dev);
+ struct netdev_private *np = netdev_priv(dev);
/* eeprom_read reads 16 bits, and indexes by 16 bits */
- for (i = 0; i < NATSEMI_EEPROM_SIZE/2; i++) {
+ for (i = 0; i < np->eeprom_size/2; i++) {
ebuf[i] = eeprom_read(ioaddr, i);
/* The EEPROM itself stores data bit-swapped, but eeprom_read
* reads it back "sanely". So we swap it back here in order to
diff --git a/drivers/net/netconsole.c b/drivers/net/netconsole.c
index edd1b5306b16..75b35ad760de 100644
--- a/drivers/net/netconsole.c
+++ b/drivers/net/netconsole.c
@@ -94,7 +94,7 @@ static struct console netconsole = {
static int option_setup(char *opt)
{
configured = !netpoll_parse_options(&np, opt);
- return 0;
+ return 1;
}
__setup("netconsole=", option_setup);
diff --git a/drivers/net/pcmcia/axnet_cs.c b/drivers/net/pcmcia/axnet_cs.c
index aa5581369399..1cc94b2d76c1 100644
--- a/drivers/net/pcmcia/axnet_cs.c
+++ b/drivers/net/pcmcia/axnet_cs.c
@@ -35,6 +35,7 @@
#include <linux/spinlock.h>
#include <linux/ethtool.h>
#include <linux/netdevice.h>
+#include <linux/crc32.h>
#include "../8390.h"
#include <pcmcia/cs_types.h>
@@ -1682,17 +1683,67 @@ static struct net_device_stats *get_stats(struct net_device *dev)
return &ei_local->stat;
}
+/*
+ * Form the 64 bit 8390 multicast table from the linked list of addresses
+ * associated with this dev structure.
+ */
+
+static inline void make_mc_bits(u8 *bits, struct net_device *dev)
+{
+ struct dev_mc_list *dmi;
+ u32 crc;
+
+ for (dmi=dev->mc_list; dmi; dmi=dmi->next) {
+
+ crc = ether_crc(ETH_ALEN, dmi->dmi_addr);
+ /*
+ * The 8390 uses the 6 most significant bits of the
+ * CRC to index the multicast table.
+ */
+ bits[crc>>29] |= (1<<((crc>>26)&7));
+ }
+}
+
/**
* do_set_multicast_list - set/clear multicast filter
* @dev: net device for which multicast filter is adjusted
*
- * Set or clear the multicast filter for this adaptor. May be called
- * from a BH in 2.1.x. Must be called with lock held.
+ * Set or clear the multicast filter for this adaptor.
+ * Must be called with lock held.
*/
static void do_set_multicast_list(struct net_device *dev)
{
long e8390_base = dev->base_addr;
+ int i;
+ struct ei_device *ei_local = (struct ei_device*)netdev_priv(dev);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI))) {
+ memset(ei_local->mcfilter, 0, 8);
+ if (dev->mc_list)
+ make_mc_bits(ei_local->mcfilter, dev);
+ } else {
+ /* set to accept-all */
+ memset(ei_local->mcfilter, 0xFF, 8);
+ }
+
+ /*
+ * DP8390 manuals don't specify any magic sequence for altering
+ * the multicast regs on an already running card. To be safe, we
+ * ensure multicast mode is off prior to loading up the new hash
+ * table. If this proves to be not enough, we can always resort
+ * to stopping the NIC, loading the table and then restarting.
+ */
+
+ if (netif_running(dev))
+ outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR);
+
+ outb_p(E8390_NODMA + E8390_PAGE1, e8390_base + E8390_CMD);
+ for(i = 0; i < 8; i++)
+ {
+ outb_p(ei_local->mcfilter[i], e8390_base + EN1_MULT_SHIFT(i));
+ }
+ outb_p(E8390_NODMA + E8390_PAGE0, e8390_base + E8390_CMD);
if(dev->flags&IFF_PROMISC)
outb_p(E8390_RXCONFIG | 0x58, e8390_base + EN0_RXCR);
@@ -1794,12 +1845,6 @@ static void AX88190_init(struct net_device *dev, int startp)
if(inb_p(e8390_base + EN1_PHYS_SHIFT(i))!=dev->dev_addr[i])
printk(KERN_ERR "Hw. address read/write mismap %d\n",i);
}
- /*
- * Initialize the multicast list to accept-all. If we enable multicast
- * the higher levels can do the filtering.
- */
- for (i = 0; i < 8; i++)
- outb_p(0xff, e8390_base + EN1_MULT + i);
outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
diff --git a/drivers/net/pcmcia/xirc2ps_cs.c b/drivers/net/pcmcia/xirc2ps_cs.c
index eed496803fe4..e8f849e12976 100644
--- a/drivers/net/pcmcia/xirc2ps_cs.c
+++ b/drivers/net/pcmcia/xirc2ps_cs.c
@@ -1973,7 +1973,7 @@ static int __init setup_xirc2ps_cs(char *str)
MAYBE_SET(lockup_hack, 6);
#undef MAYBE_SET
- return 0;
+ return 1;
}
__setup("xirc2ps_cs=", setup_xirc2ps_cs);
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index 9595f74da93f..07c31f19c6ba 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -1167,8 +1167,8 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev)
* station address PROM at the base address and programmed into the
* "Physical Address Registers" CSR12-14.
* As a precautionary measure, we read the PROM values and complain if
- * they disagree with the CSRs. Either way, we use the CSR values, and
- * double check that they are valid.
+ * they disagree with the CSRs. If they miscompare, and the PROM addr
+ * is valid, then the PROM addr is used.
*/
for (i = 0; i < 3; i++) {
unsigned int val;
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index 1f5975a61e1f..43f5e86fc559 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -1442,7 +1442,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
case SPIDER_NET_GRFAFLLINT: /* fallthrough */
case SPIDER_NET_GRMFLLINT:
if (netif_msg_intr(card) && net_ratelimit())
- pr_err("Spider RX RAM full, incoming packets "
+ pr_debug("Spider RX RAM full, incoming packets "
"might be discarded!\n");
spider_net_rx_irq_off(card);
tasklet_schedule(&card->rxram_full_tl);
@@ -2086,7 +2086,7 @@ spider_net_setup_netdev(struct spider_net_card *card)
spider_net_setup_netdev_ops(netdev);
- netdev->features = 0;
+ netdev->features = NETIF_F_HW_CSUM;
/* some time: NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
* NETIF_F_HW_VLAN_FILTER */
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index b5473325bff4..964c09644832 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -69,8 +69,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.54"
-#define DRV_MODULE_RELDATE "Mar 23, 2006"
+#define DRV_MODULE_VERSION "3.55"
+#define DRV_MODULE_RELDATE "Mar 27, 2006"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -497,21 +497,20 @@ static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
unsigned long flags;
spin_lock_irqsave(&tp->indirect_lock, flags);
- pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
- pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
+ if (tp->write32 != tg3_write_indirect_reg32) {
+ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off);
+ tw32_f(TG3PCI_MEM_WIN_DATA, val);
- /* Always leave this as zero. */
- pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
- spin_unlock_irqrestore(&tp->indirect_lock, flags);
-}
+ /* Always leave this as zero. */
+ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ } else {
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
-static void tg3_write_mem_fast(struct tg3 *tp, u32 off, u32 val)
-{
- /* If no workaround is needed, write to mem space directly */
- if (tp->write32 != tg3_write_indirect_reg32)
- tw32(NIC_SRAM_WIN_BASE + off, val);
- else
- tg3_write_mem(tp, off, val);
+ /* Always leave this as zero. */
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ }
+ spin_unlock_irqrestore(&tp->indirect_lock, flags);
}
static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
@@ -519,11 +518,19 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
unsigned long flags;
spin_lock_irqsave(&tp->indirect_lock, flags);
- pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
- pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
+ if (tp->write32 != tg3_write_indirect_reg32) {
+ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, off);
+ *val = tr32(TG3PCI_MEM_WIN_DATA);
- /* Always leave this as zero. */
- pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ /* Always leave this as zero. */
+ tw32_f(TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ } else {
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
+ pci_read_config_dword(tp->pdev, TG3PCI_MEM_WIN_DATA, val);
+
+ /* Always leave this as zero. */
+ pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, 0);
+ }
spin_unlock_irqrestore(&tp->indirect_lock, flags);
}
@@ -1367,12 +1374,12 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
}
}
+ tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
+
/* Finally, set the new power state. */
pci_write_config_word(tp->pdev, pm + PCI_PM_CTRL, power_control);
udelay(100); /* Delay after power state change */
- tg3_write_sig_post_reset(tp, RESET_KIND_SHUTDOWN);
-
return 0;
}
@@ -3600,7 +3607,7 @@ static inline int tg3_40bit_overflow_test(struct tg3 *tp, dma_addr_t mapping,
int len)
{
#if defined(CONFIG_HIGHMEM) && (BITS_PER_LONG == 64)
- if (tp->tg3_flags2 & TG3_FLG2_5780_CLASS)
+ if (tp->tg3_flags & TG3_FLAG_40BIT_DMA_BUG)
return (((u64) mapping + len) > DMA_40BIT_MASK);
return 0;
#else
@@ -6461,6 +6468,9 @@ static void tg3_timer(unsigned long __opaque)
{
struct tg3 *tp = (struct tg3 *) __opaque;
+ if (tp->irq_sync)
+ goto restart_timer;
+
spin_lock(&tp->lock);
if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS)) {
@@ -6537,11 +6547,11 @@ static void tg3_timer(unsigned long __opaque)
if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
u32 val;
- tg3_write_mem_fast(tp, NIC_SRAM_FW_CMD_MBOX,
- FWCMD_NICDRV_ALIVE2);
- tg3_write_mem_fast(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
+ FWCMD_NICDRV_ALIVE2);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
/* 5 seconds timeout */
- tg3_write_mem_fast(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
+ tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
val = tr32(GRC_RX_CPU_EVENT);
val |= (1 << 14);
tw32(GRC_RX_CPU_EVENT, val);
@@ -6551,6 +6561,7 @@ static void tg3_timer(unsigned long __opaque)
spin_unlock(&tp->lock);
+restart_timer:
tp->timer.expires = jiffies + tp->timer_offset;
add_timer(&tp->timer);
}
@@ -8399,8 +8410,11 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
}
mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
MAC_MODE_LINK_POLARITY | MAC_MODE_PORT_MODE_GMII;
- if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401)
+ if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
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);
}
else
@@ -10531,6 +10545,7 @@ static int __devinit tg3_get_device_address(struct tg3 *tp)
{
struct net_device *dev = tp->dev;
u32 hi, lo, mac_offset;
+ int addr_ok = 0;
#ifdef CONFIG_SPARC64
if (!tg3_get_macaddr_sparc(tp))
@@ -10560,29 +10575,34 @@ static int __devinit tg3_get_device_address(struct tg3 *tp)
dev->dev_addr[3] = (lo >> 16) & 0xff;
dev->dev_addr[4] = (lo >> 8) & 0xff;
dev->dev_addr[5] = (lo >> 0) & 0xff;
- }
- /* Next, try NVRAM. */
- else if (!(tp->tg3_flags & TG3_FLG2_SUN_570X) &&
- !tg3_nvram_read(tp, mac_offset + 0, &hi) &&
- !tg3_nvram_read(tp, mac_offset + 4, &lo)) {
- dev->dev_addr[0] = ((hi >> 16) & 0xff);
- dev->dev_addr[1] = ((hi >> 24) & 0xff);
- dev->dev_addr[2] = ((lo >> 0) & 0xff);
- dev->dev_addr[3] = ((lo >> 8) & 0xff);
- dev->dev_addr[4] = ((lo >> 16) & 0xff);
- dev->dev_addr[5] = ((lo >> 24) & 0xff);
- }
- /* Finally just fetch it out of the MAC control regs. */
- else {
- hi = tr32(MAC_ADDR_0_HIGH);
- lo = tr32(MAC_ADDR_0_LOW);
- dev->dev_addr[5] = lo & 0xff;
- dev->dev_addr[4] = (lo >> 8) & 0xff;
- dev->dev_addr[3] = (lo >> 16) & 0xff;
- dev->dev_addr[2] = (lo >> 24) & 0xff;
- dev->dev_addr[1] = hi & 0xff;
- dev->dev_addr[0] = (hi >> 8) & 0xff;
+ /* Some old bootcode may report a 0 MAC address in SRAM */
+ addr_ok = is_valid_ether_addr(&dev->dev_addr[0]);
+ }
+ if (!addr_ok) {
+ /* Next, try NVRAM. */
+ if (!(tp->tg3_flags & TG3_FLG2_SUN_570X) &&
+ !tg3_nvram_read(tp, mac_offset + 0, &hi) &&
+ !tg3_nvram_read(tp, mac_offset + 4, &lo)) {
+ dev->dev_addr[0] = ((hi >> 16) & 0xff);
+ dev->dev_addr[1] = ((hi >> 24) & 0xff);
+ dev->dev_addr[2] = ((lo >> 0) & 0xff);
+ dev->dev_addr[3] = ((lo >> 8) & 0xff);
+ dev->dev_addr[4] = ((lo >> 16) & 0xff);
+ dev->dev_addr[5] = ((lo >> 24) & 0xff);
+ }
+ /* Finally just fetch it out of the MAC control regs. */
+ else {
+ hi = tr32(MAC_ADDR_0_HIGH);
+ lo = tr32(MAC_ADDR_0_LOW);
+
+ dev->dev_addr[5] = lo & 0xff;
+ dev->dev_addr[4] = (lo >> 8) & 0xff;
+ dev->dev_addr[3] = (lo >> 16) & 0xff;
+ dev->dev_addr[2] = (lo >> 24) & 0xff;
+ dev->dev_addr[1] = hi & 0xff;
+ dev->dev_addr[0] = (hi >> 8) & 0xff;
+ }
}
if (!is_valid_ether_addr(&dev->dev_addr[0])) {
diff --git a/drivers/net/tokenring/Kconfig b/drivers/net/tokenring/Kconfig
index e4cfc80b283b..99c4c1922f19 100644
--- a/drivers/net/tokenring/Kconfig
+++ b/drivers/net/tokenring/Kconfig
@@ -3,7 +3,7 @@
#
menu "Token Ring devices"
- depends on NETDEVICES
+ depends on NETDEVICES && !UML
# So far, we only have PCI, ISA, and MCA token ring devices
config TR
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index d1a86a080a65..f56094102042 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -4160,7 +4160,7 @@ get_hw_addr(struct net_device *dev)
** If the address starts with 00 a0, we have to bit-reverse
** each byte of the address.
*/
- if ( (_machine & _MACH_Pmac) &&
+ if ( machine_is(powermac) &&
(dev->dev_addr[0] == 0) &&
(dev->dev_addr[1] == 0xa0) )
{
diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c
index 241871589283..a9b2150909d6 100644
--- a/drivers/net/via-rhine.c
+++ b/drivers/net/via-rhine.c
@@ -1085,6 +1085,25 @@ static void rhine_check_media(struct net_device *dev, unsigned int init_media)
else
iowrite8(ioread8(ioaddr + ChipCmd1) & ~Cmd1FDuplex,
ioaddr + ChipCmd1);
+ if (debug > 1)
+ printk(KERN_INFO "%s: force_media %d, carrier %d\n", dev->name,
+ rp->mii_if.force_media, netif_carrier_ok(dev));
+}
+
+/* Called after status of force_media possibly changed */
+void rhine_set_carrier(struct mii_if_info *mii)
+{
+ if (mii->force_media) {
+ /* autoneg is off: Link is always assumed to be up */
+ if (!netif_carrier_ok(mii->dev))
+ netif_carrier_on(mii->dev);
+ }
+ else /* Let MMI library update carrier status */
+ rhine_check_media(mii->dev, 0);
+ if (debug > 1)
+ printk(KERN_INFO "%s: force_media %d, carrier %d\n",
+ mii->dev->name, mii->force_media,
+ netif_carrier_ok(mii->dev));
}
static void rhine_check_media_task(struct net_device *dev)
@@ -1782,6 +1801,7 @@ static int netdev_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
spin_lock_irq(&rp->lock);
rc = mii_ethtool_sset(&rp->mii_if, cmd);
spin_unlock_irq(&rp->lock);
+ rhine_set_carrier(&rp->mii_if);
return rc;
}
@@ -1869,6 +1889,7 @@ static int netdev_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
spin_lock_irq(&rp->lock);
rc = generic_mii_ioctl(&rp->mii_if, if_mii(rq), cmd, NULL);
spin_unlock_irq(&rp->lock);
+ rhine_set_carrier(&rp->mii_if);
return rc;
}
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index fd17aa8491b6..bad09ebdb50b 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -309,7 +309,10 @@ config APPLE_AIRPORT
Say Y here to support the Airport 802.11b wireless Ethernet hardware
built into the Macintosh iBook and other recent PowerPC-based
Macintosh machines. This is essentially a Lucent Orinoco card with
- a non-standard interface
+ a non-standard interface.
+
+ This driver does not support the Airport Extreme (802.11b/g). Use
+ the BCM43xx driver for Airport Extreme cards.
config PLX_HERMES
tristate "Hermes in PLX9052 based PCI adaptor support (Netgear MA301 etc.)"
@@ -353,7 +356,7 @@ config PCI_HERMES
config ATMEL
tristate "Atmel at76c50x chipset 802.11b support"
- depends on NET_RADIO
+ depends on NET_RADIO && (PCI || PCMCIA)
select FW_LOADER
select CRC32
---help---
@@ -401,6 +404,7 @@ config PCMCIA_HERMES
config PCMCIA_SPECTRUM
tristate "Symbol Spectrum24 Trilogy PCMCIA card support"
depends on NET_RADIO && PCMCIA && HERMES
+ select FW_LOADER
---help---
This is a driver for 802.11b cards using RAM-loadable Symbol
@@ -500,6 +504,7 @@ config PRISM54
will be called prism54.ko.
source "drivers/net/wireless/hostap/Kconfig"
+source "drivers/net/wireless/bcm43xx/Kconfig"
# yes, this works even when no drivers are selected
config NET_WIRELESS
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 3a6f7ba326ca..c86779879361 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel_cs.o
obj-$(CONFIG_PRISM54) += prism54/
obj-$(CONFIG_HOSTAP) += hostap/
+obj-$(CONFIG_BCM43XX) += bcm43xx/
# 16-bit wireless PCMCIA client drivers
obj-$(CONFIG_PCMCIA_RAYCS) += ray_cs.o
diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig
new file mode 100644
index 000000000000..418465600a77
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/Kconfig
@@ -0,0 +1,62 @@
+config BCM43XX
+ tristate "Broadcom BCM43xx wireless support"
+ depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
+ select FW_LOADER
+ ---help---
+ This is an experimental driver for the Broadcom 43xx wireless chip,
+ found in the Apple Airport Extreme and various other devices.
+
+config BCM43XX_DEBUG
+ bool "Broadcom BCM43xx debugging (RECOMMENDED)"
+ depends on BCM43XX
+ default y
+ ---help---
+ Broadcom 43xx debugging messages.
+ Say Y, because the driver is still very experimental and
+ this will help you get it running.
+
+config BCM43XX_DMA
+ bool
+config BCM43XX_PIO
+ bool
+
+choice
+ prompt "BCM43xx data transfer mode"
+ depends on BCM43XX
+ default BCM43XX_DMA_AND_PIO_MODE
+
+config BCM43XX_DMA_AND_PIO_MODE
+ bool "DMA + PIO"
+ select BCM43XX_DMA
+ select BCM43XX_PIO
+ ---help---
+ Include both, Direct Memory Access (DMA) and Programmed I/O (PIO)
+ data transfer modes.
+ The actually used mode is selectable through the module
+ parameter "pio". If the module parameter is pio=0, DMA is used.
+ Otherwise PIO is used. DMA is default.
+
+ If unsure, choose this option.
+
+config BCM43XX_DMA_MODE
+ bool "DMA (Direct Memory Access) only"
+ select BCM43XX_DMA
+ ---help---
+ Only include Direct Memory Access (DMA).
+ This reduces the size of the driver module, by omitting the PIO code.
+
+config BCM43XX_PIO_MODE
+ bool "PIO (Programmed I/O) only"
+ select BCM43XX_PIO
+ ---help---
+ Only include Programmed I/O (PIO).
+ This reduces the size of the driver module, by omitting the DMA code.
+ Please note that PIO transfers are slow (compared to DMA).
+
+ Also note that not all devices of the 43xx series support PIO.
+ The 4306 (Apple Airport Extreme and others) supports PIO, while
+ the 4318 is known to _not_ support PIO.
+
+ Only use PIO, if DMA does not work for you.
+
+endchoice
diff --git a/drivers/net/wireless/bcm43xx/Makefile b/drivers/net/wireless/bcm43xx/Makefile
new file mode 100644
index 000000000000..bb5220c629d2
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/Makefile
@@ -0,0 +1,12 @@
+obj-$(CONFIG_BCM43XX) += bcm43xx.o
+bcm43xx-obj-$(CONFIG_BCM43XX_DEBUG) += bcm43xx_debugfs.o
+
+bcm43xx-obj-$(CONFIG_BCM43XX_DMA) += bcm43xx_dma.o
+bcm43xx-obj-$(CONFIG_BCM43XX_PIO) += bcm43xx_pio.o
+
+bcm43xx-objs := bcm43xx_main.o bcm43xx_ilt.o \
+ bcm43xx_radio.o bcm43xx_phy.o \
+ bcm43xx_power.o bcm43xx_wx.o \
+ bcm43xx_leds.o bcm43xx_ethtool.o \
+ bcm43xx_xmit.o bcm43xx_sysfs.o \
+ $(bcm43xx-obj-y)
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
new file mode 100644
index 000000000000..dcadd295de4f
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -0,0 +1,926 @@
+#ifndef BCM43xx_H_
+#define BCM43xx_H_
+
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/stringify.h>
+#include <linux/pci.h>
+#include <net/ieee80211.h>
+#include <net/ieee80211softmac.h>
+#include <asm/atomic.h>
+#include <asm/io.h>
+
+
+#include "bcm43xx_debugfs.h"
+#include "bcm43xx_leds.h"
+#include "bcm43xx_sysfs.h"
+
+
+#define PFX KBUILD_MODNAME ": "
+
+#define BCM43xx_SWITCH_CORE_MAX_RETRIES 50
+#define BCM43xx_IRQWAIT_MAX_RETRIES 50
+
+#define BCM43xx_IO_SIZE 8192
+
+/* Active Core PCI Configuration Register. */
+#define BCM43xx_PCICFG_ACTIVE_CORE 0x80
+/* SPROM control register. */
+#define BCM43xx_PCICFG_SPROMCTL 0x88
+/* Interrupt Control PCI Configuration Register. (Only on PCI cores with rev >= 6) */
+#define BCM43xx_PCICFG_ICR 0x94
+
+/* MMIO offsets */
+#define BCM43xx_MMIO_DMA1_REASON 0x20
+#define BCM43xx_MMIO_DMA1_IRQ_MASK 0x24
+#define BCM43xx_MMIO_DMA2_REASON 0x28
+#define BCM43xx_MMIO_DMA2_IRQ_MASK 0x2C
+#define BCM43xx_MMIO_DMA3_REASON 0x30
+#define BCM43xx_MMIO_DMA3_IRQ_MASK 0x34
+#define BCM43xx_MMIO_DMA4_REASON 0x38
+#define BCM43xx_MMIO_DMA4_IRQ_MASK 0x3C
+#define BCM43xx_MMIO_STATUS_BITFIELD 0x120
+#define BCM43xx_MMIO_STATUS2_BITFIELD 0x124
+#define BCM43xx_MMIO_GEN_IRQ_REASON 0x128
+#define BCM43xx_MMIO_GEN_IRQ_MASK 0x12C
+#define BCM43xx_MMIO_RAM_CONTROL 0x130
+#define BCM43xx_MMIO_RAM_DATA 0x134
+#define BCM43xx_MMIO_PS_STATUS 0x140
+#define BCM43xx_MMIO_RADIO_HWENABLED_HI 0x158
+#define BCM43xx_MMIO_SHM_CONTROL 0x160
+#define BCM43xx_MMIO_SHM_DATA 0x164
+#define BCM43xx_MMIO_SHM_DATA_UNALIGNED 0x166
+#define BCM43xx_MMIO_XMITSTAT_0 0x170
+#define BCM43xx_MMIO_XMITSTAT_1 0x174
+#define BCM43xx_MMIO_REV3PLUS_TSF_LOW 0x180 /* core rev >= 3 only */
+#define BCM43xx_MMIO_REV3PLUS_TSF_HIGH 0x184 /* core rev >= 3 only */
+#define BCM43xx_MMIO_DMA1_BASE 0x200
+#define BCM43xx_MMIO_DMA2_BASE 0x220
+#define BCM43xx_MMIO_DMA3_BASE 0x240
+#define BCM43xx_MMIO_DMA4_BASE 0x260
+#define BCM43xx_MMIO_PIO1_BASE 0x300
+#define BCM43xx_MMIO_PIO2_BASE 0x310
+#define BCM43xx_MMIO_PIO3_BASE 0x320
+#define BCM43xx_MMIO_PIO4_BASE 0x330
+#define BCM43xx_MMIO_PHY_VER 0x3E0
+#define BCM43xx_MMIO_PHY_RADIO 0x3E2
+#define BCM43xx_MMIO_ANTENNA 0x3E8
+#define BCM43xx_MMIO_CHANNEL 0x3F0
+#define BCM43xx_MMIO_CHANNEL_EXT 0x3F4
+#define BCM43xx_MMIO_RADIO_CONTROL 0x3F6
+#define BCM43xx_MMIO_RADIO_DATA_HIGH 0x3F8
+#define BCM43xx_MMIO_RADIO_DATA_LOW 0x3FA
+#define BCM43xx_MMIO_PHY_CONTROL 0x3FC
+#define BCM43xx_MMIO_PHY_DATA 0x3FE
+#define BCM43xx_MMIO_MACFILTER_CONTROL 0x420
+#define BCM43xx_MMIO_MACFILTER_DATA 0x422
+#define BCM43xx_MMIO_RADIO_HWENABLED_LO 0x49A
+#define BCM43xx_MMIO_GPIO_CONTROL 0x49C
+#define BCM43xx_MMIO_GPIO_MASK 0x49E
+#define BCM43xx_MMIO_TSF_0 0x632 /* core rev < 3 only */
+#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */
+#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */
+#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */
+#define BCM43xx_MMIO_POWERUP_DELAY 0x6A8
+
+/* SPROM offsets. */
+#define BCM43xx_SPROM_BASE 0x1000
+#define BCM43xx_SPROM_BOARDFLAGS2 0x1c
+#define BCM43xx_SPROM_IL0MACADDR 0x24
+#define BCM43xx_SPROM_ET0MACADDR 0x27
+#define BCM43xx_SPROM_ET1MACADDR 0x2a
+#define BCM43xx_SPROM_ETHPHY 0x2d
+#define BCM43xx_SPROM_BOARDREV 0x2e
+#define BCM43xx_SPROM_PA0B0 0x2f
+#define BCM43xx_SPROM_PA0B1 0x30
+#define BCM43xx_SPROM_PA0B2 0x31
+#define BCM43xx_SPROM_WL0GPIO0 0x32
+#define BCM43xx_SPROM_WL0GPIO2 0x33
+#define BCM43xx_SPROM_MAXPWR 0x34
+#define BCM43xx_SPROM_PA1B0 0x35
+#define BCM43xx_SPROM_PA1B1 0x36
+#define BCM43xx_SPROM_PA1B2 0x37
+#define BCM43xx_SPROM_IDL_TSSI_TGT 0x38
+#define BCM43xx_SPROM_BOARDFLAGS 0x39
+#define BCM43xx_SPROM_ANTENNA_GAIN 0x3a
+#define BCM43xx_SPROM_VERSION 0x3f
+
+/* BCM43xx_SPROM_BOARDFLAGS values */
+#define BCM43xx_BFL_BTCOEXIST 0x0001 /* implements Bluetooth coexistance */
+#define BCM43xx_BFL_PACTRL 0x0002 /* GPIO 9 controlling the PA */
+#define BCM43xx_BFL_AIRLINEMODE 0x0004 /* implements GPIO 13 radio disable indication */
+#define BCM43xx_BFL_RSSI 0x0008 /* software calculates nrssi slope. */
+#define BCM43xx_BFL_ENETSPI 0x0010 /* has ephy roboswitch spi */
+#define BCM43xx_BFL_XTAL_NOSLOW 0x0020 /* no slow clock available */
+#define BCM43xx_BFL_CCKHIPWR 0x0040 /* can do high power CCK transmission */
+#define BCM43xx_BFL_ENETADM 0x0080 /* has ADMtek switch */
+#define BCM43xx_BFL_ENETVLAN 0x0100 /* can do vlan */
+#define BCM43xx_BFL_AFTERBURNER 0x0200 /* supports Afterburner mode */
+#define BCM43xx_BFL_NOPCI 0x0400 /* leaves PCI floating */
+#define BCM43xx_BFL_FEM 0x0800 /* supports the Front End Module */
+#define BCM43xx_BFL_EXTLNA 0x1000 /* has an external LNA */
+#define BCM43xx_BFL_HGPA 0x2000 /* had high gain PA */
+#define BCM43xx_BFL_BTCMOD 0x4000 /* BFL_BTCOEXIST is given in alternate GPIOs */
+#define BCM43xx_BFL_ALTIQ 0x8000 /* alternate I/Q settings */
+
+/* GPIO register offset, in both ChipCommon and PCI core. */
+#define BCM43xx_GPIO_CONTROL 0x6c
+
+/* SHM Routing */
+#define BCM43xx_SHM_SHARED 0x0001
+#define BCM43xx_SHM_WIRELESS 0x0002
+#define BCM43xx_SHM_PCM 0x0003
+#define BCM43xx_SHM_HWMAC 0x0004
+#define BCM43xx_SHM_UCODE 0x0300
+
+/* MacFilter offsets. */
+#define BCM43xx_MACFILTER_SELF 0x0000
+#define BCM43xx_MACFILTER_ASSOC 0x0003
+
+/* Chipcommon registers. */
+#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04
+#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0
+#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4
+#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8
+#define BCM43xx_CHIPCOMMON_SYSCLKCTL 0xC0
+
+/* PCI core specific registers. */
+#define BCM43xx_PCICORE_BCAST_ADDR 0x50
+#define BCM43xx_PCICORE_BCAST_DATA 0x54
+#define BCM43xx_PCICORE_SBTOPCI2 0x108
+
+/* SBTOPCI2 values. */
+#define BCM43xx_SBTOPCI2_PREFETCH 0x4
+#define BCM43xx_SBTOPCI2_BURST 0x8
+
+/* Chipcommon capabilities. */
+#define BCM43xx_CAPABILITIES_PCTL 0x00040000
+#define BCM43xx_CAPABILITIES_PLLMASK 0x00030000
+#define BCM43xx_CAPABILITIES_PLLSHIFT 16
+#define BCM43xx_CAPABILITIES_FLASHMASK 0x00000700
+#define BCM43xx_CAPABILITIES_FLASHSHIFT 8
+#define BCM43xx_CAPABILITIES_EXTBUSPRESENT 0x00000040
+#define BCM43xx_CAPABILITIES_UARTGPIO 0x00000020
+#define BCM43xx_CAPABILITIES_UARTCLOCKMASK 0x00000018
+#define BCM43xx_CAPABILITIES_UARTCLOCKSHIFT 3
+#define BCM43xx_CAPABILITIES_MIPSBIGENDIAN 0x00000004
+#define BCM43xx_CAPABILITIES_NRUARTSMASK 0x00000003
+
+/* PowerControl */
+#define BCM43xx_PCTL_IN 0xB0
+#define BCM43xx_PCTL_OUT 0xB4
+#define BCM43xx_PCTL_OUTENABLE 0xB8
+#define BCM43xx_PCTL_XTAL_POWERUP 0x40
+#define BCM43xx_PCTL_PLL_POWERDOWN 0x80
+
+/* PowerControl Clock Modes */
+#define BCM43xx_PCTL_CLK_FAST 0x00
+#define BCM43xx_PCTL_CLK_SLOW 0x01
+#define BCM43xx_PCTL_CLK_DYNAMIC 0x02
+
+#define BCM43xx_PCTL_FORCE_SLOW 0x0800
+#define BCM43xx_PCTL_FORCE_PLL 0x1000
+#define BCM43xx_PCTL_DYN_XTAL 0x2000
+
+/* COREIDs */
+#define BCM43xx_COREID_CHIPCOMMON 0x800
+#define BCM43xx_COREID_ILINE20 0x801
+#define BCM43xx_COREID_SDRAM 0x803
+#define BCM43xx_COREID_PCI 0x804
+#define BCM43xx_COREID_MIPS 0x805
+#define BCM43xx_COREID_ETHERNET 0x806
+#define BCM43xx_COREID_V90 0x807
+#define BCM43xx_COREID_USB11_HOSTDEV 0x80a
+#define BCM43xx_COREID_IPSEC 0x80b
+#define BCM43xx_COREID_PCMCIA 0x80d
+#define BCM43xx_COREID_EXT_IF 0x80f
+#define BCM43xx_COREID_80211 0x812
+#define BCM43xx_COREID_MIPS_3302 0x816
+#define BCM43xx_COREID_USB11_HOST 0x817
+#define BCM43xx_COREID_USB11_DEV 0x818
+#define BCM43xx_COREID_USB20_HOST 0x819
+#define BCM43xx_COREID_USB20_DEV 0x81a
+#define BCM43xx_COREID_SDIO_HOST 0x81b
+
+/* Core Information Registers */
+#define BCM43xx_CIR_BASE 0xf00
+#define BCM43xx_CIR_SBTPSFLAG (BCM43xx_CIR_BASE + 0x18)
+#define BCM43xx_CIR_SBIMSTATE (BCM43xx_CIR_BASE + 0x90)
+#define BCM43xx_CIR_SBINTVEC (BCM43xx_CIR_BASE + 0x94)
+#define BCM43xx_CIR_SBTMSTATELOW (BCM43xx_CIR_BASE + 0x98)
+#define BCM43xx_CIR_SBTMSTATEHIGH (BCM43xx_CIR_BASE + 0x9c)
+#define BCM43xx_CIR_SBIMCONFIGLOW (BCM43xx_CIR_BASE + 0xa8)
+#define BCM43xx_CIR_SB_ID_HI (BCM43xx_CIR_BASE + 0xfc)
+
+/* Mask to get the Backplane Flag Number from SBTPSFLAG. */
+#define BCM43xx_BACKPLANE_FLAG_NR_MASK 0x3f
+
+/* SBIMCONFIGLOW values/masks. */
+#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK 0x00000007
+#define BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT 0
+#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK 0x00000070
+#define BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT 4
+#define BCM43xx_SBIMCONFIGLOW_CONNID_MASK 0x00ff0000
+#define BCM43xx_SBIMCONFIGLOW_CONNID_SHIFT 16
+
+/* sbtmstatelow state flags */
+#define BCM43xx_SBTMSTATELOW_RESET 0x01
+#define BCM43xx_SBTMSTATELOW_REJECT 0x02
+#define BCM43xx_SBTMSTATELOW_CLOCK 0x10000
+#define BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK 0x20000
+
+/* sbtmstatehigh state flags */
+#define BCM43xx_SBTMSTATEHIGH_SERROR 0x1
+#define BCM43xx_SBTMSTATEHIGH_BUSY 0x4
+
+/* sbimstate flags */
+#define BCM43xx_SBIMSTATE_IB_ERROR 0x20000
+#define BCM43xx_SBIMSTATE_TIMEOUT 0x40000
+
+/* PHYVersioning */
+#define BCM43xx_PHYTYPE_A 0x00
+#define BCM43xx_PHYTYPE_B 0x01
+#define BCM43xx_PHYTYPE_G 0x02
+
+/* PHYRegisters */
+#define BCM43xx_PHY_ILT_A_CTRL 0x0072
+#define BCM43xx_PHY_ILT_A_DATA1 0x0073
+#define BCM43xx_PHY_ILT_A_DATA2 0x0074
+#define BCM43xx_PHY_G_LO_CONTROL 0x0810
+#define BCM43xx_PHY_ILT_G_CTRL 0x0472
+#define BCM43xx_PHY_ILT_G_DATA1 0x0473
+#define BCM43xx_PHY_ILT_G_DATA2 0x0474
+#define BCM43xx_PHY_A_PCTL 0x007B
+#define BCM43xx_PHY_G_PCTL 0x0029
+#define BCM43xx_PHY_A_CRS 0x0029
+#define BCM43xx_PHY_RADIO_BITFIELD 0x0401
+#define BCM43xx_PHY_G_CRS 0x0429
+#define BCM43xx_PHY_NRSSILT_CTRL 0x0803
+#define BCM43xx_PHY_NRSSILT_DATA 0x0804
+
+/* RadioRegisters */
+#define BCM43xx_RADIOCTL_ID 0x01
+
+/* StatusBitField */
+#define BCM43xx_SBF_MAC_ENABLED 0x00000001
+#define BCM43xx_SBF_2 0x00000002 /*FIXME: fix name*/
+#define BCM43xx_SBF_CORE_READY 0x00000004
+#define BCM43xx_SBF_400 0x00000400 /*FIXME: fix name*/
+#define BCM43xx_SBF_4000 0x00004000 /*FIXME: fix name*/
+#define BCM43xx_SBF_8000 0x00008000 /*FIXME: fix name*/
+#define BCM43xx_SBF_XFER_REG_BYTESWAP 0x00010000
+#define BCM43xx_SBF_MODE_NOTADHOC 0x00020000
+#define BCM43xx_SBF_MODE_AP 0x00040000
+#define BCM43xx_SBF_RADIOREG_LOCK 0x00080000
+#define BCM43xx_SBF_MODE_MONITOR 0x00400000
+#define BCM43xx_SBF_MODE_PROMISC 0x01000000
+#define BCM43xx_SBF_PS1 0x02000000
+#define BCM43xx_SBF_PS2 0x04000000
+#define BCM43xx_SBF_NO_SSID_BCAST 0x08000000
+#define BCM43xx_SBF_TIME_UPDATE 0x10000000
+#define BCM43xx_SBF_80000000 0x80000000 /*FIXME: fix name*/
+
+/* MicrocodeFlagsBitfield (addr + lo-word values?)*/
+#define BCM43xx_UCODEFLAGS_OFFSET 0x005E
+
+#define BCM43xx_UCODEFLAG_AUTODIV 0x0001
+#define BCM43xx_UCODEFLAG_UNKBGPHY 0x0002
+#define BCM43xx_UCODEFLAG_UNKBPHY 0x0004
+#define BCM43xx_UCODEFLAG_UNKGPHY 0x0020
+#define BCM43xx_UCODEFLAG_UNKPACTRL 0x0040
+#define BCM43xx_UCODEFLAG_JAPAN 0x0080
+
+/* Generic-Interrupt reasons. */
+#define BCM43xx_IRQ_READY (1 << 0)
+#define BCM43xx_IRQ_BEACON (1 << 1)
+#define BCM43xx_IRQ_PS (1 << 2)
+#define BCM43xx_IRQ_REG124 (1 << 5)
+#define BCM43xx_IRQ_PMQ (1 << 6)
+#define BCM43xx_IRQ_PIO_WORKAROUND (1 << 8)
+#define BCM43xx_IRQ_XMIT_ERROR (1 << 11)
+#define BCM43xx_IRQ_RX (1 << 15)
+#define BCM43xx_IRQ_SCAN (1 << 16)
+#define BCM43xx_IRQ_NOISE (1 << 18)
+#define BCM43xx_IRQ_XMIT_STATUS (1 << 29)
+
+#define BCM43xx_IRQ_ALL 0xffffffff
+#define BCM43xx_IRQ_INITIAL (BCM43xx_IRQ_PS | \
+ BCM43xx_IRQ_REG124 | \
+ BCM43xx_IRQ_PMQ | \
+ BCM43xx_IRQ_XMIT_ERROR | \
+ BCM43xx_IRQ_RX | \
+ BCM43xx_IRQ_SCAN | \
+ BCM43xx_IRQ_NOISE | \
+ BCM43xx_IRQ_XMIT_STATUS)
+
+
+/* Initial default iw_mode */
+#define BCM43xx_INITIAL_IWMODE IW_MODE_INFRA
+
+/* Bus type PCI. */
+#define BCM43xx_BUSTYPE_PCI 0
+/* Bus type Silicone Backplane Bus. */
+#define BCM43xx_BUSTYPE_SB 1
+/* Bus type PCMCIA. */
+#define BCM43xx_BUSTYPE_PCMCIA 2
+
+/* Threshold values. */
+#define BCM43xx_MIN_RTS_THRESHOLD 1U
+#define BCM43xx_MAX_RTS_THRESHOLD 2304U
+#define BCM43xx_DEFAULT_RTS_THRESHOLD BCM43xx_MAX_RTS_THRESHOLD
+
+#define BCM43xx_DEFAULT_SHORT_RETRY_LIMIT 7
+#define BCM43xx_DEFAULT_LONG_RETRY_LIMIT 4
+
+/* Max size of a security key */
+#define BCM43xx_SEC_KEYSIZE 16
+/* Security algorithms. */
+enum {
+ BCM43xx_SEC_ALGO_NONE = 0, /* unencrypted, as of TX header. */
+ BCM43xx_SEC_ALGO_WEP,
+ BCM43xx_SEC_ALGO_UNKNOWN,
+ BCM43xx_SEC_ALGO_AES,
+ BCM43xx_SEC_ALGO_WEP104,
+ BCM43xx_SEC_ALGO_TKIP,
+};
+
+#ifdef assert
+# undef assert
+#endif
+#ifdef CONFIG_BCM43XX_DEBUG
+#define assert(expr) \
+ do { \
+ if (unlikely(!(expr))) { \
+ printk(KERN_ERR PFX "ASSERTION FAILED (%s) at: %s:%d:%s()\n", \
+ #expr, __FILE__, __LINE__, __FUNCTION__); \
+ } \
+ } while (0)
+#else
+#define assert(expr) do { /* nothing */ } while (0)
+#endif
+
+/* rate limited printk(). */
+#ifdef printkl
+# undef printkl
+#endif
+#define printkl(f, x...) do { if (printk_ratelimit()) printk(f ,##x); } while (0)
+/* rate limited printk() for debugging */
+#ifdef dprintkl
+# undef dprintkl
+#endif
+#ifdef CONFIG_BCM43XX_DEBUG
+# define dprintkl printkl
+#else
+# define dprintkl(f, x...) do { /* nothing */ } while (0)
+#endif
+
+/* Helper macro for if branches.
+ * An if branch marked with this macro is only taken in DEBUG mode.
+ * Example:
+ * if (DEBUG_ONLY(foo == bar)) {
+ * do something
+ * }
+ * In DEBUG mode, the branch will be taken if (foo == bar).
+ * In non-DEBUG mode, the branch will never be taken.
+ */
+#ifdef DEBUG_ONLY
+# undef DEBUG_ONLY
+#endif
+#ifdef CONFIG_BCM43XX_DEBUG
+# define DEBUG_ONLY(x) (x)
+#else
+# define DEBUG_ONLY(x) 0
+#endif
+
+/* debugging printk() */
+#ifdef dprintk
+# undef dprintk
+#endif
+#ifdef CONFIG_BCM43XX_DEBUG
+# define dprintk(f, x...) do { printk(f ,##x); } while (0)
+#else
+# define dprintk(f, x...) do { /* nothing */ } while (0)
+#endif
+
+
+struct net_device;
+struct pci_dev;
+struct bcm43xx_dmaring;
+struct bcm43xx_pioqueue;
+
+struct bcm43xx_initval {
+ u16 offset;
+ u16 size;
+ u32 value;
+} __attribute__((__packed__));
+
+/* Values for bcm430x_sprominfo.locale */
+enum {
+ BCM43xx_LOCALE_WORLD = 0,
+ BCM43xx_LOCALE_THAILAND,
+ BCM43xx_LOCALE_ISRAEL,
+ BCM43xx_LOCALE_JORDAN,
+ BCM43xx_LOCALE_CHINA,
+ BCM43xx_LOCALE_JAPAN,
+ BCM43xx_LOCALE_USA_CANADA_ANZ,
+ BCM43xx_LOCALE_EUROPE,
+ BCM43xx_LOCALE_USA_LOW,
+ BCM43xx_LOCALE_JAPAN_HIGH,
+ BCM43xx_LOCALE_ALL,
+ BCM43xx_LOCALE_NONE,
+};
+
+#define BCM43xx_SPROM_SIZE 64 /* in 16-bit words. */
+struct bcm43xx_sprominfo {
+ u16 boardflags2;
+ u8 il0macaddr[6];
+ u8 et0macaddr[6];
+ u8 et1macaddr[6];
+ u8 et0phyaddr:5;
+ u8 et1phyaddr:5;
+ u8 et0mdcport:1;
+ u8 et1mdcport:1;
+ u8 boardrev;
+ u8 locale:4;
+ u8 antennas_aphy:2;
+ u8 antennas_bgphy:2;
+ u16 pa0b0;
+ u16 pa0b1;
+ u16 pa0b2;
+ u8 wl0gpio0;
+ u8 wl0gpio1;
+ u8 wl0gpio2;
+ u8 wl0gpio3;
+ u8 maxpower_aphy;
+ u8 maxpower_bgphy;
+ u16 pa1b0;
+ u16 pa1b1;
+ u16 pa1b2;
+ u8 idle_tssi_tgt_aphy;
+ u8 idle_tssi_tgt_bgphy;
+ u16 boardflags;
+ u16 antennagain_aphy;
+ u16 antennagain_bgphy;
+};
+
+/* Value pair to measure the LocalOscillator. */
+struct bcm43xx_lopair {
+ s8 low;
+ s8 high;
+ u8 used:1;
+};
+#define BCM43xx_LO_COUNT (14*4)
+
+struct bcm43xx_phyinfo {
+ /* Hardware Data */
+ u8 version;
+ u8 type;
+ u8 rev;
+ u16 antenna_diversity;
+ u16 savedpctlreg;
+ u16 minlowsig[2];
+ u16 minlowsigpos[2];
+ u8 connected:1,
+ calibrated:1,
+ is_locked:1, /* used in bcm43xx_phy_{un}lock() */
+ dyn_tssi_tbl:1; /* used in bcm43xx_phy_init_tssi2dbm_table() */
+ /* LO Measurement Data.
+ * Use bcm43xx_get_lopair() to get a value.
+ */
+ struct bcm43xx_lopair *_lo_pairs;
+
+ /* TSSI to dBm table in use */
+ const s8 *tssi2dbm;
+ /* idle TSSI value */
+ s8 idle_tssi;
+
+ /* Values from bcm43xx_calc_loopback_gain() */
+ u16 loopback_gain[2];
+
+ /* PHY lock for core.rev < 3
+ * This lock is only used by bcm43xx_phy_{un}lock()
+ */
+ spinlock_t lock;
+};
+
+
+struct bcm43xx_radioinfo {
+ u16 manufact;
+ u16 version;
+ u8 revision;
+
+ /* Desired TX power in dBm Q5.2 */
+ u16 txpower_desired;
+ /* TX Power control values. */
+ union {
+ /* B/G PHY */
+ struct {
+ u16 baseband_atten;
+ u16 radio_atten;
+ u16 txctl1;
+ u16 txctl2;
+ };
+ /* A PHY */
+ struct {
+ u16 txpwr_offset;
+ };
+ };
+
+ /* Current Interference Mitigation mode */
+ int interfmode;
+ /* Stack of saved values from the Interference Mitigation code.
+ * Each value in the stack is layed out as follows:
+ * bit 0-11: offset
+ * bit 12-15: register ID
+ * bit 16-32: value
+ * register ID is: 0x1 PHY, 0x2 Radio, 0x3 ILT
+ */
+#define BCM43xx_INTERFSTACK_SIZE 26
+ u32 interfstack[BCM43xx_INTERFSTACK_SIZE];
+
+ /* Saved values from the NRSSI Slope calculation */
+ s16 nrssi[2];
+ s32 nrssislope;
+ /* In memory nrssi lookup table. */
+ s8 nrssi_lt[64];
+
+ /* current channel */
+ u8 channel;
+ u8 initial_channel;
+
+ u16 lofcal;
+
+ u16 initval;
+
+ u8 enabled:1;
+ /* ACI (adjacent channel interference) flags. */
+ u8 aci_enable:1,
+ aci_wlan_automatic:1,
+ aci_hw_rssi:1;
+};
+
+/* Data structures for DMA transmission, per 80211 core. */
+struct bcm43xx_dma {
+ struct bcm43xx_dmaring *tx_ring0;
+ struct bcm43xx_dmaring *tx_ring1;
+ struct bcm43xx_dmaring *tx_ring2;
+ struct bcm43xx_dmaring *tx_ring3;
+ struct bcm43xx_dmaring *rx_ring0;
+ struct bcm43xx_dmaring *rx_ring1; /* only available on core.rev < 5 */
+};
+
+/* Data structures for PIO transmission, per 80211 core. */
+struct bcm43xx_pio {
+ struct bcm43xx_pioqueue *queue0;
+ struct bcm43xx_pioqueue *queue1;
+ struct bcm43xx_pioqueue *queue2;
+ struct bcm43xx_pioqueue *queue3;
+};
+
+#define BCM43xx_MAX_80211_CORES 2
+
+#ifdef CONFIG_BCM947XX
+#define core_offset(bcm) (bcm)->current_core_offset
+#else
+#define core_offset(bcm) 0
+#endif
+
+/* Generic information about a core. */
+struct bcm43xx_coreinfo {
+ u8 available:1,
+ enabled:1,
+ initialized:1;
+ /** core_id ID number */
+ u16 id;
+ /** core_rev revision number */
+ u8 rev;
+ /** Index number for _switch_core() */
+ u8 index;
+};
+
+/* Additional information for each 80211 core. */
+struct bcm43xx_coreinfo_80211 {
+ /* PHY device. */
+ struct bcm43xx_phyinfo phy;
+ /* Radio device. */
+ struct bcm43xx_radioinfo radio;
+ union {
+ /* DMA context. */
+ struct bcm43xx_dma dma;
+ /* PIO context. */
+ struct bcm43xx_pio pio;
+ };
+};
+
+/* Context information for a noise calculation (Link Quality). */
+struct bcm43xx_noise_calculation {
+ struct bcm43xx_coreinfo *core_at_start;
+ u8 channel_at_start;
+ u8 calculation_running:1;
+ u8 nr_samples;
+ s8 samples[8][4];
+};
+
+struct bcm43xx_stats {
+ u8 link_quality;
+ u8 noise;
+ struct iw_statistics wstats;
+ /* Store the last TX/RX times here for updating the leds. */
+ unsigned long last_tx;
+ unsigned long last_rx;
+};
+
+struct bcm43xx_key {
+ u8 enabled:1;
+ u8 algorithm;
+};
+
+struct bcm43xx_private {
+ struct bcm43xx_sysfs sysfs;
+
+ struct ieee80211_device *ieee;
+ struct ieee80211softmac_device *softmac;
+
+ struct net_device *net_dev;
+ struct pci_dev *pci_dev;
+ unsigned int irq;
+
+ void __iomem *mmio_addr;
+ unsigned int mmio_len;
+
+ /* Do not use the lock directly. Use the bcm43xx_lock* helper
+ * functions, to be MMIO-safe. */
+ spinlock_t _lock;
+
+ /* Driver status flags. */
+ u32 initialized:1, /* init_board() succeed */
+ was_initialized:1, /* for PCI suspend/resume. */
+ shutting_down:1, /* free_board() in progress */
+ __using_pio:1, /* Internal, use bcm43xx_using_pio(). */
+ bad_frames_preempt:1, /* Use "Bad Frames Preemption" (default off) */
+ reg124_set_0x4:1, /* Some variable to keep track of IRQ stuff. */
+ powersaving:1, /* TRUE if we are in PowerSaving mode. FALSE otherwise. */
+ short_preamble:1, /* TRUE, if short preamble is enabled. */
+ firmware_norelease:1; /* Do not release the firmware. Used on suspend. */
+
+ struct bcm43xx_stats stats;
+
+ /* Bus type we are connected to.
+ * This is currently always BCM43xx_BUSTYPE_PCI
+ */
+ u8 bustype;
+
+ u16 board_vendor;
+ u16 board_type;
+ u16 board_revision;
+
+ u16 chip_id;
+ u8 chip_rev;
+ u8 chip_package;
+
+ struct bcm43xx_sprominfo sprom;
+#define BCM43xx_NR_LEDS 4
+ struct bcm43xx_led leds[BCM43xx_NR_LEDS];
+
+ /* The currently active core. */
+ struct bcm43xx_coreinfo *current_core;
+#ifdef CONFIG_BCM947XX
+ /** current core memory offset */
+ u32 current_core_offset;
+#endif
+ struct bcm43xx_coreinfo *active_80211_core;
+ /* coreinfo structs for all possible cores follow.
+ * Note that a core might not exist.
+ * So check the coreinfo flags before using it.
+ */
+ struct bcm43xx_coreinfo core_chipcommon;
+ struct bcm43xx_coreinfo core_pci;
+ struct bcm43xx_coreinfo core_80211[ BCM43xx_MAX_80211_CORES ];
+ /* Additional information, specific to the 80211 cores. */
+ struct bcm43xx_coreinfo_80211 core_80211_ext[ BCM43xx_MAX_80211_CORES ];
+ /* Index of the current 80211 core. If current_core is not
+ * an 80211 core, this is -1.
+ */
+ int current_80211_core_idx;
+ /* Number of available 80211 cores. */
+ int nr_80211_available;
+
+ u32 chipcommon_capabilities;
+
+ /* Reason code of the last interrupt. */
+ u32 irq_reason;
+ u32 dma_reason[4];
+ /* saved irq enable/disable state bitfield. */
+ u32 irq_savedstate;
+ /* Link Quality calculation context. */
+ struct bcm43xx_noise_calculation noisecalc;
+
+ /* Threshold values. */
+ //TODO: The RTS thr has to be _used_. Currently, it is only set via WX.
+ u32 rts_threshold;
+
+ /* Interrupt Service Routine tasklet (bottom-half) */
+ struct tasklet_struct isr_tasklet;
+
+ /* Periodic tasks */
+ struct timer_list periodic_tasks;
+ unsigned int periodic_state;
+
+ struct work_struct restart_work;
+
+ /* Informational stuff. */
+ char nick[IW_ESSID_MAX_SIZE + 1];
+
+ /* encryption/decryption */
+ u16 security_offset;
+ struct bcm43xx_key key[54];
+ u8 default_key_idx;
+
+ /* Firmware. */
+ const struct firmware *ucode;
+ const struct firmware *pcm;
+ const struct firmware *initvals0;
+ const struct firmware *initvals1;
+
+ /* Debugging stuff follows. */
+#ifdef CONFIG_BCM43XX_DEBUG
+ struct bcm43xx_dfsentry *dfsentry;
+#endif
+};
+
+/* bcm43xx_(un)lock() protect struct bcm43xx_private.
+ * Note that _NO_ MMIO writes are allowed. If you want to
+ * write to the device through MMIO in the critical section, use
+ * the *_mmio lock functions.
+ * MMIO read-access is allowed, though.
+ */
+#define bcm43xx_lock(bcm, flags) spin_lock_irqsave(&(bcm)->_lock, flags)
+#define bcm43xx_unlock(bcm, flags) spin_unlock_irqrestore(&(bcm)->_lock, flags)
+/* bcm43xx_(un)lock_mmio() protect struct bcm43xx_private and MMIO.
+ * MMIO write-access to the device is allowed.
+ * All MMIO writes are flushed on unlock, so it is guaranteed to not
+ * interfere with other threads writing MMIO registers.
+ */
+#define bcm43xx_lock_mmio(bcm, flags) bcm43xx_lock(bcm, flags)
+#define bcm43xx_unlock_mmio(bcm, flags) do { mmiowb(); bcm43xx_unlock(bcm, flags); } while (0)
+
+static inline
+struct bcm43xx_private * bcm43xx_priv(struct net_device *dev)
+{
+ return ieee80211softmac_priv(dev);
+}
+
+
+/* Helper function, which returns a boolean.
+ * TRUE, if PIO is used; FALSE, if DMA is used.
+ */
+#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO)
+static inline
+int bcm43xx_using_pio(struct bcm43xx_private *bcm)
+{
+ return bcm->__using_pio;
+}
+#elif defined(CONFIG_BCM43XX_DMA)
+static inline
+int bcm43xx_using_pio(struct bcm43xx_private *bcm)
+{
+ return 0;
+}
+#elif defined(CONFIG_BCM43XX_PIO)
+static inline
+int bcm43xx_using_pio(struct bcm43xx_private *bcm)
+{
+ return 1;
+}
+#else
+# error "Using neither DMA nor PIO? Confused..."
+#endif
+
+/* Helper functions to access data structures private to the 80211 cores.
+ * Note that we _must_ have an 80211 core mapped when calling
+ * any of these functions.
+ */
+static inline
+struct bcm43xx_pio * bcm43xx_current_pio(struct bcm43xx_private *bcm)
+{
+ assert(bcm43xx_using_pio(bcm));
+ assert(bcm->current_80211_core_idx >= 0);
+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].pio);
+}
+static inline
+struct bcm43xx_dma * bcm43xx_current_dma(struct bcm43xx_private *bcm)
+{
+ assert(!bcm43xx_using_pio(bcm));
+ assert(bcm->current_80211_core_idx >= 0);
+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].dma);
+}
+static inline
+struct bcm43xx_phyinfo * bcm43xx_current_phy(struct bcm43xx_private *bcm)
+{
+ assert(bcm->current_80211_core_idx >= 0);
+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].phy);
+}
+static inline
+struct bcm43xx_radioinfo * bcm43xx_current_radio(struct bcm43xx_private *bcm)
+{
+ assert(bcm->current_80211_core_idx >= 0);
+ assert(bcm->current_80211_core_idx < BCM43xx_MAX_80211_CORES);
+ return &(bcm->core_80211_ext[bcm->current_80211_core_idx].radio);
+}
+
+/* Are we running in init_board() context? */
+static inline
+int bcm43xx_is_initializing(struct bcm43xx_private *bcm)
+{
+ if (bcm->initialized)
+ return 0;
+ if (bcm->shutting_down)
+ return 0;
+ return 1;
+}
+
+static inline
+struct bcm43xx_lopair * bcm43xx_get_lopair(struct bcm43xx_phyinfo *phy,
+ u16 radio_attenuation,
+ u16 baseband_attenuation)
+{
+ return phy->_lo_pairs + (radio_attenuation + 14 * (baseband_attenuation / 2));
+}
+
+
+static inline
+u16 bcm43xx_read16(struct bcm43xx_private *bcm, u16 offset)
+{
+ return ioread16(bcm->mmio_addr + core_offset(bcm) + offset);
+}
+
+static inline
+void bcm43xx_write16(struct bcm43xx_private *bcm, u16 offset, u16 value)
+{
+ iowrite16(value, bcm->mmio_addr + core_offset(bcm) + offset);
+}
+
+static inline
+u32 bcm43xx_read32(struct bcm43xx_private *bcm, u16 offset)
+{
+ return ioread32(bcm->mmio_addr + core_offset(bcm) + offset);
+}
+
+static inline
+void bcm43xx_write32(struct bcm43xx_private *bcm, u16 offset, u32 value)
+{
+ iowrite32(value, bcm->mmio_addr + core_offset(bcm) + offset);
+}
+
+static inline
+int bcm43xx_pci_read_config16(struct bcm43xx_private *bcm, int offset, u16 *value)
+{
+ return pci_read_config_word(bcm->pci_dev, offset, value);
+}
+
+static inline
+int bcm43xx_pci_read_config32(struct bcm43xx_private *bcm, int offset, u32 *value)
+{
+ return pci_read_config_dword(bcm->pci_dev, offset, value);
+}
+
+static inline
+int bcm43xx_pci_write_config16(struct bcm43xx_private *bcm, int offset, u16 value)
+{
+ return pci_write_config_word(bcm->pci_dev, offset, value);
+}
+
+static inline
+int bcm43xx_pci_write_config32(struct bcm43xx_private *bcm, int offset, u32 value)
+{
+ return pci_write_config_dword(bcm->pci_dev, offset, value);
+}
+
+/** Limit a value between two limits */
+#ifdef limit_value
+# undef limit_value
+#endif
+#define limit_value(value, min, max) \
+ ({ \
+ typeof(value) __value = (value); \
+ typeof(value) __min = (min); \
+ typeof(value) __max = (max); \
+ if (__value < __min) \
+ __value = __min; \
+ else if (__value > __max) \
+ __value = __max; \
+ __value; \
+ })
+
+/** Helpers to print MAC addresses. */
+#define BCM43xx_MACFMT "%02x:%02x:%02x:%02x:%02x:%02x"
+#define BCM43xx_MACARG(x) ((u8*)(x))[0], ((u8*)(x))[1], \
+ ((u8*)(x))[2], ((u8*)(x))[3], \
+ ((u8*)(x))[4], ((u8*)(x))[5]
+
+#endif /* BCM43xx_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
new file mode 100644
index 000000000000..d2c3401e9b70
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
@@ -0,0 +1,499 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ debugfs driver debugging code
+
+ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.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; 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/fs.h>
+#include <linux/debugfs.h>
+#include <linux/slab.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+#include "bcm43xx.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_debugfs.h"
+#include "bcm43xx_dma.h"
+#include "bcm43xx_pio.h"
+#include "bcm43xx_xmit.h"
+
+#define REALLY_BIG_BUFFER_SIZE (1024*256)
+
+static struct bcm43xx_debugfs fs;
+static char really_big_buffer[REALLY_BIG_BUFFER_SIZE];
+static DECLARE_MUTEX(big_buffer_sem);
+
+
+static ssize_t write_file_dummy(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return count;
+}
+
+static int open_file_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->u.generic_ip;
+ return 0;
+}
+
+#define fappend(fmt, x...) pos += snprintf(buf + pos, len - pos, fmt , ##x)
+
+static ssize_t devinfo_read_file(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ const size_t len = REALLY_BIG_BUFFER_SIZE;
+
+ struct bcm43xx_private *bcm = file->private_data;
+ char *buf = really_big_buffer;
+ size_t pos = 0;
+ ssize_t res;
+ struct net_device *net_dev;
+ struct pci_dev *pci_dev;
+ unsigned long flags;
+ u16 tmp16;
+ int i;
+
+ down(&big_buffer_sem);
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (!bcm->initialized) {
+ fappend("Board not initialized.\n");
+ goto out;
+ }
+ net_dev = bcm->net_dev;
+ pci_dev = bcm->pci_dev;
+
+ /* This is where the information is written to the "devinfo" file */
+ fappend("*** %s devinfo ***\n", net_dev->name);
+ fappend("vendor: 0x%04x device: 0x%04x\n",
+ pci_dev->vendor, pci_dev->device);
+ fappend("subsystem_vendor: 0x%04x subsystem_device: 0x%04x\n",
+ pci_dev->subsystem_vendor, pci_dev->subsystem_device);
+ fappend("IRQ: %d\n", bcm->irq);
+ fappend("mmio_addr: 0x%p mmio_len: %u\n", bcm->mmio_addr, bcm->mmio_len);
+ fappend("chip_id: 0x%04x chip_rev: 0x%02x\n", bcm->chip_id, bcm->chip_rev);
+ if ((bcm->core_80211[0].rev >= 3) && (bcm43xx_read32(bcm, 0x0158) & (1 << 16)))
+ fappend("Radio disabled by hardware!\n");
+ if ((bcm->core_80211[0].rev < 3) && !(bcm43xx_read16(bcm, 0x049A) & (1 << 4)))
+ fappend("Radio disabled by hardware!\n");
+ fappend("board_vendor: 0x%04x board_type: 0x%04x\n", bcm->board_vendor,
+ bcm->board_type);
+
+ fappend("\nCores:\n");
+#define fappend_core(name, info) fappend("core \"" name "\" %s, %s, id: 0x%04x, " \
+ "rev: 0x%02x, index: 0x%02x\n", \
+ (info).available \
+ ? "available" : "nonavailable", \
+ (info).enabled \
+ ? "enabled" : "disabled", \
+ (info).id, (info).rev, (info).index)
+ fappend_core("CHIPCOMMON", bcm->core_chipcommon);
+ fappend_core("PCI", bcm->core_pci);
+ fappend_core("first 80211", bcm->core_80211[0]);
+ fappend_core("second 80211", bcm->core_80211[1]);
+#undef fappend_core
+ tmp16 = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
+ fappend("LEDs: ");
+ for (i = 0; i < BCM43xx_NR_LEDS; i++)
+ fappend("%d ", !!(tmp16 & (1 << i)));
+ fappend("\n");
+
+out:
+ bcm43xx_unlock_mmio(bcm, flags);
+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ up(&big_buffer_sem);
+ return res;
+}
+
+static ssize_t drvinfo_read_file(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ const size_t len = REALLY_BIG_BUFFER_SIZE;
+
+ char *buf = really_big_buffer;
+ size_t pos = 0;
+ ssize_t res;
+
+ down(&big_buffer_sem);
+
+ /* This is where the information is written to the "driver" file */
+ fappend(KBUILD_MODNAME " driver\n");
+ fappend("Compiled at: %s %s\n", __DATE__, __TIME__);
+
+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ up(&big_buffer_sem);
+ return res;
+}
+
+static ssize_t spromdump_read_file(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ const size_t len = REALLY_BIG_BUFFER_SIZE;
+
+ struct bcm43xx_private *bcm = file->private_data;
+ char *buf = really_big_buffer;
+ size_t pos = 0;
+ ssize_t res;
+ unsigned long flags;
+
+ down(&big_buffer_sem);
+ bcm43xx_lock_mmio(bcm, flags);
+ if (!bcm->initialized) {
+ fappend("Board not initialized.\n");
+ goto out;
+ }
+
+ /* This is where the information is written to the "sprom_dump" file */
+ fappend("boardflags: 0x%04x\n", bcm->sprom.boardflags);
+
+out:
+ bcm43xx_unlock_mmio(bcm, flags);
+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ up(&big_buffer_sem);
+ return res;
+}
+
+static ssize_t tsf_read_file(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ const size_t len = REALLY_BIG_BUFFER_SIZE;
+
+ struct bcm43xx_private *bcm = file->private_data;
+ char *buf = really_big_buffer;
+ size_t pos = 0;
+ ssize_t res;
+ unsigned long flags;
+ u64 tsf;
+
+ down(&big_buffer_sem);
+ bcm43xx_lock_mmio(bcm, flags);
+ if (!bcm->initialized) {
+ fappend("Board not initialized.\n");
+ goto out;
+ }
+ bcm43xx_tsf_read(bcm, &tsf);
+ fappend("0x%08x%08x\n",
+ (unsigned int)((tsf & 0xFFFFFFFF00000000ULL) >> 32),
+ (unsigned int)(tsf & 0xFFFFFFFFULL));
+
+out:
+ bcm43xx_unlock_mmio(bcm, flags);
+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ up(&big_buffer_sem);
+ return res;
+}
+
+static ssize_t tsf_write_file(struct file *file, const char __user *user_buf,
+ size_t count, loff_t *ppos)
+{
+ struct bcm43xx_private *bcm = file->private_data;
+ char *buf = really_big_buffer;
+ ssize_t buf_size;
+ ssize_t res;
+ unsigned long flags;
+ u64 tsf;
+
+ buf_size = min(count, sizeof (really_big_buffer) - 1);
+ down(&big_buffer_sem);
+ if (copy_from_user(buf, user_buf, buf_size)) {
+ res = -EFAULT;
+ goto out_up;
+ }
+ bcm43xx_lock_mmio(bcm, flags);
+ if (!bcm->initialized) {
+ printk(KERN_INFO PFX "debugfs: Board not initialized.\n");
+ res = -EFAULT;
+ goto out_unlock;
+ }
+ if (sscanf(buf, "%lli", &tsf) != 1) {
+ printk(KERN_INFO PFX "debugfs: invalid values for \"tsf\"\n");
+ res = -EINVAL;
+ goto out_unlock;
+ }
+ bcm43xx_tsf_write(bcm, tsf);
+ res = buf_size;
+
+out_unlock:
+ bcm43xx_unlock_mmio(bcm, flags);
+out_up:
+ up(&big_buffer_sem);
+ return res;
+}
+
+static ssize_t txstat_read_file(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ const size_t len = REALLY_BIG_BUFFER_SIZE;
+
+ struct bcm43xx_private *bcm = file->private_data;
+ char *buf = really_big_buffer;
+ size_t pos = 0;
+ ssize_t res;
+ unsigned long flags;
+ struct bcm43xx_dfsentry *e;
+ struct bcm43xx_xmitstatus *status;
+ int i, cnt, j = 0;
+
+ down(&big_buffer_sem);
+ bcm43xx_lock(bcm, flags);
+
+ fappend("Last %d logged xmitstatus blobs (Latest first):\n\n",
+ BCM43xx_NR_LOGGED_XMITSTATUS);
+ e = bcm->dfsentry;
+ if (e->xmitstatus_printing == 0) {
+ /* At the beginning, make a copy of all data to avoid
+ * concurrency, as this function is called multiple
+ * times for big logs. Without copying, the data might
+ * change between reads. This would result in total trash.
+ */
+ e->xmitstatus_printing = 1;
+ e->saved_xmitstatus_ptr = e->xmitstatus_ptr;
+ e->saved_xmitstatus_cnt = e->xmitstatus_cnt;
+ memcpy(e->xmitstatus_print_buffer, e->xmitstatus_buffer,
+ BCM43xx_NR_LOGGED_XMITSTATUS * sizeof(*(e->xmitstatus_buffer)));
+ }
+ i = e->saved_xmitstatus_ptr - 1;
+ if (i < 0)
+ i = BCM43xx_NR_LOGGED_XMITSTATUS - 1;
+ cnt = e->saved_xmitstatus_cnt;
+ while (cnt) {
+ status = e->xmitstatus_print_buffer + i;
+ fappend("0x%02x: cookie: 0x%04x, flags: 0x%02x, "
+ "cnt1: 0x%02x, cnt2: 0x%02x, seq: 0x%04x, "
+ "unk: 0x%04x\n", j,
+ status->cookie, status->flags,
+ status->cnt1, status->cnt2, status->seq,
+ status->unknown);
+ j++;
+ cnt--;
+ i--;
+ if (i < 0)
+ i = BCM43xx_NR_LOGGED_XMITSTATUS - 1;
+ }
+
+ bcm43xx_unlock(bcm, flags);
+ res = simple_read_from_buffer(userbuf, count, ppos, buf, pos);
+ bcm43xx_lock(bcm, flags);
+ if (*ppos == pos) {
+ /* Done. Drop the copied data. */
+ e->xmitstatus_printing = 0;
+ }
+ bcm43xx_unlock(bcm, flags);
+ up(&big_buffer_sem);
+ return res;
+}
+
+#undef fappend
+
+
+static struct file_operations devinfo_fops = {
+ .read = devinfo_read_file,
+ .write = write_file_dummy,
+ .open = open_file_generic,
+};
+
+static struct file_operations spromdump_fops = {
+ .read = spromdump_read_file,
+ .write = write_file_dummy,
+ .open = open_file_generic,
+};
+
+static struct file_operations drvinfo_fops = {
+ .read = drvinfo_read_file,
+ .write = write_file_dummy,
+ .open = open_file_generic,
+};
+
+static struct file_operations tsf_fops = {
+ .read = tsf_read_file,
+ .write = tsf_write_file,
+ .open = open_file_generic,
+};
+
+static struct file_operations txstat_fops = {
+ .read = txstat_read_file,
+ .write = write_file_dummy,
+ .open = open_file_generic,
+};
+
+
+void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_dfsentry *e;
+ char devdir[IFNAMSIZ];
+
+ assert(bcm);
+ e = kzalloc(sizeof(*e), GFP_KERNEL);
+ if (!e) {
+ printk(KERN_ERR PFX "out of memory\n");
+ return;
+ }
+ e->bcm = bcm;
+ e->xmitstatus_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS
+ * sizeof(*(e->xmitstatus_buffer)),
+ GFP_KERNEL);
+ if (!e->xmitstatus_buffer) {
+ printk(KERN_ERR PFX "out of memory\n");
+ kfree(e);
+ return;
+ }
+ e->xmitstatus_print_buffer = kzalloc(BCM43xx_NR_LOGGED_XMITSTATUS
+ * sizeof(*(e->xmitstatus_buffer)),
+ GFP_KERNEL);
+ if (!e->xmitstatus_print_buffer) {
+ printk(KERN_ERR PFX "out of memory\n");
+ kfree(e);
+ return;
+ }
+
+
+ bcm->dfsentry = e;
+
+ strncpy(devdir, bcm->net_dev->name, ARRAY_SIZE(devdir));
+ e->subdir = debugfs_create_dir(devdir, fs.root);
+ e->dentry_devinfo = debugfs_create_file("devinfo", 0444, e->subdir,
+ bcm, &devinfo_fops);
+ if (!e->dentry_devinfo)
+ printk(KERN_ERR PFX "debugfs: creating \"devinfo\" for \"%s\" failed!\n", devdir);
+ e->dentry_spromdump = debugfs_create_file("sprom_dump", 0444, e->subdir,
+ bcm, &spromdump_fops);
+ if (!e->dentry_spromdump)
+ printk(KERN_ERR PFX "debugfs: creating \"sprom_dump\" for \"%s\" failed!\n", devdir);
+ e->dentry_tsf = debugfs_create_file("tsf", 0666, e->subdir,
+ bcm, &tsf_fops);
+ if (!e->dentry_tsf)
+ printk(KERN_ERR PFX "debugfs: creating \"tsf\" for \"%s\" failed!\n", devdir);
+ e->dentry_txstat = debugfs_create_file("tx_status", 0444, e->subdir,
+ bcm, &txstat_fops);
+ if (!e->dentry_txstat)
+ printk(KERN_ERR PFX "debugfs: creating \"tx_status\" for \"%s\" failed!\n", devdir);
+}
+
+void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_dfsentry *e;
+
+ if (!bcm)
+ return;
+
+ e = bcm->dfsentry;
+ assert(e);
+ debugfs_remove(e->dentry_spromdump);
+ debugfs_remove(e->dentry_devinfo);
+ debugfs_remove(e->dentry_tsf);
+ debugfs_remove(e->dentry_txstat);
+ debugfs_remove(e->subdir);
+ kfree(e->xmitstatus_buffer);
+ kfree(e->xmitstatus_print_buffer);
+ kfree(e);
+}
+
+void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status)
+{
+ struct bcm43xx_dfsentry *e;
+ struct bcm43xx_xmitstatus *savedstatus;
+
+ /* This is protected by bcm->_lock */
+ e = bcm->dfsentry;
+ assert(e);
+ savedstatus = e->xmitstatus_buffer + e->xmitstatus_ptr;
+ memcpy(savedstatus, status, sizeof(*status));
+ e->xmitstatus_ptr++;
+ if (e->xmitstatus_ptr >= BCM43xx_NR_LOGGED_XMITSTATUS)
+ e->xmitstatus_ptr = 0;
+ if (e->xmitstatus_cnt < BCM43xx_NR_LOGGED_XMITSTATUS)
+ e->xmitstatus_cnt++;
+}
+
+void bcm43xx_debugfs_init(void)
+{
+ memset(&fs, 0, sizeof(fs));
+ fs.root = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (!fs.root)
+ printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "\" subdir failed!\n");
+ fs.dentry_driverinfo = debugfs_create_file("driver", 0444, fs.root, NULL, &drvinfo_fops);
+ if (!fs.dentry_driverinfo)
+ printk(KERN_ERR PFX "debugfs: creating \"" KBUILD_MODNAME "/driver\" failed!\n");
+}
+
+void bcm43xx_debugfs_exit(void)
+{
+ debugfs_remove(fs.dentry_driverinfo);
+ debugfs_remove(fs.root);
+}
+
+void bcm43xx_printk_dump(const char *data,
+ size_t size,
+ const char *description)
+{
+ size_t i;
+ char c;
+
+ printk(KERN_INFO PFX "Data dump (%s, %u bytes):",
+ description, size);
+ for (i = 0; i < size; i++) {
+ c = data[i];
+ if (i % 8 == 0)
+ printk("\n" KERN_INFO PFX "0x%08x: 0x%02x, ", i, c & 0xff);
+ else
+ printk("0x%02x, ", c & 0xff);
+ }
+ printk("\n");
+}
+
+void bcm43xx_printk_bitdump(const unsigned char *data,
+ size_t bytes, int msb_to_lsb,
+ const char *description)
+{
+ size_t i;
+ int j;
+ const unsigned char *d;
+
+ printk(KERN_INFO PFX "*** Bitdump (%s, %u bytes, %s) ***",
+ description, bytes, msb_to_lsb ? "MSB to LSB" : "LSB to MSB");
+ for (i = 0; i < bytes; i++) {
+ d = data + i;
+ if (i % 8 == 0)
+ printk("\n" KERN_INFO PFX "0x%08x: ", i);
+ if (msb_to_lsb) {
+ for (j = 7; j >= 0; j--) {
+ if (*d & (1 << j))
+ printk("1");
+ else
+ printk("0");
+ }
+ } else {
+ for (j = 0; j < 8; j++) {
+ if (*d & (1 << j))
+ printk("1");
+ else
+ printk("0");
+ }
+ }
+ printk(" ");
+ }
+ printk("\n");
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h
new file mode 100644
index 000000000000..50ce267f794d
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.h
@@ -0,0 +1,117 @@
+#ifndef BCM43xx_DEBUGFS_H_
+#define BCM43xx_DEBUGFS_H_
+
+struct bcm43xx_private;
+struct bcm43xx_xmitstatus;
+
+#ifdef CONFIG_BCM43XX_DEBUG
+
+#include <linux/list.h>
+#include <asm/semaphore.h>
+
+struct dentry;
+
+/* limited by the size of the "really_big_buffer" */
+#define BCM43xx_NR_LOGGED_XMITSTATUS 100
+
+struct bcm43xx_dfsentry {
+ struct dentry *subdir;
+ struct dentry *dentry_devinfo;
+ struct dentry *dentry_spromdump;
+ struct dentry *dentry_tsf;
+ struct dentry *dentry_txstat;
+
+ struct bcm43xx_private *bcm;
+
+ /* saved xmitstatus. */
+ struct bcm43xx_xmitstatus *xmitstatus_buffer;
+ int xmitstatus_ptr;
+ int xmitstatus_cnt;
+ /* We need a seperate buffer while printing to avoid
+ * concurrency issues. (New xmitstatus can arrive
+ * while we are printing).
+ */
+ struct bcm43xx_xmitstatus *xmitstatus_print_buffer;
+ int saved_xmitstatus_ptr;
+ int saved_xmitstatus_cnt;
+ int xmitstatus_printing;
+};
+
+struct bcm43xx_debugfs {
+ struct dentry *root;
+ struct dentry *dentry_driverinfo;
+};
+
+void bcm43xx_debugfs_init(void);
+void bcm43xx_debugfs_exit(void);
+void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm);
+void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm);
+void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status);
+
+/* Debug helper: Dump binary data through printk. */
+void bcm43xx_printk_dump(const char *data,
+ size_t size,
+ const char *description);
+/* Debug helper: Dump bitwise binary data through printk. */
+void bcm43xx_printk_bitdump(const unsigned char *data,
+ size_t bytes, int msb_to_lsb,
+ const char *description);
+#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) \
+ do { \
+ bcm43xx_printk_bitdump((const unsigned char *)(pointer), \
+ sizeof(*(pointer)), \
+ (msb_to_lsb), \
+ (description)); \
+ } while (0)
+
+#else /* CONFIG_BCM43XX_DEBUG*/
+
+static inline
+void bcm43xx_debugfs_init(void) { }
+static inline
+void bcm43xx_debugfs_exit(void) { }
+static inline
+void bcm43xx_debugfs_add_device(struct bcm43xx_private *bcm) { }
+static inline
+void bcm43xx_debugfs_remove_device(struct bcm43xx_private *bcm) { }
+static inline
+void bcm43xx_debugfs_log_txstat(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status) { }
+
+static inline
+void bcm43xx_printk_dump(const char *data,
+ size_t size,
+ const char *description)
+{
+}
+static inline
+void bcm43xx_printk_bitdump(const unsigned char *data,
+ size_t bytes, int msb_to_lsb,
+ const char *description)
+{
+}
+#define bcm43xx_printk_bitdumpt(pointer, msb_to_lsb, description) do { /* nothing */ } while (0)
+
+#endif /* CONFIG_BCM43XX_DEBUG*/
+
+/* Ugly helper macros to make incomplete code more verbose on runtime */
+#ifdef TODO
+# undef TODO
+#endif
+#define TODO() \
+ do { \
+ printk(KERN_INFO PFX "TODO: Incomplete code in %s() at %s:%d\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+ } while (0)
+
+#ifdef FIXME
+# undef FIXME
+#endif
+#define FIXME() \
+ do { \
+ printk(KERN_INFO PFX "FIXME: Possibly broken code in %s() at %s:%d\n", \
+ __FUNCTION__, __FILE__, __LINE__); \
+ } while (0)
+
+#endif /* BCM43xx_DEBUGFS_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.c b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
new file mode 100644
index 000000000000..c3681b8f09b4
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.c
@@ -0,0 +1,968 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ DMA ringbuffer and descriptor allocation/management
+
+ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.de>
+
+ Some code in this file is derived from the b44.c driver
+ Copyright (C) 2002 David S. Miller
+ Copyright (C) Pekka Pietikainen
+
+ 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 "bcm43xx.h"
+#include "bcm43xx_dma.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_debugfs.h"
+#include "bcm43xx_power.h"
+#include "bcm43xx_xmit.h"
+
+#include <linux/dma-mapping.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/skbuff.h>
+
+
+static inline int free_slots(struct bcm43xx_dmaring *ring)
+{
+ return (ring->nr_slots - ring->used_slots);
+}
+
+static inline int next_slot(struct bcm43xx_dmaring *ring, int slot)
+{
+ assert(slot >= -1 && slot <= ring->nr_slots - 1);
+ if (slot == ring->nr_slots - 1)
+ return 0;
+ return slot + 1;
+}
+
+static inline int prev_slot(struct bcm43xx_dmaring *ring, int slot)
+{
+ assert(slot >= 0 && slot <= ring->nr_slots - 1);
+ if (slot == 0)
+ return ring->nr_slots - 1;
+ return slot - 1;
+}
+
+/* Request a slot for usage. */
+static inline
+int request_slot(struct bcm43xx_dmaring *ring)
+{
+ int slot;
+
+ assert(ring->tx);
+ assert(!ring->suspended);
+ assert(free_slots(ring) != 0);
+
+ slot = next_slot(ring, ring->current_slot);
+ ring->current_slot = slot;
+ ring->used_slots++;
+
+ /* Check the number of available slots and suspend TX,
+ * if we are running low on free slots.
+ */
+ if (unlikely(free_slots(ring) < ring->suspend_mark)) {
+ netif_stop_queue(ring->bcm->net_dev);
+ ring->suspended = 1;
+ }
+#ifdef CONFIG_BCM43XX_DEBUG
+ if (ring->used_slots > ring->max_used_slots)
+ ring->max_used_slots = ring->used_slots;
+#endif /* CONFIG_BCM43XX_DEBUG*/
+
+ return slot;
+}
+
+/* Return a slot to the free slots. */
+static inline
+void return_slot(struct bcm43xx_dmaring *ring, int slot)
+{
+ assert(ring->tx);
+
+ ring->used_slots--;
+
+ /* Check if TX is suspended and check if we have
+ * enough free slots to resume it again.
+ */
+ if (unlikely(ring->suspended)) {
+ if (free_slots(ring) >= ring->resume_mark) {
+ ring->suspended = 0;
+ netif_wake_queue(ring->bcm->net_dev);
+ }
+ }
+}
+
+static inline
+dma_addr_t map_descbuffer(struct bcm43xx_dmaring *ring,
+ unsigned char *buf,
+ size_t len,
+ int tx)
+{
+ dma_addr_t dmaaddr;
+
+ if (tx) {
+ dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
+ buf, len,
+ DMA_TO_DEVICE);
+ } else {
+ dmaaddr = dma_map_single(&ring->bcm->pci_dev->dev,
+ buf, len,
+ DMA_FROM_DEVICE);
+ }
+
+ return dmaaddr;
+}
+
+static inline
+void unmap_descbuffer(struct bcm43xx_dmaring *ring,
+ dma_addr_t addr,
+ size_t len,
+ int tx)
+{
+ if (tx) {
+ dma_unmap_single(&ring->bcm->pci_dev->dev,
+ addr, len,
+ DMA_TO_DEVICE);
+ } else {
+ dma_unmap_single(&ring->bcm->pci_dev->dev,
+ addr, len,
+ DMA_FROM_DEVICE);
+ }
+}
+
+static inline
+void sync_descbuffer_for_cpu(struct bcm43xx_dmaring *ring,
+ dma_addr_t addr,
+ size_t len)
+{
+ assert(!ring->tx);
+
+ dma_sync_single_for_cpu(&ring->bcm->pci_dev->dev,
+ addr, len, DMA_FROM_DEVICE);
+}
+
+static inline
+void sync_descbuffer_for_device(struct bcm43xx_dmaring *ring,
+ dma_addr_t addr,
+ size_t len)
+{
+ assert(!ring->tx);
+
+ dma_sync_single_for_device(&ring->bcm->pci_dev->dev,
+ addr, len, DMA_FROM_DEVICE);
+}
+
+/* Unmap and free a descriptor buffer. */
+static inline
+void free_descriptor_buffer(struct bcm43xx_dmaring *ring,
+ struct bcm43xx_dmadesc *desc,
+ struct bcm43xx_dmadesc_meta *meta,
+ int irq_context)
+{
+ assert(meta->skb);
+ if (irq_context)
+ dev_kfree_skb_irq(meta->skb);
+ else
+ dev_kfree_skb(meta->skb);
+ meta->skb = NULL;
+}
+
+static int alloc_ringmemory(struct bcm43xx_dmaring *ring)
+{
+ struct device *dev = &(ring->bcm->pci_dev->dev);
+
+ ring->vbase = dma_alloc_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,
+ &(ring->dmabase), GFP_KERNEL);
+ if (!ring->vbase) {
+ printk(KERN_ERR PFX "DMA ringmemory allocation failed\n");
+ return -ENOMEM;
+ }
+ if (ring->dmabase + BCM43xx_DMA_RINGMEMSIZE > BCM43xx_DMA_BUSADDRMAX) {
+ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RINGMEMORY >1G "
+ "(0x%08x, len: %lu)\n",
+ ring->dmabase, BCM43xx_DMA_RINGMEMSIZE);
+ dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,
+ ring->vbase, ring->dmabase);
+ return -ENOMEM;
+ }
+ assert(!(ring->dmabase & 0x000003FF));
+ memset(ring->vbase, 0, BCM43xx_DMA_RINGMEMSIZE);
+
+ return 0;
+}
+
+static void free_ringmemory(struct bcm43xx_dmaring *ring)
+{
+ struct device *dev = &(ring->bcm->pci_dev->dev);
+
+ dma_free_coherent(dev, BCM43xx_DMA_RINGMEMSIZE,
+ ring->vbase, ring->dmabase);
+}
+
+/* Reset the RX DMA channel */
+int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,
+ u16 mmio_base)
+{
+ int i;
+ u32 value;
+
+ bcm43xx_write32(bcm,
+ mmio_base + BCM43xx_DMA_RX_CONTROL,
+ 0x00000000);
+ for (i = 0; i < 1000; i++) {
+ value = bcm43xx_read32(bcm,
+ mmio_base + BCM43xx_DMA_RX_STATUS);
+ value &= BCM43xx_DMA_RXSTAT_STAT_MASK;
+ if (value == BCM43xx_DMA_RXSTAT_STAT_DISABLED) {
+ i = -1;
+ break;
+ }
+ udelay(10);
+ }
+ if (i != -1) {
+ printk(KERN_ERR PFX "Error: Wait on DMA RX status timed out.\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+/* Reset the RX DMA channel */
+int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,
+ u16 mmio_base)
+{
+ int i;
+ u32 value;
+
+ for (i = 0; i < 1000; i++) {
+ value = bcm43xx_read32(bcm,
+ mmio_base + BCM43xx_DMA_TX_STATUS);
+ value &= BCM43xx_DMA_TXSTAT_STAT_MASK;
+ if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED ||
+ value == BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT ||
+ value == BCM43xx_DMA_TXSTAT_STAT_STOPPED)
+ break;
+ udelay(10);
+ }
+ bcm43xx_write32(bcm,
+ mmio_base + BCM43xx_DMA_TX_CONTROL,
+ 0x00000000);
+ for (i = 0; i < 1000; i++) {
+ value = bcm43xx_read32(bcm,
+ mmio_base + BCM43xx_DMA_TX_STATUS);
+ value &= BCM43xx_DMA_TXSTAT_STAT_MASK;
+ if (value == BCM43xx_DMA_TXSTAT_STAT_DISABLED) {
+ i = -1;
+ break;
+ }
+ udelay(10);
+ }
+ if (i != -1) {
+ printk(KERN_ERR PFX "Error: Wait on DMA TX status timed out.\n");
+ return -ENODEV;
+ }
+ /* ensure the reset is completed. */
+ udelay(300);
+
+ return 0;
+}
+
+static int setup_rx_descbuffer(struct bcm43xx_dmaring *ring,
+ struct bcm43xx_dmadesc *desc,
+ struct bcm43xx_dmadesc_meta *meta,
+ gfp_t gfp_flags)
+{
+ struct bcm43xx_rxhdr *rxhdr;
+ dma_addr_t dmaaddr;
+ u32 desc_addr;
+ u32 desc_ctl;
+ const int slot = (int)(desc - ring->vbase);
+ struct sk_buff *skb;
+
+ assert(slot >= 0 && slot < ring->nr_slots);
+ assert(!ring->tx);
+
+ skb = __dev_alloc_skb(ring->rx_buffersize, gfp_flags);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ dmaaddr = map_descbuffer(ring, skb->data, ring->rx_buffersize, 0);
+ if (unlikely(dmaaddr + ring->rx_buffersize > BCM43xx_DMA_BUSADDRMAX)) {
+ unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);
+ dev_kfree_skb_any(skb);
+ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA RX SKB >1G "
+ "(0x%08x, len: %u)\n",
+ dmaaddr, ring->rx_buffersize);
+ return -ENOMEM;
+ }
+ meta->skb = skb;
+ meta->dmaaddr = dmaaddr;
+ skb->dev = ring->bcm->net_dev;
+ desc_addr = (u32)(dmaaddr + ring->memoffset);
+ desc_ctl = (BCM43xx_DMADTOR_BYTECNT_MASK &
+ (u32)(ring->rx_buffersize - ring->frameoffset));
+ if (slot == ring->nr_slots - 1)
+ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND;
+ set_desc_addr(desc, desc_addr);
+ set_desc_ctl(desc, desc_ctl);
+
+ rxhdr = (struct bcm43xx_rxhdr *)(skb->data);
+ rxhdr->frame_length = 0;
+ rxhdr->flags1 = 0;
+
+ return 0;
+}
+
+/* Allocate the initial descbuffers.
+ * This is used for an RX ring only.
+ */
+static int alloc_initial_descbuffers(struct bcm43xx_dmaring *ring)
+{
+ int i, err = -ENOMEM;
+ struct bcm43xx_dmadesc *desc;
+ struct bcm43xx_dmadesc_meta *meta;
+
+ for (i = 0; i < ring->nr_slots; i++) {
+ desc = ring->vbase + i;
+ meta = ring->meta + i;
+
+ err = setup_rx_descbuffer(ring, desc, meta, GFP_KERNEL);
+ if (err)
+ goto err_unwind;
+ }
+ ring->used_slots = ring->nr_slots;
+ err = 0;
+out:
+ return err;
+
+err_unwind:
+ for (i--; i >= 0; i--) {
+ desc = ring->vbase + i;
+ meta = ring->meta + i;
+
+ unmap_descbuffer(ring, meta->dmaaddr, ring->rx_buffersize, 0);
+ dev_kfree_skb(meta->skb);
+ }
+ goto out;
+}
+
+/* Do initial setup of the DMA controller.
+ * Reset the controller, write the ring busaddress
+ * and switch the "enable" bit on.
+ */
+static int dmacontroller_setup(struct bcm43xx_dmaring *ring)
+{
+ int err = 0;
+ u32 value;
+
+ if (ring->tx) {
+ /* Set Transmit Control register to "transmit enable" */
+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL,
+ BCM43xx_DMA_TXCTRL_ENABLE);
+ /* Set Transmit Descriptor ring address. */
+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING,
+ ring->dmabase + ring->memoffset);
+ } else {
+ err = alloc_initial_descbuffers(ring);
+ if (err)
+ goto out;
+ /* Set Receive Control "receive enable" and frame offset */
+ value = (ring->frameoffset << BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT);
+ value |= BCM43xx_DMA_RXCTRL_ENABLE;
+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_CONTROL, value);
+ /* Set Receive Descriptor ring address. */
+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING,
+ ring->dmabase + ring->memoffset);
+ /* Init the descriptor pointer. */
+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX, 200);
+ }
+
+out:
+ return err;
+}
+
+/* Shutdown the DMA controller. */
+static void dmacontroller_cleanup(struct bcm43xx_dmaring *ring)
+{
+ if (ring->tx) {
+ bcm43xx_dmacontroller_tx_reset(ring->bcm, ring->mmio_base);
+ /* Zero out Transmit Descriptor ring address. */
+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_RING, 0);
+ } else {
+ bcm43xx_dmacontroller_rx_reset(ring->bcm, ring->mmio_base);
+ /* Zero out Receive Descriptor ring address. */
+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_RING, 0);
+ }
+}
+
+static void free_all_descbuffers(struct bcm43xx_dmaring *ring)
+{
+ struct bcm43xx_dmadesc *desc;
+ struct bcm43xx_dmadesc_meta *meta;
+ int i;
+
+ if (!ring->used_slots)
+ return;
+ for (i = 0; i < ring->nr_slots; i++) {
+ desc = ring->vbase + i;
+ meta = ring->meta + i;
+
+ if (!meta->skb) {
+ assert(ring->tx);
+ continue;
+ }
+ if (ring->tx) {
+ unmap_descbuffer(ring, meta->dmaaddr,
+ meta->skb->len, 1);
+ } else {
+ unmap_descbuffer(ring, meta->dmaaddr,
+ ring->rx_buffersize, 0);
+ }
+ free_descriptor_buffer(ring, desc, meta, 0);
+ }
+}
+
+/* Main initialization function. */
+static
+struct bcm43xx_dmaring * bcm43xx_setup_dmaring(struct bcm43xx_private *bcm,
+ u16 dma_controller_base,
+ int nr_descriptor_slots,
+ int tx)
+{
+ struct bcm43xx_dmaring *ring;
+ int err;
+
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+ if (!ring)
+ goto out;
+
+ ring->meta = kzalloc(sizeof(*ring->meta) * nr_descriptor_slots,
+ GFP_KERNEL);
+ if (!ring->meta)
+ goto err_kfree_ring;
+
+ ring->memoffset = BCM43xx_DMA_DMABUSADDROFFSET;
+#ifdef CONFIG_BCM947XX
+ if (bcm->pci_dev->bus->number == 0)
+ ring->memoffset = 0;
+#endif
+
+ ring->bcm = bcm;
+ ring->nr_slots = nr_descriptor_slots;
+ ring->suspend_mark = ring->nr_slots * BCM43xx_TXSUSPEND_PERCENT / 100;
+ ring->resume_mark = ring->nr_slots * BCM43xx_TXRESUME_PERCENT / 100;
+ assert(ring->suspend_mark < ring->resume_mark);
+ ring->mmio_base = dma_controller_base;
+ if (tx) {
+ ring->tx = 1;
+ ring->current_slot = -1;
+ } else {
+ switch (dma_controller_base) {
+ case BCM43xx_MMIO_DMA1_BASE:
+ ring->rx_buffersize = BCM43xx_DMA1_RXBUFFERSIZE;
+ ring->frameoffset = BCM43xx_DMA1_RX_FRAMEOFFSET;
+ break;
+ case BCM43xx_MMIO_DMA4_BASE:
+ ring->rx_buffersize = BCM43xx_DMA4_RXBUFFERSIZE;
+ ring->frameoffset = BCM43xx_DMA4_RX_FRAMEOFFSET;
+ break;
+ default:
+ assert(0);
+ }
+ }
+
+ err = alloc_ringmemory(ring);
+ if (err)
+ goto err_kfree_meta;
+ err = dmacontroller_setup(ring);
+ if (err)
+ goto err_free_ringmemory;
+
+out:
+ return ring;
+
+err_free_ringmemory:
+ free_ringmemory(ring);
+err_kfree_meta:
+ kfree(ring->meta);
+err_kfree_ring:
+ kfree(ring);
+ ring = NULL;
+ goto out;
+}
+
+/* Main cleanup function. */
+static void bcm43xx_destroy_dmaring(struct bcm43xx_dmaring *ring)
+{
+ if (!ring)
+ return;
+
+ dprintk(KERN_INFO PFX "DMA 0x%04x (%s) max used slots: %d/%d\n",
+ ring->mmio_base,
+ (ring->tx) ? "TX" : "RX",
+ ring->max_used_slots, ring->nr_slots);
+ /* Device IRQs are disabled prior entering this function,
+ * so no need to take care of concurrency with rx handler stuff.
+ */
+ dmacontroller_cleanup(ring);
+ free_all_descbuffers(ring);
+ free_ringmemory(ring);
+
+ kfree(ring->meta);
+ kfree(ring);
+}
+
+void bcm43xx_dma_free(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_dma *dma;
+
+ if (bcm43xx_using_pio(bcm))
+ return;
+ dma = bcm43xx_current_dma(bcm);
+
+ bcm43xx_destroy_dmaring(dma->rx_ring1);
+ dma->rx_ring1 = NULL;
+ bcm43xx_destroy_dmaring(dma->rx_ring0);
+ dma->rx_ring0 = NULL;
+ bcm43xx_destroy_dmaring(dma->tx_ring3);
+ dma->tx_ring3 = NULL;
+ bcm43xx_destroy_dmaring(dma->tx_ring2);
+ dma->tx_ring2 = NULL;
+ bcm43xx_destroy_dmaring(dma->tx_ring1);
+ dma->tx_ring1 = NULL;
+ bcm43xx_destroy_dmaring(dma->tx_ring0);
+ dma->tx_ring0 = NULL;
+}
+
+int bcm43xx_dma_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm);
+ struct bcm43xx_dmaring *ring;
+ int err = -ENOMEM;
+
+ /* setup TX DMA channels. */
+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE,
+ BCM43xx_TXRING_SLOTS, 1);
+ if (!ring)
+ goto out;
+ dma->tx_ring0 = ring;
+
+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA2_BASE,
+ BCM43xx_TXRING_SLOTS, 1);
+ if (!ring)
+ goto err_destroy_tx0;
+ dma->tx_ring1 = ring;
+
+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA3_BASE,
+ BCM43xx_TXRING_SLOTS, 1);
+ if (!ring)
+ goto err_destroy_tx1;
+ dma->tx_ring2 = ring;
+
+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE,
+ BCM43xx_TXRING_SLOTS, 1);
+ if (!ring)
+ goto err_destroy_tx2;
+ dma->tx_ring3 = ring;
+
+ /* setup RX DMA channels. */
+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA1_BASE,
+ BCM43xx_RXRING_SLOTS, 0);
+ if (!ring)
+ goto err_destroy_tx3;
+ dma->rx_ring0 = ring;
+
+ if (bcm->current_core->rev < 5) {
+ ring = bcm43xx_setup_dmaring(bcm, BCM43xx_MMIO_DMA4_BASE,
+ BCM43xx_RXRING_SLOTS, 0);
+ if (!ring)
+ goto err_destroy_rx0;
+ dma->rx_ring1 = ring;
+ }
+
+ dprintk(KERN_INFO PFX "DMA initialized\n");
+ err = 0;
+out:
+ return err;
+
+err_destroy_rx0:
+ bcm43xx_destroy_dmaring(dma->rx_ring0);
+ dma->rx_ring0 = NULL;
+err_destroy_tx3:
+ bcm43xx_destroy_dmaring(dma->tx_ring3);
+ dma->tx_ring3 = NULL;
+err_destroy_tx2:
+ bcm43xx_destroy_dmaring(dma->tx_ring2);
+ dma->tx_ring2 = NULL;
+err_destroy_tx1:
+ bcm43xx_destroy_dmaring(dma->tx_ring1);
+ dma->tx_ring1 = NULL;
+err_destroy_tx0:
+ bcm43xx_destroy_dmaring(dma->tx_ring0);
+ dma->tx_ring0 = NULL;
+ goto out;
+}
+
+/* Generate a cookie for the TX header. */
+static u16 generate_cookie(struct bcm43xx_dmaring *ring,
+ int slot)
+{
+ u16 cookie = 0x0000;
+
+ /* Use the upper 4 bits of the cookie as
+ * DMA controller ID and store the slot number
+ * in the lower 12 bits
+ */
+ switch (ring->mmio_base) {
+ default:
+ assert(0);
+ case BCM43xx_MMIO_DMA1_BASE:
+ break;
+ case BCM43xx_MMIO_DMA2_BASE:
+ cookie = 0x1000;
+ break;
+ case BCM43xx_MMIO_DMA3_BASE:
+ cookie = 0x2000;
+ break;
+ case BCM43xx_MMIO_DMA4_BASE:
+ cookie = 0x3000;
+ break;
+ }
+ assert(((u16)slot & 0xF000) == 0x0000);
+ cookie |= (u16)slot;
+
+ return cookie;
+}
+
+/* Inspect a cookie and find out to which controller/slot it belongs. */
+static
+struct bcm43xx_dmaring * parse_cookie(struct bcm43xx_private *bcm,
+ u16 cookie, int *slot)
+{
+ struct bcm43xx_dma *dma = bcm43xx_current_dma(bcm);
+ struct bcm43xx_dmaring *ring = NULL;
+
+ switch (cookie & 0xF000) {
+ case 0x0000:
+ ring = dma->tx_ring0;
+ break;
+ case 0x1000:
+ ring = dma->tx_ring1;
+ break;
+ case 0x2000:
+ ring = dma->tx_ring2;
+ break;
+ case 0x3000:
+ ring = dma->tx_ring3;
+ break;
+ default:
+ assert(0);
+ }
+ *slot = (cookie & 0x0FFF);
+ assert(*slot >= 0 && *slot < ring->nr_slots);
+
+ return ring;
+}
+
+static void dmacontroller_poke_tx(struct bcm43xx_dmaring *ring,
+ int slot)
+{
+ /* Everything is ready to start. Buffers are DMA mapped and
+ * associated with slots.
+ * "slot" is the last slot of the new frame we want to transmit.
+ * Close your seat belts now, please.
+ */
+ wmb();
+ slot = next_slot(ring, slot);
+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_DESC_INDEX,
+ (u32)(slot * sizeof(struct bcm43xx_dmadesc)));
+}
+
+static int dma_tx_fragment(struct bcm43xx_dmaring *ring,
+ struct sk_buff *skb,
+ u8 cur_frag)
+{
+ int slot;
+ struct bcm43xx_dmadesc *desc;
+ struct bcm43xx_dmadesc_meta *meta;
+ u32 desc_ctl;
+ u32 desc_addr;
+
+ assert(skb_shinfo(skb)->nr_frags == 0);
+
+ slot = request_slot(ring);
+ desc = ring->vbase + slot;
+ meta = ring->meta + slot;
+
+ /* Add a device specific TX header. */
+ assert(skb_headroom(skb) >= sizeof(struct bcm43xx_txhdr));
+ /* Reserve enough headroom for the device tx header. */
+ __skb_push(skb, sizeof(struct bcm43xx_txhdr));
+ /* Now calculate and add the tx header.
+ * The tx header includes the PLCP header.
+ */
+ bcm43xx_generate_txhdr(ring->bcm,
+ (struct bcm43xx_txhdr *)skb->data,
+ skb->data + sizeof(struct bcm43xx_txhdr),
+ skb->len - sizeof(struct bcm43xx_txhdr),
+ (cur_frag == 0),
+ generate_cookie(ring, slot));
+
+ meta->skb = skb;
+ meta->dmaaddr = map_descbuffer(ring, skb->data, skb->len, 1);
+ if (unlikely(meta->dmaaddr + skb->len > BCM43xx_DMA_BUSADDRMAX)) {
+ return_slot(ring, slot);
+ printk(KERN_ERR PFX ">>>FATAL ERROR<<< DMA TX SKB >1G "
+ "(0x%08x, len: %u)\n",
+ meta->dmaaddr, skb->len);
+ return -ENOMEM;
+ }
+
+ desc_addr = (u32)(meta->dmaaddr + ring->memoffset);
+ desc_ctl = BCM43xx_DMADTOR_FRAMESTART | BCM43xx_DMADTOR_FRAMEEND;
+ desc_ctl |= BCM43xx_DMADTOR_COMPIRQ;
+ desc_ctl |= (BCM43xx_DMADTOR_BYTECNT_MASK &
+ (u32)(meta->skb->len - ring->frameoffset));
+ if (slot == ring->nr_slots - 1)
+ desc_ctl |= BCM43xx_DMADTOR_DTABLEEND;
+
+ set_desc_ctl(desc, desc_ctl);
+ set_desc_addr(desc, desc_addr);
+ /* Now transfer the whole frame. */
+ dmacontroller_poke_tx(ring, slot);
+
+ return 0;
+}
+
+int bcm43xx_dma_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb)
+{
+ /* We just received a packet from the kernel network subsystem.
+ * Add headers and DMA map the memory. Poke
+ * the device to send the stuff.
+ * Note that this is called from atomic context.
+ */
+ struct bcm43xx_dmaring *ring = bcm43xx_current_dma(bcm)->tx_ring1;
+ u8 i;
+ struct sk_buff *skb;
+
+ assert(ring->tx);
+ if (unlikely(free_slots(ring) < txb->nr_frags)) {
+ /* The queue should be stopped,
+ * if we are low on free slots.
+ * If this ever triggers, we have to lower the suspend_mark.
+ */
+ dprintkl(KERN_ERR PFX "Out of DMA descriptor slots!\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < txb->nr_frags; i++) {
+ skb = txb->fragments[i];
+ /* Take skb from ieee80211_txb_free */
+ txb->fragments[i] = NULL;
+ dma_tx_fragment(ring, skb, i);
+ //TODO: handle failure of dma_tx_fragment
+ }
+ ieee80211_txb_free(txb);
+
+ return 0;
+}
+
+void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status)
+{
+ struct bcm43xx_dmaring *ring;
+ struct bcm43xx_dmadesc *desc;
+ struct bcm43xx_dmadesc_meta *meta;
+ int is_last_fragment;
+ int slot;
+
+ ring = parse_cookie(bcm, status->cookie, &slot);
+ assert(ring);
+ assert(ring->tx);
+ assert(get_desc_ctl(ring->vbase + slot) & BCM43xx_DMADTOR_FRAMESTART);
+ while (1) {
+ assert(slot >= 0 && slot < ring->nr_slots);
+ desc = ring->vbase + slot;
+ meta = ring->meta + slot;
+
+ is_last_fragment = !!(get_desc_ctl(desc) & BCM43xx_DMADTOR_FRAMEEND);
+ unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len, 1);
+ free_descriptor_buffer(ring, desc, meta, 1);
+ /* Everything belonging to the slot is unmapped
+ * and freed, so we can return it.
+ */
+ return_slot(ring, slot);
+
+ if (is_last_fragment)
+ break;
+ slot = next_slot(ring, slot);
+ }
+ bcm->stats.last_tx = jiffies;
+}
+
+static void dma_rx(struct bcm43xx_dmaring *ring,
+ int *slot)
+{
+ struct bcm43xx_dmadesc *desc;
+ struct bcm43xx_dmadesc_meta *meta;
+ struct bcm43xx_rxhdr *rxhdr;
+ struct sk_buff *skb;
+ u16 len;
+ int err;
+ dma_addr_t dmaaddr;
+
+ desc = ring->vbase + *slot;
+ meta = ring->meta + *slot;
+
+ sync_descbuffer_for_cpu(ring, meta->dmaaddr, ring->rx_buffersize);
+ skb = meta->skb;
+
+ if (ring->mmio_base == BCM43xx_MMIO_DMA4_BASE) {
+ /* We received an xmit status. */
+ struct bcm43xx_hwxmitstatus *hw = (struct bcm43xx_hwxmitstatus *)skb->data;
+ struct bcm43xx_xmitstatus stat;
+
+ stat.cookie = le16_to_cpu(hw->cookie);
+ stat.flags = hw->flags;
+ stat.cnt1 = hw->cnt1;
+ stat.cnt2 = hw->cnt2;
+ stat.seq = le16_to_cpu(hw->seq);
+ stat.unknown = le16_to_cpu(hw->unknown);
+
+ bcm43xx_debugfs_log_txstat(ring->bcm, &stat);
+ bcm43xx_dma_handle_xmitstatus(ring->bcm, &stat);
+ /* recycle the descriptor buffer. */
+ sync_descbuffer_for_device(ring, meta->dmaaddr, ring->rx_buffersize);
+
+ return;
+ }
+ rxhdr = (struct bcm43xx_rxhdr *)skb->data;
+ len = le16_to_cpu(rxhdr->frame_length);
+ if (len == 0) {
+ int i = 0;
+
+ do {
+ udelay(2);
+ barrier();
+ len = le16_to_cpu(rxhdr->frame_length);
+ } while (len == 0 && i++ < 5);
+ if (unlikely(len == 0)) {
+ /* recycle the descriptor buffer. */
+ sync_descbuffer_for_device(ring, meta->dmaaddr,
+ ring->rx_buffersize);
+ goto drop;
+ }
+ }
+ if (unlikely(len > ring->rx_buffersize)) {
+ /* The data did not fit into one descriptor buffer
+ * and is split over multiple buffers.
+ * This should never happen, as we try to allocate buffers
+ * big enough. So simply ignore this packet.
+ */
+ int cnt = 0;
+ s32 tmp = len;
+
+ while (1) {
+ desc = ring->vbase + *slot;
+ meta = ring->meta + *slot;
+ /* recycle the descriptor buffer. */
+ sync_descbuffer_for_device(ring, meta->dmaaddr,
+ ring->rx_buffersize);
+ *slot = next_slot(ring, *slot);
+ cnt++;
+ tmp -= ring->rx_buffersize;
+ if (tmp <= 0)
+ break;
+ }
+ printkl(KERN_ERR PFX "DMA RX buffer too small "
+ "(len: %u, buffer: %u, nr-dropped: %d)\n",
+ len, ring->rx_buffersize, cnt);
+ goto drop;
+ }
+ len -= IEEE80211_FCS_LEN;
+
+ dmaaddr = meta->dmaaddr;
+ err = setup_rx_descbuffer(ring, desc, meta, GFP_ATOMIC);
+ if (unlikely(err)) {
+ dprintkl(KERN_ERR PFX "DMA RX: setup_rx_descbuffer() failed\n");
+ sync_descbuffer_for_device(ring, dmaaddr,
+ ring->rx_buffersize);
+ goto drop;
+ }
+
+ unmap_descbuffer(ring, dmaaddr, ring->rx_buffersize, 0);
+ skb_put(skb, len + ring->frameoffset);
+ skb_pull(skb, ring->frameoffset);
+
+ err = bcm43xx_rx(ring->bcm, skb, rxhdr);
+ if (err) {
+ dev_kfree_skb_irq(skb);
+ goto drop;
+ }
+
+drop:
+ return;
+}
+
+void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)
+{
+ u32 status;
+ u16 descptr;
+ int slot, current_slot;
+#ifdef CONFIG_BCM43XX_DEBUG
+ int used_slots = 0;
+#endif
+
+ assert(!ring->tx);
+ status = bcm43xx_dma_read(ring, BCM43xx_DMA_RX_STATUS);
+ descptr = (status & BCM43xx_DMA_RXSTAT_DPTR_MASK);
+ current_slot = descptr / sizeof(struct bcm43xx_dmadesc);
+ assert(current_slot >= 0 && current_slot < ring->nr_slots);
+
+ slot = ring->current_slot;
+ for ( ; slot != current_slot; slot = next_slot(ring, slot)) {
+ dma_rx(ring, &slot);
+#ifdef CONFIG_BCM43XX_DEBUG
+ if (++used_slots > ring->max_used_slots)
+ ring->max_used_slots = used_slots;
+#endif
+ }
+ bcm43xx_dma_write(ring, BCM43xx_DMA_RX_DESC_INDEX,
+ (u32)(slot * sizeof(struct bcm43xx_dmadesc)));
+ ring->current_slot = slot;
+}
+
+void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring)
+{
+ assert(ring->tx);
+ bcm43xx_power_saving_ctl_bits(ring->bcm, -1, 1);
+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL,
+ bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL)
+ | BCM43xx_DMA_TXCTRL_SUSPEND);
+}
+
+void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring)
+{
+ assert(ring->tx);
+ bcm43xx_dma_write(ring, BCM43xx_DMA_TX_CONTROL,
+ bcm43xx_dma_read(ring, BCM43xx_DMA_TX_CONTROL)
+ & ~BCM43xx_DMA_TXCTRL_SUSPEND);
+ bcm43xx_power_saving_ctl_bits(ring->bcm, -1, -1);
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_dma.h b/drivers/net/wireless/bcm43xx/bcm43xx_dma.h
new file mode 100644
index 000000000000..2d520e4b0276
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_dma.h
@@ -0,0 +1,218 @@
+#ifndef BCM43xx_DMA_H_
+#define BCM43xx_DMA_H_
+
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/linkage.h>
+#include <asm/atomic.h>
+
+
+/* DMA-Interrupt reasons. */
+#define BCM43xx_DMAIRQ_FATALMASK ((1 << 10) | (1 << 11) | (1 << 12) \
+ | (1 << 14) | (1 << 15))
+#define BCM43xx_DMAIRQ_NONFATALMASK (1 << 13)
+#define BCM43xx_DMAIRQ_RX_DONE (1 << 16)
+
+/* DMA controller register offsets. (relative to BCM43xx_DMA#_BASE) */
+#define BCM43xx_DMA_TX_CONTROL 0x00
+#define BCM43xx_DMA_TX_DESC_RING 0x04
+#define BCM43xx_DMA_TX_DESC_INDEX 0x08
+#define BCM43xx_DMA_TX_STATUS 0x0c
+#define BCM43xx_DMA_RX_CONTROL 0x10
+#define BCM43xx_DMA_RX_DESC_RING 0x14
+#define BCM43xx_DMA_RX_DESC_INDEX 0x18
+#define BCM43xx_DMA_RX_STATUS 0x1c
+
+/* DMA controller channel control word values. */
+#define BCM43xx_DMA_TXCTRL_ENABLE (1 << 0)
+#define BCM43xx_DMA_TXCTRL_SUSPEND (1 << 1)
+#define BCM43xx_DMA_TXCTRL_LOOPBACK (1 << 2)
+#define BCM43xx_DMA_TXCTRL_FLUSH (1 << 4)
+#define BCM43xx_DMA_RXCTRL_ENABLE (1 << 0)
+#define BCM43xx_DMA_RXCTRL_FRAMEOFF_MASK 0x000000fe
+#define BCM43xx_DMA_RXCTRL_FRAMEOFF_SHIFT 1
+#define BCM43xx_DMA_RXCTRL_PIO (1 << 8)
+/* DMA controller channel status word values. */
+#define BCM43xx_DMA_TXSTAT_DPTR_MASK 0x00000fff
+#define BCM43xx_DMA_TXSTAT_STAT_MASK 0x0000f000
+#define BCM43xx_DMA_TXSTAT_STAT_DISABLED 0x00000000
+#define BCM43xx_DMA_TXSTAT_STAT_ACTIVE 0x00001000
+#define BCM43xx_DMA_TXSTAT_STAT_IDLEWAIT 0x00002000
+#define BCM43xx_DMA_TXSTAT_STAT_STOPPED 0x00003000
+#define BCM43xx_DMA_TXSTAT_STAT_SUSP 0x00004000
+#define BCM43xx_DMA_TXSTAT_ERROR_MASK 0x000f0000
+#define BCM43xx_DMA_TXSTAT_FLUSHED (1 << 20)
+#define BCM43xx_DMA_RXSTAT_DPTR_MASK 0x00000fff
+#define BCM43xx_DMA_RXSTAT_STAT_MASK 0x0000f000
+#define BCM43xx_DMA_RXSTAT_STAT_DISABLED 0x00000000
+#define BCM43xx_DMA_RXSTAT_STAT_ACTIVE 0x00001000
+#define BCM43xx_DMA_RXSTAT_STAT_IDLEWAIT 0x00002000
+#define BCM43xx_DMA_RXSTAT_STAT_RESERVED 0x00003000
+#define BCM43xx_DMA_RXSTAT_STAT_ERRORS 0x00004000
+#define BCM43xx_DMA_RXSTAT_ERROR_MASK 0x000f0000
+
+/* DMA descriptor control field values. */
+#define BCM43xx_DMADTOR_BYTECNT_MASK 0x00001fff
+#define BCM43xx_DMADTOR_DTABLEEND (1 << 28) /* End of descriptor table */
+#define BCM43xx_DMADTOR_COMPIRQ (1 << 29) /* IRQ on completion request */
+#define BCM43xx_DMADTOR_FRAMEEND (1 << 30)
+#define BCM43xx_DMADTOR_FRAMESTART (1 << 31)
+
+/* Misc DMA constants */
+#define BCM43xx_DMA_RINGMEMSIZE PAGE_SIZE
+#define BCM43xx_DMA_BUSADDRMAX 0x3FFFFFFF
+#define BCM43xx_DMA_DMABUSADDROFFSET (1 << 30)
+#define BCM43xx_DMA1_RX_FRAMEOFFSET 30
+#define BCM43xx_DMA4_RX_FRAMEOFFSET 0
+
+/* DMA engine tuning knobs */
+#define BCM43xx_TXRING_SLOTS 512
+#define BCM43xx_RXRING_SLOTS 64
+#define BCM43xx_DMA1_RXBUFFERSIZE (2304 + 100)
+#define BCM43xx_DMA4_RXBUFFERSIZE 16
+/* Suspend the tx queue, if less than this percent slots are free. */
+#define BCM43xx_TXSUSPEND_PERCENT 20
+/* Resume the tx queue, if more than this percent slots are free. */
+#define BCM43xx_TXRESUME_PERCENT 50
+
+
+
+#ifdef CONFIG_BCM43XX_DMA
+
+
+struct sk_buff;
+struct bcm43xx_private;
+struct bcm43xx_xmitstatus;
+
+
+struct bcm43xx_dmadesc {
+ __le32 _control;
+ __le32 _address;
+} __attribute__((__packed__));
+
+/* Macros to access the bcm43xx_dmadesc struct */
+#define get_desc_ctl(desc) le32_to_cpu((desc)->_control)
+#define set_desc_ctl(desc, ctl) do { (desc)->_control = cpu_to_le32(ctl); } while (0)
+#define get_desc_addr(desc) le32_to_cpu((desc)->_address)
+#define set_desc_addr(desc, addr) do { (desc)->_address = cpu_to_le32(addr); } while (0)
+
+struct bcm43xx_dmadesc_meta {
+ /* The kernel DMA-able buffer. */
+ struct sk_buff *skb;
+ /* DMA base bus-address of the descriptor buffer. */
+ dma_addr_t dmaaddr;
+};
+
+struct bcm43xx_dmaring {
+ struct bcm43xx_private *bcm;
+ /* Kernel virtual base address of the ring memory. */
+ struct bcm43xx_dmadesc *vbase;
+ /* DMA memory offset */
+ dma_addr_t memoffset;
+ /* (Unadjusted) DMA base bus-address of the ring memory. */
+ dma_addr_t dmabase;
+ /* Meta data about all descriptors. */
+ struct bcm43xx_dmadesc_meta *meta;
+ /* Number of descriptor slots in the ring. */
+ int nr_slots;
+ /* Number of used descriptor slots. */
+ int used_slots;
+ /* Currently used slot in the ring. */
+ int current_slot;
+ /* Marks to suspend/resume the queue. */
+ int suspend_mark;
+ int resume_mark;
+ /* Frameoffset in octets. */
+ u32 frameoffset;
+ /* Descriptor buffer size. */
+ u16 rx_buffersize;
+ /* The MMIO base register of the DMA controller, this
+ * ring is posted to.
+ */
+ u16 mmio_base;
+ u8 tx:1, /* TRUE, if this is a TX ring. */
+ suspended:1; /* TRUE, if transfers are suspended on this ring. */
+#ifdef CONFIG_BCM43XX_DEBUG
+ /* Maximum number of used slots. */
+ int max_used_slots;
+#endif /* CONFIG_BCM43XX_DEBUG*/
+};
+
+
+static inline
+u32 bcm43xx_dma_read(struct bcm43xx_dmaring *ring,
+ u16 offset)
+{
+ return bcm43xx_read32(ring->bcm, ring->mmio_base + offset);
+}
+
+static inline
+void bcm43xx_dma_write(struct bcm43xx_dmaring *ring,
+ u16 offset, u32 value)
+{
+ bcm43xx_write32(ring->bcm, ring->mmio_base + offset, value);
+}
+
+
+int bcm43xx_dma_init(struct bcm43xx_private *bcm);
+void bcm43xx_dma_free(struct bcm43xx_private *bcm);
+
+int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,
+ u16 dmacontroller_mmio_base);
+int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,
+ u16 dmacontroller_mmio_base);
+
+void bcm43xx_dma_tx_suspend(struct bcm43xx_dmaring *ring);
+void bcm43xx_dma_tx_resume(struct bcm43xx_dmaring *ring);
+
+void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status);
+
+int bcm43xx_dma_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb);
+void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring);
+
+
+#else /* CONFIG_BCM43XX_DMA */
+
+
+static inline
+int bcm43xx_dma_init(struct bcm43xx_private *bcm)
+{
+ return 0;
+}
+static inline
+void bcm43xx_dma_free(struct bcm43xx_private *bcm)
+{
+}
+static inline
+int bcm43xx_dmacontroller_rx_reset(struct bcm43xx_private *bcm,
+ u16 dmacontroller_mmio_base)
+{
+ return 0;
+}
+static inline
+int bcm43xx_dmacontroller_tx_reset(struct bcm43xx_private *bcm,
+ u16 dmacontroller_mmio_base)
+{
+ return 0;
+}
+static inline
+int bcm43xx_dma_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb)
+{
+ return 0;
+}
+static inline
+void bcm43xx_dma_handle_xmitstatus(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status)
+{
+}
+static inline
+void bcm43xx_dma_rx(struct bcm43xx_dmaring *ring)
+{
+}
+
+#endif /* CONFIG_BCM43XX_DMA */
+#endif /* BCM43xx_DMA_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c
new file mode 100644
index 000000000000..b3ffcf501311
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.c
@@ -0,0 +1,50 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ ethtool support
+
+ Copyright (c) 2006 Jason Lunz <lunz@falooley.org>
+
+ Some code in this file is derived from the 8139too.c driver
+ Copyright (C) 2002 Jeff Garzik
+
+ 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 "bcm43xx.h"
+#include "bcm43xx_ethtool.h"
+
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/version.h>
+
+
+static void bcm43xx_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(dev);
+
+ strncpy(info->driver, KBUILD_MODNAME, sizeof(info->driver));
+ strncpy(info->version, UTS_RELEASE, sizeof(info->version));
+ strncpy(info->bus_info, pci_name(bcm->pci_dev), ETHTOOL_BUSINFO_LEN);
+}
+
+struct ethtool_ops bcm43xx_ethtool_ops = {
+ .get_drvinfo = bcm43xx_get_drvinfo,
+ .get_link = ethtool_op_get_link,
+};
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h
new file mode 100644
index 000000000000..813704991f62
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_ethtool.h
@@ -0,0 +1,8 @@
+#ifndef BCM43xx_ETHTOOL_H_
+#define BCM43xx_ETHTOOL_H_
+
+#include <linux/ethtool.h>
+
+extern struct ethtool_ops bcm43xx_ethtool_ops;
+
+#endif /* BCM43xx_ETHTOOL_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c
new file mode 100644
index 000000000000..ad8e569d1faf
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.c
@@ -0,0 +1,337 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.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
+ 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 "bcm43xx.h"
+#include "bcm43xx_ilt.h"
+#include "bcm43xx_phy.h"
+
+
+/**** Initial Internal Lookup Tables ****/
+
+const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE] = {
+ 0xFEB93FFD, 0xFEC63FFD, /* 0 */
+ 0xFED23FFD, 0xFEDF3FFD,
+ 0xFEEC3FFE, 0xFEF83FFE,
+ 0xFF053FFE, 0xFF113FFE,
+ 0xFF1E3FFE, 0xFF2A3FFF, /* 8 */
+ 0xFF373FFF, 0xFF443FFF,
+ 0xFF503FFF, 0xFF5D3FFF,
+ 0xFF693FFF, 0xFF763FFF,
+ 0xFF824000, 0xFF8F4000, /* 16 */
+ 0xFF9B4000, 0xFFA84000,
+ 0xFFB54000, 0xFFC14000,
+ 0xFFCE4000, 0xFFDA4000,
+ 0xFFE74000, 0xFFF34000, /* 24 */
+ 0x00004000, 0x000D4000,
+ 0x00194000, 0x00264000,
+ 0x00324000, 0x003F4000,
+ 0x004B4000, 0x00584000, /* 32 */
+ 0x00654000, 0x00714000,
+ 0x007E4000, 0x008A3FFF,
+ 0x00973FFF, 0x00A33FFF,
+ 0x00B03FFF, 0x00BC3FFF, /* 40 */
+ 0x00C93FFF, 0x00D63FFF,
+ 0x00E23FFE, 0x00EF3FFE,
+ 0x00FB3FFE, 0x01083FFE,
+ 0x01143FFE, 0x01213FFD, /* 48 */
+ 0x012E3FFD, 0x013A3FFD,
+ 0x01473FFD,
+};
+
+const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE] = {
+ 0xDB93CB87, 0xD666CF64, /* 0 */
+ 0xD1FDD358, 0xCDA6D826,
+ 0xCA38DD9F, 0xC729E2B4,
+ 0xC469E88E, 0xC26AEE2B,
+ 0xC0DEF46C, 0xC073FA62, /* 8 */
+ 0xC01D00D5, 0xC0760743,
+ 0xC1560D1E, 0xC2E51369,
+ 0xC4ED18FF, 0xC7AC1ED7,
+ 0xCB2823B2, 0xCEFA28D9, /* 16 */
+ 0xD2F62D3F, 0xD7BB3197,
+ 0xDCE53568, 0xE1FE3875,
+ 0xE7D13B35, 0xED663D35,
+ 0xF39B3EC4, 0xF98E3FA7, /* 24 */
+ 0x00004000, 0x06723FA7,
+ 0x0C653EC4, 0x129A3D35,
+ 0x182F3B35, 0x1E023875,
+ 0x231B3568, 0x28453197, /* 32 */
+ 0x2D0A2D3F, 0x310628D9,
+ 0x34D823B2, 0x38541ED7,
+ 0x3B1318FF, 0x3D1B1369,
+ 0x3EAA0D1E, 0x3F8A0743, /* 40 */
+ 0x3FE300D5, 0x3F8DFA62,
+ 0x3F22F46C, 0x3D96EE2B,
+ 0x3B97E88E, 0x38D7E2B4,
+ 0x35C8DD9F, 0x325AD826, /* 48 */
+ 0x2E03D358, 0x299ACF64,
+ 0x246DCB87,
+};
+
+const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE] = {
+ 0x0082, 0x0082, 0x0102, 0x0182, /* 0 */
+ 0x0202, 0x0282, 0x0302, 0x0382,
+ 0x0402, 0x0482, 0x0502, 0x0582,
+ 0x05E2, 0x0662, 0x06E2, 0x0762,
+ 0x07E2, 0x0842, 0x08C2, 0x0942, /* 16 */
+ 0x09C2, 0x0A22, 0x0AA2, 0x0B02,
+ 0x0B82, 0x0BE2, 0x0C62, 0x0CC2,
+ 0x0D42, 0x0DA2, 0x0E02, 0x0E62,
+ 0x0EE2, 0x0F42, 0x0FA2, 0x1002, /* 32 */
+ 0x1062, 0x10C2, 0x1122, 0x1182,
+ 0x11E2, 0x1242, 0x12A2, 0x12E2,
+ 0x1342, 0x13A2, 0x1402, 0x1442,
+ 0x14A2, 0x14E2, 0x1542, 0x1582, /* 48 */
+ 0x15E2, 0x1622, 0x1662, 0x16C1,
+ 0x1701, 0x1741, 0x1781, 0x17E1,
+ 0x1821, 0x1861, 0x18A1, 0x18E1,
+ 0x1921, 0x1961, 0x19A1, 0x19E1, /* 64 */
+ 0x1A21, 0x1A61, 0x1AA1, 0x1AC1,
+ 0x1B01, 0x1B41, 0x1B81, 0x1BA1,
+ 0x1BE1, 0x1C21, 0x1C41, 0x1C81,
+ 0x1CA1, 0x1CE1, 0x1D01, 0x1D41, /* 80 */
+ 0x1D61, 0x1DA1, 0x1DC1, 0x1E01,
+ 0x1E21, 0x1E61, 0x1E81, 0x1EA1,
+ 0x1EE1, 0x1F01, 0x1F21, 0x1F41,
+ 0x1F81, 0x1FA1, 0x1FC1, 0x1FE1, /* 96 */
+ 0x2001, 0x2041, 0x2061, 0x2081,
+ 0x20A1, 0x20C1, 0x20E1, 0x2101,
+ 0x2121, 0x2141, 0x2161, 0x2181,
+ 0x21A1, 0x21C1, 0x21E1, 0x2201, /* 112 */
+ 0x2221, 0x2241, 0x2261, 0x2281,
+ 0x22A1, 0x22C1, 0x22C1, 0x22E1,
+ 0x2301, 0x2321, 0x2341, 0x2361,
+ 0x2361, 0x2381, 0x23A1, 0x23C1, /* 128 */
+ 0x23E1, 0x23E1, 0x2401, 0x2421,
+ 0x2441, 0x2441, 0x2461, 0x2481,
+ 0x2481, 0x24A1, 0x24C1, 0x24C1,
+ 0x24E1, 0x2501, 0x2501, 0x2521, /* 144 */
+ 0x2541, 0x2541, 0x2561, 0x2561,
+ 0x2581, 0x25A1, 0x25A1, 0x25C1,
+ 0x25C1, 0x25E1, 0x2601, 0x2601,
+ 0x2621, 0x2621, 0x2641, 0x2641, /* 160 */
+ 0x2661, 0x2661, 0x2681, 0x2681,
+ 0x26A1, 0x26A1, 0x26C1, 0x26C1,
+ 0x26E1, 0x26E1, 0x2701, 0x2701,
+ 0x2721, 0x2721, 0x2740, 0x2740, /* 176 */
+ 0x2760, 0x2760, 0x2780, 0x2780,
+ 0x2780, 0x27A0, 0x27A0, 0x27C0,
+ 0x27C0, 0x27E0, 0x27E0, 0x27E0,
+ 0x2800, 0x2800, 0x2820, 0x2820, /* 192 */
+ 0x2820, 0x2840, 0x2840, 0x2840,
+ 0x2860, 0x2860, 0x2880, 0x2880,
+ 0x2880, 0x28A0, 0x28A0, 0x28A0,
+ 0x28C0, 0x28C0, 0x28C0, 0x28E0, /* 208 */
+ 0x28E0, 0x28E0, 0x2900, 0x2900,
+ 0x2900, 0x2920, 0x2920, 0x2920,
+ 0x2940, 0x2940, 0x2940, 0x2960,
+ 0x2960, 0x2960, 0x2960, 0x2980, /* 224 */
+ 0x2980, 0x2980, 0x29A0, 0x29A0,
+ 0x29A0, 0x29A0, 0x29C0, 0x29C0,
+ 0x29C0, 0x29E0, 0x29E0, 0x29E0,
+ 0x29E0, 0x2A00, 0x2A00, 0x2A00, /* 240 */
+ 0x2A00, 0x2A20, 0x2A20, 0x2A20,
+ 0x2A20, 0x2A40, 0x2A40, 0x2A40,
+ 0x2A40, 0x2A60, 0x2A60, 0x2A60,
+};
+
+const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE] = {
+ 0x0089, 0x02E9, 0x0409, 0x04E9, /* 0 */
+ 0x05A9, 0x0669, 0x0709, 0x0789,
+ 0x0829, 0x08A9, 0x0929, 0x0989,
+ 0x0A09, 0x0A69, 0x0AC9, 0x0B29,
+ 0x0BA9, 0x0BE9, 0x0C49, 0x0CA9, /* 16 */
+ 0x0D09, 0x0D69, 0x0DA9, 0x0E09,
+ 0x0E69, 0x0EA9, 0x0F09, 0x0F49,
+ 0x0FA9, 0x0FE9, 0x1029, 0x1089,
+ 0x10C9, 0x1109, 0x1169, 0x11A9, /* 32 */
+ 0x11E9, 0x1229, 0x1289, 0x12C9,
+ 0x1309, 0x1349, 0x1389, 0x13C9,
+ 0x1409, 0x1449, 0x14A9, 0x14E9,
+ 0x1529, 0x1569, 0x15A9, 0x15E9, /* 48 */
+ 0x1629, 0x1669, 0x16A9, 0x16E8,
+ 0x1728, 0x1768, 0x17A8, 0x17E8,
+ 0x1828, 0x1868, 0x18A8, 0x18E8,
+ 0x1928, 0x1968, 0x19A8, 0x19E8, /* 64 */
+ 0x1A28, 0x1A68, 0x1AA8, 0x1AE8,
+ 0x1B28, 0x1B68, 0x1BA8, 0x1BE8,
+ 0x1C28, 0x1C68, 0x1CA8, 0x1CE8,
+ 0x1D28, 0x1D68, 0x1DC8, 0x1E08, /* 80 */
+ 0x1E48, 0x1E88, 0x1EC8, 0x1F08,
+ 0x1F48, 0x1F88, 0x1FE8, 0x2028,
+ 0x2068, 0x20A8, 0x2108, 0x2148,
+ 0x2188, 0x21C8, 0x2228, 0x2268, /* 96 */
+ 0x22C8, 0x2308, 0x2348, 0x23A8,
+ 0x23E8, 0x2448, 0x24A8, 0x24E8,
+ 0x2548, 0x25A8, 0x2608, 0x2668,
+ 0x26C8, 0x2728, 0x2787, 0x27E7, /* 112 */
+ 0x2847, 0x28C7, 0x2947, 0x29A7,
+ 0x2A27, 0x2AC7, 0x2B47, 0x2BE7,
+ 0x2CA7, 0x2D67, 0x2E47, 0x2F67,
+ 0x3247, 0x3526, 0x3646, 0x3726, /* 128 */
+ 0x3806, 0x38A6, 0x3946, 0x39E6,
+ 0x3A66, 0x3AE6, 0x3B66, 0x3BC6,
+ 0x3C45, 0x3CA5, 0x3D05, 0x3D85,
+ 0x3DE5, 0x3E45, 0x3EA5, 0x3EE5, /* 144 */
+ 0x3F45, 0x3FA5, 0x4005, 0x4045,
+ 0x40A5, 0x40E5, 0x4145, 0x4185,
+ 0x41E5, 0x4225, 0x4265, 0x42C5,
+ 0x4305, 0x4345, 0x43A5, 0x43E5, /* 160 */
+ 0x4424, 0x4464, 0x44C4, 0x4504,
+ 0x4544, 0x4584, 0x45C4, 0x4604,
+ 0x4644, 0x46A4, 0x46E4, 0x4724,
+ 0x4764, 0x47A4, 0x47E4, 0x4824, /* 176 */
+ 0x4864, 0x48A4, 0x48E4, 0x4924,
+ 0x4964, 0x49A4, 0x49E4, 0x4A24,
+ 0x4A64, 0x4AA4, 0x4AE4, 0x4B23,
+ 0x4B63, 0x4BA3, 0x4BE3, 0x4C23, /* 192 */
+ 0x4C63, 0x4CA3, 0x4CE3, 0x4D23,
+ 0x4D63, 0x4DA3, 0x4DE3, 0x4E23,
+ 0x4E63, 0x4EA3, 0x4EE3, 0x4F23,
+ 0x4F63, 0x4FC3, 0x5003, 0x5043, /* 208 */
+ 0x5083, 0x50C3, 0x5103, 0x5143,
+ 0x5183, 0x51E2, 0x5222, 0x5262,
+ 0x52A2, 0x52E2, 0x5342, 0x5382,
+ 0x53C2, 0x5402, 0x5462, 0x54A2, /* 224 */
+ 0x5502, 0x5542, 0x55A2, 0x55E2,
+ 0x5642, 0x5682, 0x56E2, 0x5722,
+ 0x5782, 0x57E1, 0x5841, 0x58A1,
+ 0x5901, 0x5961, 0x59C1, 0x5A21, /* 240 */
+ 0x5AA1, 0x5B01, 0x5B81, 0x5BE1,
+ 0x5C61, 0x5D01, 0x5D80, 0x5E20,
+ 0x5EE0, 0x5FA0, 0x6080, 0x61C0,
+};
+
+const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE] = {
+ 0x0001, 0x0001, 0x0001, 0xFFFE,
+ 0xFFFE, 0x3FFF, 0x1000, 0x0393,
+};
+
+const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE] = {
+ 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36,
+ 0x4C4C, 0x4C4C, 0x4C4C, 0x2D36,
+};
+
+const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE] = {
+ 0x013C, 0x01F5, 0x031A, 0x0631,
+ 0x0001, 0x0001, 0x0001, 0x0001,
+};
+
+const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE] = {
+ 0x5484, 0x3C40, 0x0000, 0x0000,
+ 0x0000, 0x0000, 0x0000, 0x0000,
+};
+
+const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE] = {
+ 0x6C77, 0x5162, 0x3B40, 0x3335, /* 0 */
+ 0x2F2D, 0x2A2A, 0x2527, 0x1F21,
+ 0x1A1D, 0x1719, 0x1616, 0x1414,
+ 0x1414, 0x1400, 0x1414, 0x1614,
+ 0x1716, 0x1A19, 0x1F1D, 0x2521, /* 16 */
+ 0x2A27, 0x2F2A, 0x332D, 0x3B35,
+ 0x5140, 0x6C62, 0x0077,
+};
+
+const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE] = {
+ 0xD8DD, 0xCBD4, 0xBCC0, 0XB6B7, /* 0 */
+ 0xB2B0, 0xADAD, 0xA7A9, 0x9FA1,
+ 0x969B, 0x9195, 0x8F8F, 0x8A8A,
+ 0x8A8A, 0x8A00, 0x8A8A, 0x8F8A,
+ 0x918F, 0x9695, 0x9F9B, 0xA7A1, /* 16 */
+ 0xADA9, 0xB2AD, 0xB6B0, 0xBCB7,
+ 0xCBC0, 0xD8D4, 0x00DD,
+};
+
+const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE] = {
+ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 0 */
+ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4,
+ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4,
+ 0xA4A4, 0xA400, 0xA4A4, 0xA4A4,
+ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4, /* 16 */
+ 0xA4A4, 0xA4A4, 0xA4A4, 0xA4A4,
+ 0xA4A4, 0xA4A4, 0x00A4,
+};
+
+const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE] = {
+ 0x007A, 0x0075, 0x0071, 0x006C, /* 0 */
+ 0x0067, 0x0063, 0x005E, 0x0059,
+ 0x0054, 0x0050, 0x004B, 0x0046,
+ 0x0042, 0x003D, 0x003D, 0x003D,
+ 0x003D, 0x003D, 0x003D, 0x003D, /* 16 */
+ 0x003D, 0x003D, 0x003D, 0x003D,
+ 0x003D, 0x003D, 0x0000, 0x003D,
+ 0x003D, 0x003D, 0x003D, 0x003D,
+ 0x003D, 0x003D, 0x003D, 0x003D, /* 32 */
+ 0x003D, 0x003D, 0x003D, 0x003D,
+ 0x0042, 0x0046, 0x004B, 0x0050,
+ 0x0054, 0x0059, 0x005E, 0x0063,
+ 0x0067, 0x006C, 0x0071, 0x0075, /* 48 */
+ 0x007A,
+};
+
+const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE] = {
+ 0x00DE, 0x00DC, 0x00DA, 0x00D8, /* 0 */
+ 0x00D6, 0x00D4, 0x00D2, 0x00CF,
+ 0x00CD, 0x00CA, 0x00C7, 0x00C4,
+ 0x00C1, 0x00BE, 0x00BE, 0x00BE,
+ 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 16 */
+ 0x00BE, 0x00BE, 0x00BE, 0x00BE,
+ 0x00BE, 0x00BE, 0x0000, 0x00BE,
+ 0x00BE, 0x00BE, 0x00BE, 0x00BE,
+ 0x00BE, 0x00BE, 0x00BE, 0x00BE, /* 32 */
+ 0x00BE, 0x00BE, 0x00BE, 0x00BE,
+ 0x00C1, 0x00C4, 0x00C7, 0x00CA,
+ 0x00CD, 0x00CF, 0x00D2, 0x00D4,
+ 0x00D6, 0x00D8, 0x00DA, 0x00DC, /* 48 */
+ 0x00DE,
+};
+
+/**** Helper functions to access the device Internal Lookup Tables ****/
+
+void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val)
+{
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset);
+ mmiowb();
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, val);
+ } else {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset);
+ mmiowb();
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_DATA1, val);
+ }
+}
+
+u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset)
+{
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A) {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, offset);
+ return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_A_DATA1);
+ } else {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_G_CTRL, offset);
+ return bcm43xx_phy_read(bcm, BCM43xx_PHY_ILT_G_DATA1);
+ }
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h
new file mode 100644
index 000000000000..464521abf73c
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_ilt.h
@@ -0,0 +1,32 @@
+#ifndef BCM43xx_ILT_H_
+#define BCM43xx_ILT_H_
+
+#define BCM43xx_ILT_ROTOR_SIZE 53
+extern const u32 bcm43xx_ilt_rotor[BCM43xx_ILT_ROTOR_SIZE];
+#define BCM43xx_ILT_RETARD_SIZE 53
+extern const u32 bcm43xx_ilt_retard[BCM43xx_ILT_RETARD_SIZE];
+#define BCM43xx_ILT_FINEFREQA_SIZE 256
+extern const u16 bcm43xx_ilt_finefreqa[BCM43xx_ILT_FINEFREQA_SIZE];
+#define BCM43xx_ILT_FINEFREQG_SIZE 256
+extern const u16 bcm43xx_ilt_finefreqg[BCM43xx_ILT_FINEFREQG_SIZE];
+#define BCM43xx_ILT_NOISEA2_SIZE 8
+extern const u16 bcm43xx_ilt_noisea2[BCM43xx_ILT_NOISEA2_SIZE];
+#define BCM43xx_ILT_NOISEA3_SIZE 8
+extern const u16 bcm43xx_ilt_noisea3[BCM43xx_ILT_NOISEA3_SIZE];
+#define BCM43xx_ILT_NOISEG1_SIZE 8
+extern const u16 bcm43xx_ilt_noiseg1[BCM43xx_ILT_NOISEG1_SIZE];
+#define BCM43xx_ILT_NOISEG2_SIZE 8
+extern const u16 bcm43xx_ilt_noiseg2[BCM43xx_ILT_NOISEG2_SIZE];
+#define BCM43xx_ILT_NOISESCALEG_SIZE 27
+extern const u16 bcm43xx_ilt_noisescaleg1[BCM43xx_ILT_NOISESCALEG_SIZE];
+extern const u16 bcm43xx_ilt_noisescaleg2[BCM43xx_ILT_NOISESCALEG_SIZE];
+extern const u16 bcm43xx_ilt_noisescaleg3[BCM43xx_ILT_NOISESCALEG_SIZE];
+#define BCM43xx_ILT_SIGMASQR_SIZE 53
+extern const u16 bcm43xx_ilt_sigmasqr1[BCM43xx_ILT_SIGMASQR_SIZE];
+extern const u16 bcm43xx_ilt_sigmasqr2[BCM43xx_ILT_SIGMASQR_SIZE];
+
+
+void bcm43xx_ilt_write(struct bcm43xx_private *bcm, u16 offset, u16 val);
+u16 bcm43xx_ilt_read(struct bcm43xx_private *bcm, u16 offset);
+
+#endif /* BCM43xx_ILT_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_leds.c b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c
new file mode 100644
index 000000000000..4b2c02c0b31e
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_leds.c
@@ -0,0 +1,293 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.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
+ 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 "bcm43xx_leds.h"
+#include "bcm43xx.h"
+
+#include <asm/bitops.h>
+
+
+static void bcm43xx_led_changestate(struct bcm43xx_led *led)
+{
+ struct bcm43xx_private *bcm = led->bcm;
+ const int index = bcm43xx_led_index(led);
+ const u16 mask = (1 << index);
+ u16 ledctl;
+
+ assert(index >= 0 && index < BCM43xx_NR_LEDS);
+ assert(led->blink_interval);
+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
+ ledctl = (ledctl & mask) ? (ledctl & ~mask) : (ledctl | mask);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
+}
+
+static void bcm43xx_led_blink(unsigned long d)
+{
+ struct bcm43xx_led *led = (struct bcm43xx_led *)d;
+ struct bcm43xx_private *bcm = led->bcm;
+ unsigned long flags;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (led->blink_interval) {
+ bcm43xx_led_changestate(led);
+ mod_timer(&led->blink_timer, jiffies + led->blink_interval);
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+static void bcm43xx_led_blink_start(struct bcm43xx_led *led,
+ unsigned long interval)
+{
+ if (led->blink_interval)
+ return;
+ led->blink_interval = interval;
+ bcm43xx_led_changestate(led);
+ led->blink_timer.expires = jiffies + interval;
+ add_timer(&led->blink_timer);
+}
+
+static void bcm43xx_led_blink_stop(struct bcm43xx_led *led, int sync)
+{
+ struct bcm43xx_private *bcm = led->bcm;
+ const int index = bcm43xx_led_index(led);
+ u16 ledctl;
+
+ if (!led->blink_interval)
+ return;
+ if (unlikely(sync))
+ del_timer_sync(&led->blink_timer);
+ else
+ del_timer(&led->blink_timer);
+ led->blink_interval = 0;
+
+ /* Make sure the LED is turned off. */
+ assert(index >= 0 && index < BCM43xx_NR_LEDS);
+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
+ if (led->activelow)
+ ledctl |= (1 << index);
+ else
+ ledctl &= ~(1 << index);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
+}
+
+static void bcm43xx_led_init_hardcoded(struct bcm43xx_private *bcm,
+ struct bcm43xx_led *led,
+ int led_index)
+{
+ /* This function is called, if the behaviour (and activelow)
+ * information for a LED is missing in the SPROM.
+ * We hardcode the behaviour values for various devices here.
+ * Note that the BCM43xx_LED_TEST_XXX behaviour values can
+ * be used to figure out which led is mapped to which index.
+ */
+
+ switch (led_index) {
+ case 0:
+ led->behaviour = BCM43xx_LED_ACTIVITY;
+ if (bcm->board_vendor == PCI_VENDOR_ID_COMPAQ)
+ led->behaviour = BCM43xx_LED_RADIO_ALL;
+ break;
+ case 1:
+ led->behaviour = BCM43xx_LED_RADIO_B;
+ if (bcm->board_vendor == PCI_VENDOR_ID_ASUSTEK)
+ led->behaviour = BCM43xx_LED_ASSOC;
+ break;
+ case 2:
+ led->behaviour = BCM43xx_LED_RADIO_A;
+ break;
+ case 3:
+ led->behaviour = BCM43xx_LED_OFF;
+ break;
+ default:
+ assert(0);
+ }
+}
+
+int bcm43xx_leds_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_led *led;
+ u8 sprom[4];
+ int i;
+
+ sprom[0] = bcm->sprom.wl0gpio0;
+ sprom[1] = bcm->sprom.wl0gpio1;
+ sprom[2] = bcm->sprom.wl0gpio2;
+ sprom[3] = bcm->sprom.wl0gpio3;
+
+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {
+ led = &(bcm->leds[i]);
+ led->bcm = bcm;
+ setup_timer(&led->blink_timer,
+ bcm43xx_led_blink,
+ (unsigned long)led);
+
+ if (sprom[i] == 0xFF) {
+ bcm43xx_led_init_hardcoded(bcm, led, i);
+ } else {
+ led->behaviour = sprom[i] & BCM43xx_LED_BEHAVIOUR;
+ led->activelow = !!(sprom[i] & BCM43xx_LED_ACTIVELOW);
+ }
+ }
+
+ return 0;
+}
+
+void bcm43xx_leds_exit(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_led *led;
+ int i;
+
+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {
+ led = &(bcm->leds[i]);
+ bcm43xx_led_blink_stop(led, 1);
+ }
+ bcm43xx_leds_switch_all(bcm, 0);
+}
+
+void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity)
+{
+ struct bcm43xx_led *led;
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ const int transferring = (jiffies - bcm->stats.last_tx) < BCM43xx_LED_XFER_THRES;
+ int i, turn_on;
+ unsigned long interval = 0;
+ u16 ledctl;
+
+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {
+ led = &(bcm->leds[i]);
+
+ turn_on = 0;
+ switch (led->behaviour) {
+ case BCM43xx_LED_INACTIVE:
+ continue;
+ case BCM43xx_LED_OFF:
+ break;
+ case BCM43xx_LED_ON:
+ turn_on = 1;
+ break;
+ case BCM43xx_LED_ACTIVITY:
+ turn_on = activity;
+ break;
+ case BCM43xx_LED_RADIO_ALL:
+ turn_on = radio->enabled;
+ break;
+ case BCM43xx_LED_RADIO_A:
+ turn_on = (radio->enabled && phy->type == BCM43xx_PHYTYPE_A);
+ break;
+ case BCM43xx_LED_RADIO_B:
+ turn_on = (radio->enabled &&
+ (phy->type == BCM43xx_PHYTYPE_B ||
+ phy->type == BCM43xx_PHYTYPE_G));
+ break;
+ case BCM43xx_LED_MODE_BG:
+ if (phy->type == BCM43xx_PHYTYPE_G &&
+ 1/*FIXME: using G rates.*/)
+ turn_on = 1;
+ break;
+ case BCM43xx_LED_TRANSFER:
+ if (transferring)
+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);
+ else
+ bcm43xx_led_blink_stop(led, 0);
+ continue;
+ case BCM43xx_LED_APTRANSFER:
+ if (bcm->ieee->iw_mode == IW_MODE_MASTER) {
+ if (transferring) {
+ interval = BCM43xx_LEDBLINK_FAST;
+ turn_on = 1;
+ }
+ } else {
+ turn_on = 1;
+ if (0/*TODO: not assoc*/)
+ interval = BCM43xx_LEDBLINK_SLOW;
+ else if (transferring)
+ interval = BCM43xx_LEDBLINK_FAST;
+ else
+ turn_on = 0;
+ }
+ if (turn_on)
+ bcm43xx_led_blink_start(led, interval);
+ else
+ bcm43xx_led_blink_stop(led, 0);
+ continue;
+ case BCM43xx_LED_WEIRD:
+ //TODO
+ break;
+ case BCM43xx_LED_ASSOC:
+ if (bcm->softmac->associated)
+ turn_on = 1;
+ break;
+#ifdef CONFIG_BCM43XX_DEBUG
+ case BCM43xx_LED_TEST_BLINKSLOW:
+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_SLOW);
+ continue;
+ case BCM43xx_LED_TEST_BLINKMEDIUM:
+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_MEDIUM);
+ continue;
+ case BCM43xx_LED_TEST_BLINKFAST:
+ bcm43xx_led_blink_start(led, BCM43xx_LEDBLINK_FAST);
+ continue;
+#endif /* CONFIG_BCM43XX_DEBUG */
+ default:
+ assert(0);
+ };
+
+ if (led->activelow)
+ turn_on = !turn_on;
+ if (turn_on)
+ ledctl |= (1 << i);
+ else
+ ledctl &= ~(1 << i);
+ }
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
+}
+
+void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on)
+{
+ struct bcm43xx_led *led;
+ u16 ledctl;
+ int i;
+ int bit_on;
+
+ ledctl = bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_CONTROL);
+ for (i = 0; i < BCM43xx_NR_LEDS; i++) {
+ led = &(bcm->leds[i]);
+ if (led->behaviour == BCM43xx_LED_INACTIVE)
+ continue;
+ if (on)
+ bit_on = led->activelow ? 0 : 1;
+ else
+ bit_on = led->activelow ? 1 : 0;
+ if (bit_on)
+ ledctl |= (1 << i);
+ else
+ ledctl &= ~(1 << i);
+ }
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_CONTROL, ledctl);
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_leds.h b/drivers/net/wireless/bcm43xx/bcm43xx_leds.h
new file mode 100644
index 000000000000..d3716cf3aebc
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_leds.h
@@ -0,0 +1,56 @@
+#ifndef BCM43xx_LEDS_H_
+#define BCM43xx_LEDS_H_
+
+#include <linux/types.h>
+#include <linux/timer.h>
+
+
+struct bcm43xx_led {
+ u8 behaviour:7;
+ u8 activelow:1;
+
+ struct bcm43xx_private *bcm;
+ struct timer_list blink_timer;
+ unsigned long blink_interval;
+};
+#define bcm43xx_led_index(led) ((int)((led) - (led)->bcm->leds))
+
+/* Delay between state changes when blinking in jiffies */
+#define BCM43xx_LEDBLINK_SLOW (HZ / 1)
+#define BCM43xx_LEDBLINK_MEDIUM (HZ / 4)
+#define BCM43xx_LEDBLINK_FAST (HZ / 8)
+
+#define BCM43xx_LED_XFER_THRES (HZ / 100)
+
+#define BCM43xx_LED_BEHAVIOUR 0x7F
+#define BCM43xx_LED_ACTIVELOW 0x80
+enum { /* LED behaviour values */
+ BCM43xx_LED_OFF,
+ BCM43xx_LED_ON,
+ BCM43xx_LED_ACTIVITY,
+ BCM43xx_LED_RADIO_ALL,
+ BCM43xx_LED_RADIO_A,
+ BCM43xx_LED_RADIO_B,
+ BCM43xx_LED_MODE_BG,
+ BCM43xx_LED_TRANSFER,
+ BCM43xx_LED_APTRANSFER,
+ BCM43xx_LED_WEIRD,//FIXME
+ BCM43xx_LED_ASSOC,
+ BCM43xx_LED_INACTIVE,
+
+ /* Behaviour values for testing.
+ * With these values it is easier to figure out
+ * the real behaviour of leds, in case the SPROM
+ * is missing information.
+ */
+ BCM43xx_LED_TEST_BLINKSLOW,
+ BCM43xx_LED_TEST_BLINKMEDIUM,
+ BCM43xx_LED_TEST_BLINKFAST,
+};
+
+int bcm43xx_leds_init(struct bcm43xx_private *bcm);
+void bcm43xx_leds_exit(struct bcm43xx_private *bcm);
+void bcm43xx_leds_update(struct bcm43xx_private *bcm, int activity);
+void bcm43xx_leds_switch_all(struct bcm43xx_private *bcm, int on);
+
+#endif /* BCM43xx_LEDS_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
new file mode 100644
index 000000000000..c37371fc9e01
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -0,0 +1,3973 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; 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/delay.h>
+#include <linux/init.h>
+#include <linux/moduleparam.h>
+#include <linux/if_arp.h>
+#include <linux/etherdevice.h>
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include <linux/wireless.h>
+#include <linux/workqueue.h>
+#include <linux/skbuff.h>
+#include <linux/dma-mapping.h>
+#include <net/iw_handler.h>
+
+#include "bcm43xx.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_debugfs.h"
+#include "bcm43xx_radio.h"
+#include "bcm43xx_phy.h"
+#include "bcm43xx_dma.h"
+#include "bcm43xx_pio.h"
+#include "bcm43xx_power.h"
+#include "bcm43xx_wx.h"
+#include "bcm43xx_ethtool.h"
+#include "bcm43xx_xmit.h"
+
+
+MODULE_DESCRIPTION("Broadcom BCM43xx wireless driver");
+MODULE_AUTHOR("Martin Langer");
+MODULE_AUTHOR("Stefano Brivio");
+MODULE_AUTHOR("Michael Buesch");
+MODULE_LICENSE("GPL");
+
+#ifdef CONFIG_BCM947XX
+extern char *nvram_get(char *name);
+#endif
+
+#if defined(CONFIG_BCM43XX_DMA) && defined(CONFIG_BCM43XX_PIO)
+static int modparam_pio;
+module_param_named(pio, modparam_pio, int, 0444);
+MODULE_PARM_DESC(pio, "enable(1) / disable(0) PIO mode");
+#elif defined(CONFIG_BCM43XX_DMA)
+# define modparam_pio 0
+#elif defined(CONFIG_BCM43XX_PIO)
+# define modparam_pio 1
+#endif
+
+static int modparam_bad_frames_preempt;
+module_param_named(bad_frames_preempt, modparam_bad_frames_preempt, int, 0444);
+MODULE_PARM_DESC(bad_frames_preempt, "enable(1) / disable(0) Bad Frames Preemption");
+
+static int modparam_short_retry = BCM43xx_DEFAULT_SHORT_RETRY_LIMIT;
+module_param_named(short_retry, modparam_short_retry, int, 0444);
+MODULE_PARM_DESC(short_retry, "Short-Retry-Limit (0 - 15)");
+
+static int modparam_long_retry = BCM43xx_DEFAULT_LONG_RETRY_LIMIT;
+module_param_named(long_retry, modparam_long_retry, int, 0444);
+MODULE_PARM_DESC(long_retry, "Long-Retry-Limit (0 - 15)");
+
+static int modparam_locale = -1;
+module_param_named(locale, modparam_locale, int, 0444);
+MODULE_PARM_DESC(country, "Select LocaleCode 0-11 (For travelers)");
+
+static int modparam_noleds;
+module_param_named(noleds, modparam_noleds, int, 0444);
+MODULE_PARM_DESC(noleds, "Turn off all LED activity");
+
+#ifdef CONFIG_BCM43XX_DEBUG
+static char modparam_fwpostfix[64];
+module_param_string(fwpostfix, modparam_fwpostfix, 64, 0444);
+MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for debugging.");
+#else
+# define modparam_fwpostfix ""
+#endif /* CONFIG_BCM43XX_DEBUG*/
+
+
+/* If you want to debug with just a single device, enable this,
+ * where the string is the pci device ID (as given by the kernel's
+ * pci_name function) of the device to be used.
+ */
+//#define DEBUG_SINGLE_DEVICE_ONLY "0001:11:00.0"
+
+/* If you want to enable printing of each MMIO access, enable this. */
+//#define DEBUG_ENABLE_MMIO_PRINT
+
+/* If you want to enable printing of MMIO access within
+ * ucode/pcm upload, initvals write, enable this.
+ */
+//#define DEBUG_ENABLE_UCODE_MMIO_PRINT
+
+/* If you want to enable printing of PCI Config Space access, enable this */
+//#define DEBUG_ENABLE_PCILOG
+
+
+/* Detailed list maintained at:
+ * http://openfacts.berlios.de/index-en.phtml?title=Bcm43xxDevices
+ */
+ static struct pci_device_id bcm43xx_pci_tbl[] = {
+ /* Broadcom 4303 802.11b */
+ { PCI_VENDOR_ID_BROADCOM, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 4307 802.11b */
+ { PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 4318 802.11b/g */
+ { PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 4306 802.11b/g */
+ { PCI_VENDOR_ID_BROADCOM, 0x4320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 4306 802.11a */
+// { PCI_VENDOR_ID_BROADCOM, 0x4321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 4309 802.11a/b/g */
+ { PCI_VENDOR_ID_BROADCOM, 0x4324, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ /* Broadcom 43XG 802.11b/g */
+ { PCI_VENDOR_ID_BROADCOM, 0x4325, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+#ifdef CONFIG_BCM947XX
+ /* SB bus on BCM947xx */
+ { PCI_VENDOR_ID_BROADCOM, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+#endif
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, bcm43xx_pci_tbl);
+
+static void bcm43xx_ram_write(struct bcm43xx_private *bcm, u16 offset, u32 val)
+{
+ u32 status;
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ if (!(status & BCM43xx_SBF_XFER_REG_BYTESWAP))
+ val = swab32(val);
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_CONTROL, offset);
+ mmiowb();
+ bcm43xx_write32(bcm, BCM43xx_MMIO_RAM_DATA, val);
+}
+
+static inline
+void bcm43xx_shm_control_word(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset)
+{
+ u32 control;
+
+ /* "offset" is the WORD offset. */
+
+ control = routing;
+ control <<= 16;
+ control |= offset;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_CONTROL, control);
+}
+
+u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset)
+{
+ u32 ret;
+
+ if (routing == BCM43xx_SHM_SHARED) {
+ if (offset & 0x0003) {
+ /* Unaligned access */
+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);
+ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED);
+ ret <<= 16;
+ bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1);
+ ret |= bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA);
+
+ return ret;
+ }
+ offset >>= 2;
+ }
+ bcm43xx_shm_control_word(bcm, routing, offset);
+ ret = bcm43xx_read32(bcm, BCM43xx_MMIO_SHM_DATA);
+
+ return ret;
+}
+
+u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset)
+{
+ u16 ret;
+
+ if (routing == BCM43xx_SHM_SHARED) {
+ if (offset & 0x0003) {
+ /* Unaligned access */
+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);
+ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED);
+
+ return ret;
+ }
+ offset >>= 2;
+ }
+ bcm43xx_shm_control_word(bcm, routing, offset);
+ ret = bcm43xx_read16(bcm, BCM43xx_MMIO_SHM_DATA);
+
+ return ret;
+}
+
+void bcm43xx_shm_write32(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset,
+ u32 value)
+{
+ if (routing == BCM43xx_SHM_SHARED) {
+ if (offset & 0x0003) {
+ /* Unaligned access */
+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED,
+ (value >> 16) & 0xffff);
+ mmiowb();
+ bcm43xx_shm_control_word(bcm, routing, (offset >> 2) + 1);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA,
+ value & 0xffff);
+ return;
+ }
+ offset >>= 2;
+ }
+ bcm43xx_shm_control_word(bcm, routing, offset);
+ mmiowb();
+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, value);
+}
+
+void bcm43xx_shm_write16(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset,
+ u16 value)
+{
+ if (routing == BCM43xx_SHM_SHARED) {
+ if (offset & 0x0003) {
+ /* Unaligned access */
+ bcm43xx_shm_control_word(bcm, routing, offset >> 2);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA_UNALIGNED,
+ value);
+ return;
+ }
+ offset >>= 2;
+ }
+ bcm43xx_shm_control_word(bcm, routing, offset);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_SHM_DATA, value);
+}
+
+void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf)
+{
+ /* We need to be careful. As we read the TSF from multiple
+ * registers, we should take care of register overflows.
+ * In theory, the whole tsf read process should be atomic.
+ * We try to be atomic here, by restaring the read process,
+ * if any of the high registers changed (overflew).
+ */
+ if (bcm->current_core->rev >= 3) {
+ u32 low, high, high2;
+
+ do {
+ high = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH);
+ low = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW);
+ high2 = bcm43xx_read32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH);
+ } while (unlikely(high != high2));
+
+ *tsf = high;
+ *tsf <<= 32;
+ *tsf |= low;
+ } else {
+ u64 tmp;
+ u16 v0, v1, v2, v3;
+ u16 test1, test2, test3;
+
+ do {
+ v3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3);
+ v2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2);
+ v1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1);
+ v0 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_0);
+
+ test3 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_3);
+ test2 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_2);
+ test1 = bcm43xx_read16(bcm, BCM43xx_MMIO_TSF_1);
+ } while (v3 != test3 || v2 != test2 || v1 != test1);
+
+ *tsf = v3;
+ *tsf <<= 48;
+ tmp = v2;
+ tmp <<= 32;
+ *tsf |= tmp;
+ tmp = v1;
+ tmp <<= 16;
+ *tsf |= tmp;
+ *tsf |= v0;
+ }
+}
+
+void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf)
+{
+ u32 status;
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ status |= BCM43xx_SBF_TIME_UPDATE;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+ mmiowb();
+
+ /* Be careful with the in-progress timer.
+ * First zero out the low register, so we have a full
+ * register-overflow duration to complete the operation.
+ */
+ if (bcm->current_core->rev >= 3) {
+ u32 lo = (tsf & 0x00000000FFFFFFFFULL);
+ u32 hi = (tsf & 0xFFFFFFFF00000000ULL) >> 32;
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, 0);
+ mmiowb();
+ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_HIGH, hi);
+ mmiowb();
+ bcm43xx_write32(bcm, BCM43xx_MMIO_REV3PLUS_TSF_LOW, lo);
+ } else {
+ u16 v0 = (tsf & 0x000000000000FFFFULL);
+ u16 v1 = (tsf & 0x00000000FFFF0000ULL) >> 16;
+ u16 v2 = (tsf & 0x0000FFFF00000000ULL) >> 32;
+ u16 v3 = (tsf & 0xFFFF000000000000ULL) >> 48;
+
+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, 0);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_3, v3);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_2, v2);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_1, v1);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_TSF_0, v0);
+ }
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ status &= ~BCM43xx_SBF_TIME_UPDATE;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+}
+
+static
+void bcm43xx_macfilter_set(struct bcm43xx_private *bcm,
+ u16 offset,
+ const u8 *mac)
+{
+ u16 data;
+
+ offset |= 0x0020;
+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_CONTROL, offset);
+
+ data = mac[0];
+ data |= mac[1] << 8;
+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data);
+ data = mac[2];
+ data |= mac[3] << 8;
+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data);
+ data = mac[4];
+ data |= mac[5] << 8;
+ bcm43xx_write16(bcm, BCM43xx_MMIO_MACFILTER_DATA, data);
+}
+
+static void bcm43xx_macfilter_clear(struct bcm43xx_private *bcm,
+ u16 offset)
+{
+ const u8 zero_addr[ETH_ALEN] = { 0 };
+
+ bcm43xx_macfilter_set(bcm, offset, zero_addr);
+}
+
+static void bcm43xx_write_mac_bssid_templates(struct bcm43xx_private *bcm)
+{
+ const u8 *mac = (const u8 *)(bcm->net_dev->dev_addr);
+ const u8 *bssid = (const u8 *)(bcm->ieee->bssid);
+ u8 mac_bssid[ETH_ALEN * 2];
+ int i;
+
+ memcpy(mac_bssid, mac, ETH_ALEN);
+ memcpy(mac_bssid + ETH_ALEN, bssid, ETH_ALEN);
+
+ /* Write our MAC address and BSSID to template ram */
+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))
+ bcm43xx_ram_write(bcm, 0x20 + i, *((u32 *)(mac_bssid + i)));
+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))
+ bcm43xx_ram_write(bcm, 0x78 + i, *((u32 *)(mac_bssid + i)));
+ for (i = 0; i < ARRAY_SIZE(mac_bssid); i += sizeof(u32))
+ bcm43xx_ram_write(bcm, 0x478 + i, *((u32 *)(mac_bssid + i)));
+}
+
+//FIXME: Well, we should probably call them from somewhere.
+#if 0
+static void bcm43xx_set_slot_time(struct bcm43xx_private *bcm, u16 slot_time)
+{
+ /* slot_time is in usec. */
+ if (bcm43xx_current_phy(bcm)->type != BCM43xx_PHYTYPE_G)
+ return;
+ bcm43xx_write16(bcm, 0x684, 510 + slot_time);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0010, slot_time);
+}
+
+static void bcm43xx_short_slot_timing_enable(struct bcm43xx_private *bcm)
+{
+ bcm43xx_set_slot_time(bcm, 9);
+}
+
+static void bcm43xx_short_slot_timing_disable(struct bcm43xx_private *bcm)
+{
+ bcm43xx_set_slot_time(bcm, 20);
+}
+#endif
+
+/* FIXME: To get the MAC-filter working, we need to implement the
+ * following functions (and rename them :)
+ */
+#if 0
+static void bcm43xx_disassociate(struct bcm43xx_private *bcm)
+{
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
+
+ bcm43xx_ram_write(bcm, 0x0026, 0x0000);
+ bcm43xx_ram_write(bcm, 0x0028, 0x0000);
+ bcm43xx_ram_write(bcm, 0x007E, 0x0000);
+ bcm43xx_ram_write(bcm, 0x0080, 0x0000);
+ bcm43xx_ram_write(bcm, 0x047E, 0x0000);
+ bcm43xx_ram_write(bcm, 0x0480, 0x0000);
+
+ if (bcm->current_core->rev < 3) {
+ bcm43xx_write16(bcm, 0x0610, 0x8000);
+ bcm43xx_write16(bcm, 0x060E, 0x0000);
+ } else
+ bcm43xx_write32(bcm, 0x0188, 0x80000000);
+
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
+
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G &&
+ ieee80211_is_ofdm_rate(bcm->softmac->txrates.default_rate))
+ bcm43xx_short_slot_timing_enable(bcm);
+
+ bcm43xx_mac_enable(bcm);
+}
+
+static void bcm43xx_associate(struct bcm43xx_private *bcm,
+ const u8 *mac)
+{
+ memcpy(bcm->ieee->bssid, mac, ETH_ALEN);
+
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_ASSOC, mac);
+ bcm43xx_write_mac_bssid_templates(bcm);
+ bcm43xx_mac_enable(bcm);
+}
+#endif
+
+/* Enable a Generic IRQ. "mask" is the mask of which IRQs to enable.
+ * Returns the _previously_ enabled IRQ mask.
+ */
+static inline u32 bcm43xx_interrupt_enable(struct bcm43xx_private *bcm, u32 mask)
+{
+ u32 old_mask;
+
+ old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask | mask);
+
+ return old_mask;
+}
+
+/* Disable a Generic IRQ. "mask" is the mask of which IRQs to disable.
+ * Returns the _previously_ enabled IRQ mask.
+ */
+static inline u32 bcm43xx_interrupt_disable(struct bcm43xx_private *bcm, u32 mask)
+{
+ u32 old_mask;
+
+ old_mask = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK, old_mask & ~mask);
+
+ return old_mask;
+}
+
+/* Make sure we don't receive more data from the device. */
+static int bcm43xx_disable_interrupts_sync(struct bcm43xx_private *bcm, u32 *oldstate)
+{
+ u32 old;
+ unsigned long flags;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (bcm43xx_is_initializing(bcm) || bcm->shutting_down) {
+ bcm43xx_unlock_mmio(bcm, flags);
+ return -EBUSY;
+ }
+ old = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ tasklet_disable(&bcm->isr_tasklet);
+ bcm43xx_unlock_mmio(bcm, flags);
+ if (oldstate)
+ *oldstate = old;
+
+ return 0;
+}
+
+static int bcm43xx_read_radioinfo(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u32 radio_id;
+ u16 manufact;
+ u16 version;
+ u8 revision;
+ s8 i;
+
+ if (bcm->chip_id == 0x4317) {
+ if (bcm->chip_rev == 0x00)
+ radio_id = 0x3205017F;
+ else if (bcm->chip_rev == 0x01)
+ radio_id = 0x4205017F;
+ else
+ radio_id = 0x5205017F;
+ } else {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID);
+ radio_id = bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_HIGH);
+ radio_id <<= 16;
+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, BCM43xx_RADIOCTL_ID);
+ radio_id |= bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW);
+ }
+
+ manufact = (radio_id & 0x00000FFF);
+ version = (radio_id & 0x0FFFF000) >> 12;
+ revision = (radio_id & 0xF0000000) >> 28;
+
+ dprintk(KERN_INFO PFX "Detected Radio: ID: %x (Manuf: %x Ver: %x Rev: %x)\n",
+ radio_id, manufact, version, revision);
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ if ((version != 0x2060) || (revision != 1) || (manufact != 0x17f))
+ goto err_unsupported_radio;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ if ((version & 0xFFF0) != 0x2050)
+ goto err_unsupported_radio;
+ break;
+ case BCM43xx_PHYTYPE_G:
+ if (version != 0x2050)
+ goto err_unsupported_radio;
+ break;
+ }
+
+ radio->manufact = manufact;
+ radio->version = version;
+ radio->revision = revision;
+
+ /* Set default attenuation values. */
+ radio->baseband_atten = bcm43xx_default_baseband_attenuation(bcm);
+ radio->radio_atten = bcm43xx_default_radio_attenuation(bcm);
+ radio->txctl1 = bcm43xx_default_txctl1(bcm);
+ radio->txctl2 = 0xFFFF;
+ if (phy->type == BCM43xx_PHYTYPE_A)
+ radio->txpower_desired = bcm->sprom.maxpower_aphy;
+ else
+ radio->txpower_desired = bcm->sprom.maxpower_bgphy;
+
+ /* Initialize the in-memory nrssi Lookup Table. */
+ for (i = 0; i < 64; i++)
+ radio->nrssi_lt[i] = i;
+
+ return 0;
+
+err_unsupported_radio:
+ printk(KERN_ERR PFX "Unsupported Radio connected to the PHY!\n");
+ return -ENODEV;
+}
+
+static const char * bcm43xx_locale_iso(u8 locale)
+{
+ /* ISO 3166-1 country codes.
+ * Note that there aren't ISO 3166-1 codes for
+ * all or locales. (Not all locales are countries)
+ */
+ switch (locale) {
+ case BCM43xx_LOCALE_WORLD:
+ case BCM43xx_LOCALE_ALL:
+ return "XX";
+ case BCM43xx_LOCALE_THAILAND:
+ return "TH";
+ case BCM43xx_LOCALE_ISRAEL:
+ return "IL";
+ case BCM43xx_LOCALE_JORDAN:
+ return "JO";
+ case BCM43xx_LOCALE_CHINA:
+ return "CN";
+ case BCM43xx_LOCALE_JAPAN:
+ case BCM43xx_LOCALE_JAPAN_HIGH:
+ return "JP";
+ case BCM43xx_LOCALE_USA_CANADA_ANZ:
+ case BCM43xx_LOCALE_USA_LOW:
+ return "US";
+ case BCM43xx_LOCALE_EUROPE:
+ return "EU";
+ case BCM43xx_LOCALE_NONE:
+ return " ";
+ }
+ assert(0);
+ return " ";
+}
+
+static const char * bcm43xx_locale_string(u8 locale)
+{
+ switch (locale) {
+ case BCM43xx_LOCALE_WORLD:
+ return "World";
+ case BCM43xx_LOCALE_THAILAND:
+ return "Thailand";
+ case BCM43xx_LOCALE_ISRAEL:
+ return "Israel";
+ case BCM43xx_LOCALE_JORDAN:
+ return "Jordan";
+ case BCM43xx_LOCALE_CHINA:
+ return "China";
+ case BCM43xx_LOCALE_JAPAN:
+ return "Japan";
+ case BCM43xx_LOCALE_USA_CANADA_ANZ:
+ return "USA/Canada/ANZ";
+ case BCM43xx_LOCALE_EUROPE:
+ return "Europe";
+ case BCM43xx_LOCALE_USA_LOW:
+ return "USAlow";
+ case BCM43xx_LOCALE_JAPAN_HIGH:
+ return "JapanHigh";
+ case BCM43xx_LOCALE_ALL:
+ return "All";
+ case BCM43xx_LOCALE_NONE:
+ return "None";
+ }
+ assert(0);
+ return "";
+}
+
+static inline u8 bcm43xx_crc8(u8 crc, u8 data)
+{
+ 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 bcm43xx_sprom_crc(const u16 *sprom)
+{
+ int word;
+ u8 crc = 0xFF;
+
+ for (word = 0; word < BCM43xx_SPROM_SIZE - 1; word++) {
+ crc = bcm43xx_crc8(crc, sprom[word] & 0x00FF);
+ crc = bcm43xx_crc8(crc, (sprom[word] & 0xFF00) >> 8);
+ }
+ crc = bcm43xx_crc8(crc, sprom[BCM43xx_SPROM_VERSION] & 0x00FF);
+ crc ^= 0xFF;
+
+ return crc;
+}
+
+int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom)
+{
+ int i;
+ u8 crc, expected_crc;
+
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++)
+ sprom[i] = bcm43xx_read16(bcm, BCM43xx_SPROM_BASE + (i * 2));
+ /* CRC-8 check. */
+ crc = bcm43xx_sprom_crc(sprom);
+ expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8;
+ if (crc != expected_crc) {
+ printk(KERN_WARNING PFX "WARNING: Invalid SPROM checksum "
+ "(0x%02X, expected: 0x%02X)\n",
+ crc, expected_crc);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom)
+{
+ int i, err;
+ u8 crc, expected_crc;
+ u32 spromctl;
+
+ /* CRC-8 validation of the input data. */
+ crc = bcm43xx_sprom_crc(sprom);
+ expected_crc = (sprom[BCM43xx_SPROM_VERSION] & 0xFF00) >> 8;
+ if (crc != expected_crc) {
+ printk(KERN_ERR PFX "SPROM input data: Invalid CRC\n");
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO PFX "Writing SPROM. Do NOT turn off the power! Please stand by...\n");
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_SPROMCTL, &spromctl);
+ if (err)
+ goto err_ctlreg;
+ spromctl |= 0x10; /* SPROM WRITE enable. */
+ bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);
+ if (err)
+ goto err_ctlreg;
+ /* We must burn lots of CPU cycles here, but that does not
+ * really matter as one does not write the SPROM every other minute...
+ */
+ printk(KERN_INFO PFX "[ 0%%");
+ mdelay(500);
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
+ if (i == 16)
+ printk("25%%");
+ else if (i == 32)
+ printk("50%%");
+ else if (i == 48)
+ printk("75%%");
+ else if (i % 2)
+ printk(".");
+ bcm43xx_write16(bcm, BCM43xx_SPROM_BASE + (i * 2), sprom[i]);
+ mmiowb();
+ mdelay(20);
+ }
+ spromctl &= ~0x10; /* SPROM WRITE enable. */
+ bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_SPROMCTL, spromctl);
+ if (err)
+ goto err_ctlreg;
+ mdelay(500);
+ printk("100%% ]\n");
+ printk(KERN_INFO PFX "SPROM written.\n");
+ bcm43xx_controller_restart(bcm, "SPROM update");
+
+ return 0;
+err_ctlreg:
+ printk(KERN_ERR PFX "Could not access SPROM control register.\n");
+ return -ENODEV;
+}
+
+static int bcm43xx_sprom_extract(struct bcm43xx_private *bcm)
+{
+ u16 value;
+ u16 *sprom;
+#ifdef CONFIG_BCM947XX
+ char *c;
+#endif
+
+ sprom = kzalloc(BCM43xx_SPROM_SIZE * sizeof(u16),
+ GFP_KERNEL);
+ if (!sprom) {
+ printk(KERN_ERR PFX "sprom_extract OOM\n");
+ return -ENOMEM;
+ }
+#ifdef CONFIG_BCM947XX
+ sprom[BCM43xx_SPROM_BOARDFLAGS2] = atoi(nvram_get("boardflags2"));
+ sprom[BCM43xx_SPROM_BOARDFLAGS] = atoi(nvram_get("boardflags"));
+
+ if ((c = nvram_get("il0macaddr")) != NULL)
+ e_aton(c, (char *) &(sprom[BCM43xx_SPROM_IL0MACADDR]));
+
+ if ((c = nvram_get("et1macaddr")) != NULL)
+ e_aton(c, (char *) &(sprom[BCM43xx_SPROM_ET1MACADDR]));
+
+ sprom[BCM43xx_SPROM_PA0B0] = atoi(nvram_get("pa0b0"));
+ sprom[BCM43xx_SPROM_PA0B1] = atoi(nvram_get("pa0b1"));
+ sprom[BCM43xx_SPROM_PA0B2] = atoi(nvram_get("pa0b2"));
+
+ sprom[BCM43xx_SPROM_PA1B0] = atoi(nvram_get("pa1b0"));
+ sprom[BCM43xx_SPROM_PA1B1] = atoi(nvram_get("pa1b1"));
+ sprom[BCM43xx_SPROM_PA1B2] = atoi(nvram_get("pa1b2"));
+
+ sprom[BCM43xx_SPROM_BOARDREV] = atoi(nvram_get("boardrev"));
+#else
+ bcm43xx_sprom_read(bcm, sprom);
+#endif
+
+ /* boardflags2 */
+ value = sprom[BCM43xx_SPROM_BOARDFLAGS2];
+ bcm->sprom.boardflags2 = value;
+
+ /* il0macaddr */
+ value = sprom[BCM43xx_SPROM_IL0MACADDR + 0];
+ *(((u16 *)bcm->sprom.il0macaddr) + 0) = cpu_to_be16(value);
+ value = sprom[BCM43xx_SPROM_IL0MACADDR + 1];
+ *(((u16 *)bcm->sprom.il0macaddr) + 1) = cpu_to_be16(value);
+ value = sprom[BCM43xx_SPROM_IL0MACADDR + 2];
+ *(((u16 *)bcm->sprom.il0macaddr) + 2) = cpu_to_be16(value);
+
+ /* et0macaddr */
+ value = sprom[BCM43xx_SPROM_ET0MACADDR + 0];
+ *(((u16 *)bcm->sprom.et0macaddr) + 0) = cpu_to_be16(value);
+ value = sprom[BCM43xx_SPROM_ET0MACADDR + 1];
+ *(((u16 *)bcm->sprom.et0macaddr) + 1) = cpu_to_be16(value);
+ value = sprom[BCM43xx_SPROM_ET0MACADDR + 2];
+ *(((u16 *)bcm->sprom.et0macaddr) + 2) = cpu_to_be16(value);
+
+ /* et1macaddr */
+ value = sprom[BCM43xx_SPROM_ET1MACADDR + 0];
+ *(((u16 *)bcm->sprom.et1macaddr) + 0) = cpu_to_be16(value);
+ value = sprom[BCM43xx_SPROM_ET1MACADDR + 1];
+ *(((u16 *)bcm->sprom.et1macaddr) + 1) = cpu_to_be16(value);
+ value = sprom[BCM43xx_SPROM_ET1MACADDR + 2];
+ *(((u16 *)bcm->sprom.et1macaddr) + 2) = cpu_to_be16(value);
+
+ /* ethernet phy settings */
+ value = sprom[BCM43xx_SPROM_ETHPHY];
+ bcm->sprom.et0phyaddr = (value & 0x001F);
+ bcm->sprom.et1phyaddr = (value & 0x03E0) >> 5;
+ bcm->sprom.et0mdcport = (value & (1 << 14)) >> 14;
+ bcm->sprom.et1mdcport = (value & (1 << 15)) >> 15;
+
+ /* boardrev, antennas, locale */
+ value = sprom[BCM43xx_SPROM_BOARDREV];
+ bcm->sprom.boardrev = (value & 0x00FF);
+ bcm->sprom.locale = (value & 0x0F00) >> 8;
+ bcm->sprom.antennas_aphy = (value & 0x3000) >> 12;
+ bcm->sprom.antennas_bgphy = (value & 0xC000) >> 14;
+ if (modparam_locale != -1) {
+ if (modparam_locale >= 0 && modparam_locale <= 11) {
+ bcm->sprom.locale = modparam_locale;
+ printk(KERN_WARNING PFX "Operating with modified "
+ "LocaleCode %u (%s)\n",
+ bcm->sprom.locale,
+ bcm43xx_locale_string(bcm->sprom.locale));
+ } else {
+ printk(KERN_WARNING PFX "Module parameter \"locale\" "
+ "invalid value. (0 - 11)\n");
+ }
+ }
+
+ /* pa0b* */
+ value = sprom[BCM43xx_SPROM_PA0B0];
+ bcm->sprom.pa0b0 = value;
+ value = sprom[BCM43xx_SPROM_PA0B1];
+ bcm->sprom.pa0b1 = value;
+ value = sprom[BCM43xx_SPROM_PA0B2];
+ bcm->sprom.pa0b2 = value;
+
+ /* wl0gpio* */
+ value = sprom[BCM43xx_SPROM_WL0GPIO0];
+ if (value == 0x0000)
+ value = 0xFFFF;
+ bcm->sprom.wl0gpio0 = value & 0x00FF;
+ bcm->sprom.wl0gpio1 = (value & 0xFF00) >> 8;
+ value = sprom[BCM43xx_SPROM_WL0GPIO2];
+ if (value == 0x0000)
+ value = 0xFFFF;
+ bcm->sprom.wl0gpio2 = value & 0x00FF;
+ bcm->sprom.wl0gpio3 = (value & 0xFF00) >> 8;
+
+ /* maxpower */
+ value = sprom[BCM43xx_SPROM_MAXPWR];
+ bcm->sprom.maxpower_aphy = (value & 0xFF00) >> 8;
+ bcm->sprom.maxpower_bgphy = value & 0x00FF;
+
+ /* pa1b* */
+ value = sprom[BCM43xx_SPROM_PA1B0];
+ bcm->sprom.pa1b0 = value;
+ value = sprom[BCM43xx_SPROM_PA1B1];
+ bcm->sprom.pa1b1 = value;
+ value = sprom[BCM43xx_SPROM_PA1B2];
+ bcm->sprom.pa1b2 = value;
+
+ /* idle tssi target */
+ value = sprom[BCM43xx_SPROM_IDL_TSSI_TGT];
+ bcm->sprom.idle_tssi_tgt_aphy = value & 0x00FF;
+ bcm->sprom.idle_tssi_tgt_bgphy = (value & 0xFF00) >> 8;
+
+ /* boardflags */
+ value = sprom[BCM43xx_SPROM_BOARDFLAGS];
+ if (value == 0xFFFF)
+ value = 0x0000;
+ bcm->sprom.boardflags = value;
+ /* boardflags workarounds */
+ if (bcm->board_vendor == PCI_VENDOR_ID_DELL &&
+ bcm->chip_id == 0x4301 &&
+ bcm->board_revision == 0x74)
+ bcm->sprom.boardflags |= BCM43xx_BFL_BTCOEXIST;
+ if (bcm->board_vendor == PCI_VENDOR_ID_APPLE &&
+ bcm->board_type == 0x4E &&
+ bcm->board_revision > 0x40)
+ bcm->sprom.boardflags |= BCM43xx_BFL_PACTRL;
+
+ /* antenna gain */
+ value = sprom[BCM43xx_SPROM_ANTENNA_GAIN];
+ if (value == 0x0000 || value == 0xFFFF)
+ value = 0x0202;
+ /* convert values to Q5.2 */
+ bcm->sprom.antennagain_aphy = ((value & 0xFF00) >> 8) * 4;
+ bcm->sprom.antennagain_bgphy = (value & 0x00FF) * 4;
+
+ kfree(sprom);
+
+ return 0;
+}
+
+static void bcm43xx_geo_init(struct bcm43xx_private *bcm)
+{
+ struct ieee80211_geo geo;
+ struct ieee80211_channel *chan;
+ int have_a = 0, have_bg = 0;
+ int i;
+ u8 channel;
+ struct bcm43xx_phyinfo *phy;
+ const char *iso_country;
+
+ memset(&geo, 0, sizeof(geo));
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ phy = &(bcm->core_80211_ext[i].phy);
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ have_bg = 1;
+ break;
+ case BCM43xx_PHYTYPE_A:
+ have_a = 1;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ iso_country = bcm43xx_locale_iso(bcm->sprom.locale);
+
+ if (have_a) {
+ for (i = 0, channel = 0; channel < 201; channel++) {
+ chan = &geo.a[i++];
+ chan->freq = bcm43xx_channel_to_freq_a(channel);
+ chan->channel = channel;
+ }
+ geo.a_channels = i;
+ }
+ if (have_bg) {
+ for (i = 0, channel = 1; channel < 15; channel++) {
+ chan = &geo.bg[i++];
+ chan->freq = bcm43xx_channel_to_freq_bg(channel);
+ chan->channel = channel;
+ }
+ geo.bg_channels = i;
+ }
+ memcpy(geo.name, iso_country, 2);
+ if (0 /*TODO: Outdoor use only */)
+ geo.name[2] = 'O';
+ else if (0 /*TODO: Indoor use only */)
+ geo.name[2] = 'I';
+ else
+ geo.name[2] = ' ';
+ geo.name[3] = '\0';
+
+ ieee80211_set_geo(bcm->ieee, &geo);
+}
+
+/* DummyTransmission function, as documented on
+ * http://bcm-specs.sipsolutions.net/DummyTransmission
+ */
+void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ unsigned int i, max_loop;
+ u16 value = 0;
+ u32 buffer[5] = {
+ 0x00000000,
+ 0x0000D400,
+ 0x00000000,
+ 0x00000001,
+ 0x00000000,
+ };
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ max_loop = 0x1E;
+ buffer[0] = 0xCC010200;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ max_loop = 0xFA;
+ buffer[0] = 0x6E840B00;
+ break;
+ default:
+ assert(0);
+ return;
+ }
+
+ for (i = 0; i < 5; i++)
+ bcm43xx_ram_write(bcm, i * 4, buffer[i]);
+
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */
+
+ bcm43xx_write16(bcm, 0x0568, 0x0000);
+ bcm43xx_write16(bcm, 0x07C0, 0x0000);
+ bcm43xx_write16(bcm, 0x050C, ((phy->type == BCM43xx_PHYTYPE_A) ? 1 : 0));
+ bcm43xx_write16(bcm, 0x0508, 0x0000);
+ bcm43xx_write16(bcm, 0x050A, 0x0000);
+ bcm43xx_write16(bcm, 0x054C, 0x0000);
+ bcm43xx_write16(bcm, 0x056A, 0x0014);
+ bcm43xx_write16(bcm, 0x0568, 0x0826);
+ bcm43xx_write16(bcm, 0x0500, 0x0000);
+ bcm43xx_write16(bcm, 0x0502, 0x0030);
+
+ if (radio->version == 0x2050 && radio->revision <= 0x5)
+ bcm43xx_radio_write16(bcm, 0x0051, 0x0017);
+ for (i = 0x00; i < max_loop; i++) {
+ value = bcm43xx_read16(bcm, 0x050E);
+ if (value & 0x0080)
+ break;
+ udelay(10);
+ }
+ for (i = 0x00; i < 0x0A; i++) {
+ value = bcm43xx_read16(bcm, 0x050E);
+ if (value & 0x0400)
+ break;
+ udelay(10);
+ }
+ for (i = 0x00; i < 0x0A; i++) {
+ value = bcm43xx_read16(bcm, 0x0690);
+ if (!(value & 0x0100))
+ break;
+ udelay(10);
+ }
+ if (radio->version == 0x2050 && radio->revision <= 0x5)
+ bcm43xx_radio_write16(bcm, 0x0051, 0x0037);
+}
+
+static void key_write(struct bcm43xx_private *bcm,
+ u8 index, u8 algorithm, const u16 *key)
+{
+ unsigned int i, basic_wep = 0;
+ u32 offset;
+ u16 value;
+
+ /* Write associated key information */
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x100 + (index * 2),
+ ((index << 4) | (algorithm & 0x0F)));
+
+ /* The first 4 WEP keys need extra love */
+ if (((algorithm == BCM43xx_SEC_ALGO_WEP) ||
+ (algorithm == BCM43xx_SEC_ALGO_WEP104)) && (index < 4))
+ basic_wep = 1;
+
+ /* Write key payload, 8 little endian words */
+ offset = bcm->security_offset + (index * BCM43xx_SEC_KEYSIZE);
+ for (i = 0; i < (BCM43xx_SEC_KEYSIZE / sizeof(u16)); i++) {
+ value = cpu_to_le16(key[i]);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,
+ offset + (i * 2), value);
+
+ if (!basic_wep)
+ continue;
+
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,
+ offset + (i * 2) + 4 * BCM43xx_SEC_KEYSIZE,
+ value);
+ }
+}
+
+static void keymac_write(struct bcm43xx_private *bcm,
+ u8 index, const u32 *addr)
+{
+ /* for keys 0-3 there is no associated mac address */
+ if (index < 4)
+ return;
+
+ index -= 4;
+ if (bcm->current_core->rev >= 5) {
+ bcm43xx_shm_write32(bcm,
+ BCM43xx_SHM_HWMAC,
+ index * 2,
+ cpu_to_be32(*addr));
+ bcm43xx_shm_write16(bcm,
+ BCM43xx_SHM_HWMAC,
+ (index * 2) + 1,
+ cpu_to_be16(*((u16 *)(addr + 1))));
+ } else {
+ if (index < 8) {
+ TODO(); /* Put them in the macaddress filter */
+ } else {
+ TODO();
+ /* Put them BCM43xx_SHM_SHARED, stating index 0x0120.
+ Keep in mind to update the count of keymacs in 0x003E as well! */
+ }
+ }
+}
+
+static int bcm43xx_key_write(struct bcm43xx_private *bcm,
+ u8 index, u8 algorithm,
+ const u8 *_key, int key_len,
+ const u8 *mac_addr)
+{
+ u8 key[BCM43xx_SEC_KEYSIZE] = { 0 };
+
+ if (index >= ARRAY_SIZE(bcm->key))
+ return -EINVAL;
+ if (key_len > ARRAY_SIZE(key))
+ return -EINVAL;
+ if (algorithm < 1 || algorithm > 5)
+ return -EINVAL;
+
+ memcpy(key, _key, key_len);
+ key_write(bcm, index, algorithm, (const u16 *)key);
+ keymac_write(bcm, index, (const u32 *)mac_addr);
+
+ bcm->key[index].algorithm = algorithm;
+
+ return 0;
+}
+
+static void bcm43xx_clear_keys(struct bcm43xx_private *bcm)
+{
+ static const u32 zero_mac[2] = { 0 };
+ unsigned int i,j, nr_keys = 54;
+ u16 offset;
+
+ if (bcm->current_core->rev < 5)
+ nr_keys = 16;
+ assert(nr_keys <= ARRAY_SIZE(bcm->key));
+
+ for (i = 0; i < nr_keys; i++) {
+ bcm->key[i].enabled = 0;
+ /* returns for i < 4 immediately */
+ keymac_write(bcm, i, zero_mac);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,
+ 0x100 + (i * 2), 0x0000);
+ for (j = 0; j < 8; j++) {
+ offset = bcm->security_offset + (j * 4) + (i * BCM43xx_SEC_KEYSIZE);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED,
+ offset, 0x0000);
+ }
+ }
+ dprintk(KERN_INFO PFX "Keys cleared\n");
+}
+
+/* Lowlevel core-switch function. This is only to be used in
+ * bcm43xx_switch_core() and bcm43xx_probe_cores()
+ */
+static int _switch_core(struct bcm43xx_private *bcm, int core)
+{
+ int err;
+ int attempts = 0;
+ u32 current_core;
+
+ assert(core >= 0);
+ while (1) {
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE,
+ (core * 0x1000) + 0x18000000);
+ if (unlikely(err))
+ goto error;
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ACTIVE_CORE,
+ &current_core);
+ if (unlikely(err))
+ goto error;
+ current_core = (current_core - 0x18000000) / 0x1000;
+ if (current_core == core)
+ break;
+
+ if (unlikely(attempts++ > BCM43xx_SWITCH_CORE_MAX_RETRIES))
+ goto error;
+ udelay(10);
+ }
+#ifdef CONFIG_BCM947XX
+ if (bcm->pci_dev->bus->number == 0)
+ bcm->current_core_offset = 0x1000 * core;
+ else
+ bcm->current_core_offset = 0;
+#endif
+
+ return 0;
+error:
+ printk(KERN_ERR PFX "Failed to switch to core %d\n", core);
+ return -ENODEV;
+}
+
+int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core)
+{
+ int err;
+
+ if (unlikely(!new_core))
+ return 0;
+ if (!new_core->available)
+ return -ENODEV;
+ if (bcm->current_core == new_core)
+ return 0;
+ err = _switch_core(bcm, new_core->index);
+ if (unlikely(err))
+ goto out;
+
+ bcm->current_core = new_core;
+ bcm->current_80211_core_idx = -1;
+ if (new_core->id == BCM43xx_COREID_80211)
+ bcm->current_80211_core_idx = (int)(new_core - &(bcm->core_80211[0]));
+
+out:
+ return err;
+}
+
+static int bcm43xx_core_enabled(struct bcm43xx_private *bcm)
+{
+ u32 value;
+
+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ value &= BCM43xx_SBTMSTATELOW_CLOCK | BCM43xx_SBTMSTATELOW_RESET
+ | BCM43xx_SBTMSTATELOW_REJECT;
+
+ return (value == BCM43xx_SBTMSTATELOW_CLOCK);
+}
+
+/* disable current core */
+static int bcm43xx_core_disable(struct bcm43xx_private *bcm, u32 core_flags)
+{
+ u32 sbtmstatelow;
+ u32 sbtmstatehigh;
+ int i;
+
+ /* fetch sbtmstatelow from core information registers */
+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+
+ /* core is already in reset */
+ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_RESET)
+ goto out;
+
+ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_CLOCK) {
+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK |
+ BCM43xx_SBTMSTATELOW_REJECT;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+
+ for (i = 0; i < 1000; i++) {
+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ if (sbtmstatelow & BCM43xx_SBTMSTATELOW_REJECT) {
+ i = -1;
+ break;
+ }
+ udelay(10);
+ }
+ if (i != -1) {
+ printk(KERN_ERR PFX "Error: core_disable() REJECT timeout!\n");
+ return -EBUSY;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
+ if (!(sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_BUSY)) {
+ i = -1;
+ break;
+ }
+ udelay(10);
+ }
+ if (i != -1) {
+ printk(KERN_ERR PFX "Error: core_disable() BUSY timeout!\n");
+ return -EBUSY;
+ }
+
+ sbtmstatelow = BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK |
+ BCM43xx_SBTMSTATELOW_REJECT |
+ BCM43xx_SBTMSTATELOW_RESET |
+ BCM43xx_SBTMSTATELOW_CLOCK |
+ core_flags;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ udelay(10);
+ }
+
+ sbtmstatelow = BCM43xx_SBTMSTATELOW_RESET |
+ BCM43xx_SBTMSTATELOW_REJECT |
+ core_flags;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+
+out:
+ bcm->current_core->enabled = 0;
+
+ return 0;
+}
+
+/* enable (reset) current core */
+static int bcm43xx_core_enable(struct bcm43xx_private *bcm, u32 core_flags)
+{
+ u32 sbtmstatelow;
+ u32 sbtmstatehigh;
+ u32 sbimstate;
+ int err;
+
+ err = bcm43xx_core_disable(bcm, core_flags);
+ if (err)
+ goto out;
+
+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK |
+ BCM43xx_SBTMSTATELOW_RESET |
+ BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK |
+ core_flags;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ udelay(1);
+
+ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
+ if (sbtmstatehigh & BCM43xx_SBTMSTATEHIGH_SERROR) {
+ sbtmstatehigh = 0x00000000;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATEHIGH, sbtmstatehigh);
+ }
+
+ sbimstate = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMSTATE);
+ if (sbimstate & (BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT)) {
+ sbimstate &= ~(BCM43xx_SBIMSTATE_IB_ERROR | BCM43xx_SBIMSTATE_TIMEOUT);
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMSTATE, sbimstate);
+ }
+
+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK |
+ BCM43xx_SBTMSTATELOW_FORCE_GATE_CLOCK |
+ core_flags;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ udelay(1);
+
+ sbtmstatelow = BCM43xx_SBTMSTATELOW_CLOCK | core_flags;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ udelay(1);
+
+ bcm->current_core->enabled = 1;
+ assert(err == 0);
+out:
+ return err;
+}
+
+/* http://bcm-specs.sipsolutions.net/80211CoreReset */
+void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy)
+{
+ u32 flags = 0x00040000;
+
+ if ((bcm43xx_core_enabled(bcm)) &&
+ !bcm43xx_using_pio(bcm)) {
+//FIXME: Do we _really_ want #ifndef CONFIG_BCM947XX here?
+#ifndef CONFIG_BCM947XX
+ /* reset all used DMA controllers. */
+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA1_BASE);
+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA2_BASE);
+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA3_BASE);
+ bcm43xx_dmacontroller_tx_reset(bcm, BCM43xx_MMIO_DMA4_BASE);
+ bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA1_BASE);
+ if (bcm->current_core->rev < 5)
+ bcm43xx_dmacontroller_rx_reset(bcm, BCM43xx_MMIO_DMA4_BASE);
+#endif
+ }
+ if (bcm->shutting_down) {
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ & ~(BCM43xx_SBF_MAC_ENABLED | 0x00000002));
+ } else {
+ if (connect_phy)
+ flags |= 0x20000000;
+ bcm43xx_phy_connect(bcm, connect_phy);
+ bcm43xx_core_enable(bcm, flags);
+ bcm43xx_write16(bcm, 0x03E6, 0x0000);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ | BCM43xx_SBF_400);
+ }
+}
+
+static void bcm43xx_wireless_core_disable(struct bcm43xx_private *bcm)
+{
+ bcm43xx_radio_turn_off(bcm);
+ bcm43xx_write16(bcm, 0x03E6, 0x00F4);
+ bcm43xx_core_disable(bcm, 0);
+}
+
+/* Mark the current 80211 core inactive.
+ * "active_80211_core" is the other 80211 core, which is used.
+ */
+static int bcm43xx_wireless_core_mark_inactive(struct bcm43xx_private *bcm,
+ struct bcm43xx_coreinfo *active_80211_core)
+{
+ u32 sbtmstatelow;
+ struct bcm43xx_coreinfo *old_core;
+ int err = 0;
+
+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ bcm43xx_radio_turn_off(bcm);
+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ sbtmstatelow &= ~0x200a0000;
+ sbtmstatelow |= 0xa0000;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ udelay(1);
+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ sbtmstatelow &= ~0xa0000;
+ sbtmstatelow |= 0x80000;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ udelay(1);
+
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_G) {
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, active_80211_core);
+ if (err)
+ goto out;
+ sbtmstatelow = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ sbtmstatelow &= ~0x20000000;
+ sbtmstatelow |= 0x20000000;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, sbtmstatelow);
+ err = bcm43xx_switch_core(bcm, old_core);
+ }
+
+out:
+ return err;
+}
+
+static void handle_irq_transmit_status(struct bcm43xx_private *bcm)
+{
+ u32 v0, v1;
+ u16 tmp;
+ struct bcm43xx_xmitstatus stat;
+
+ while (1) {
+ v0 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_0);
+ if (!v0)
+ break;
+ v1 = bcm43xx_read32(bcm, BCM43xx_MMIO_XMITSTAT_1);
+
+ stat.cookie = (v0 >> 16) & 0x0000FFFF;
+ tmp = (u16)((v0 & 0xFFF0) | ((v0 & 0xF) >> 1));
+ stat.flags = tmp & 0xFF;
+ stat.cnt1 = (tmp & 0x0F00) >> 8;
+ stat.cnt2 = (tmp & 0xF000) >> 12;
+ stat.seq = (u16)(v1 & 0xFFFF);
+ stat.unknown = (u16)((v1 >> 16) & 0xFF);
+
+ bcm43xx_debugfs_log_txstat(bcm, &stat);
+
+ if (stat.flags & BCM43xx_TXSTAT_FLAG_IGNORE)
+ continue;
+ if (!(stat.flags & BCM43xx_TXSTAT_FLAG_ACK)) {
+ //TODO: packet was not acked (was lost)
+ }
+ //TODO: There are more (unknown) flags to test. see bcm43xx_main.h
+
+ if (bcm43xx_using_pio(bcm))
+ bcm43xx_pio_handle_xmitstatus(bcm, &stat);
+ else
+ bcm43xx_dma_handle_xmitstatus(bcm, &stat);
+ }
+}
+
+static void bcm43xx_generate_noise_sample(struct bcm43xx_private *bcm)
+{
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x408, 0x7F7F);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x40A, 0x7F7F);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD) | (1 << 4));
+ assert(bcm->noisecalc.core_at_start == bcm->current_core);
+ assert(bcm->noisecalc.channel_at_start == bcm43xx_current_radio(bcm)->channel);
+}
+
+static void bcm43xx_calculate_link_quality(struct bcm43xx_private *bcm)
+{
+ /* Top half of Link Quality calculation. */
+
+ if (bcm->noisecalc.calculation_running)
+ return;
+ bcm->noisecalc.core_at_start = bcm->current_core;
+ bcm->noisecalc.channel_at_start = bcm43xx_current_radio(bcm)->channel;
+ bcm->noisecalc.calculation_running = 1;
+ bcm->noisecalc.nr_samples = 0;
+
+ bcm43xx_generate_noise_sample(bcm);
+}
+
+static void handle_irq_noise(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 tmp;
+ u8 noise[4];
+ u8 i, j;
+ s32 average;
+
+ /* Bottom half of Link Quality calculation. */
+
+ assert(bcm->noisecalc.calculation_running);
+ if (bcm->noisecalc.core_at_start != bcm->current_core ||
+ bcm->noisecalc.channel_at_start != radio->channel)
+ goto drop_calculation;
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x408);
+ noise[0] = (tmp & 0x00FF);
+ noise[1] = (tmp & 0xFF00) >> 8;
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40A);
+ noise[2] = (tmp & 0x00FF);
+ noise[3] = (tmp & 0xFF00) >> 8;
+ if (noise[0] == 0x7F || noise[1] == 0x7F ||
+ noise[2] == 0x7F || noise[3] == 0x7F)
+ goto generate_new;
+
+ /* Get the noise samples. */
+ assert(bcm->noisecalc.nr_samples <= 8);
+ i = bcm->noisecalc.nr_samples;
+ noise[0] = limit_value(noise[0], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);
+ noise[1] = limit_value(noise[1], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);
+ noise[2] = limit_value(noise[2], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);
+ noise[3] = limit_value(noise[3], 0, ARRAY_SIZE(radio->nrssi_lt) - 1);
+ bcm->noisecalc.samples[i][0] = radio->nrssi_lt[noise[0]];
+ bcm->noisecalc.samples[i][1] = radio->nrssi_lt[noise[1]];
+ bcm->noisecalc.samples[i][2] = radio->nrssi_lt[noise[2]];
+ bcm->noisecalc.samples[i][3] = radio->nrssi_lt[noise[3]];
+ bcm->noisecalc.nr_samples++;
+ if (bcm->noisecalc.nr_samples == 8) {
+ /* Calculate the Link Quality by the noise samples. */
+ average = 0;
+ for (i = 0; i < 8; i++) {
+ for (j = 0; j < 4; j++)
+ average += bcm->noisecalc.samples[i][j];
+ }
+ average /= (8 * 4);
+ average *= 125;
+ average += 64;
+ average /= 128;
+
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x40C);
+ tmp = (tmp / 128) & 0x1F;
+ if (tmp >= 8)
+ average += 2;
+ else
+ average -= 25;
+ if (tmp == 8)
+ average -= 72;
+ else
+ average -= 48;
+
+/* FIXME: This is wrong, but people want fancy stats. well... */
+bcm->stats.noise = average;
+ if (average > -65)
+ bcm->stats.link_quality = 0;
+ else if (average > -75)
+ bcm->stats.link_quality = 1;
+ else if (average > -85)
+ bcm->stats.link_quality = 2;
+ else
+ bcm->stats.link_quality = 3;
+// dprintk(KERN_INFO PFX "Link Quality: %u (avg was %d)\n", bcm->stats.link_quality, average);
+drop_calculation:
+ bcm->noisecalc.calculation_running = 0;
+ return;
+ }
+generate_new:
+ bcm43xx_generate_noise_sample(bcm);
+}
+
+static void handle_irq_ps(struct bcm43xx_private *bcm)
+{
+ if (bcm->ieee->iw_mode == IW_MODE_MASTER) {
+ ///TODO: PS TBTT
+ } else {
+ if (1/*FIXME: the last PSpoll frame was sent successfully */)
+ bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
+ }
+ if (bcm->ieee->iw_mode == IW_MODE_ADHOC)
+ bcm->reg124_set_0x4 = 1;
+ //FIXME else set to false?
+}
+
+static void handle_irq_reg124(struct bcm43xx_private *bcm)
+{
+ if (!bcm->reg124_set_0x4)
+ return;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD)
+ | 0x4);
+ //FIXME: reset reg124_set_0x4 to false?
+}
+
+static void handle_irq_pmq(struct bcm43xx_private *bcm)
+{
+ u32 tmp;
+
+ //TODO: AP mode.
+
+ while (1) {
+ tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_PS_STATUS);
+ if (!(tmp & 0x00000008))
+ break;
+ }
+ /* 16bit write is odd, but correct. */
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PS_STATUS, 0x0002);
+}
+
+static void bcm43xx_generate_beacon_template(struct bcm43xx_private *bcm,
+ u16 ram_offset, u16 shm_size_offset)
+{
+ u32 value;
+ u16 size = 0;
+
+ /* Timestamp. */
+ //FIXME: assumption: The chip sets the timestamp
+ value = 0;
+ bcm43xx_ram_write(bcm, ram_offset++, value);
+ bcm43xx_ram_write(bcm, ram_offset++, value);
+ size += 8;
+
+ /* Beacon Interval / Capability Information */
+ value = 0x0000;//FIXME: Which interval?
+ value |= (1 << 0) << 16; /* ESS */
+ value |= (1 << 2) << 16; /* CF Pollable */ //FIXME?
+ value |= (1 << 3) << 16; /* CF Poll Request */ //FIXME?
+ if (!bcm->ieee->open_wep)
+ value |= (1 << 4) << 16; /* Privacy */
+ bcm43xx_ram_write(bcm, ram_offset++, value);
+ size += 4;
+
+ /* SSID */
+ //TODO
+
+ /* FH Parameter Set */
+ //TODO
+
+ /* DS Parameter Set */
+ //TODO
+
+ /* CF Parameter Set */
+ //TODO
+
+ /* TIM */
+ //TODO
+
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, shm_size_offset, size);
+}
+
+static void handle_irq_beacon(struct bcm43xx_private *bcm)
+{
+ u32 status;
+
+ bcm->irq_savedstate &= ~BCM43xx_IRQ_BEACON;
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD);
+
+ if ((status & 0x1) && (status & 0x2)) {
+ /* ACK beacon IRQ. */
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON,
+ BCM43xx_IRQ_BEACON);
+ bcm->irq_savedstate |= BCM43xx_IRQ_BEACON;
+ return;
+ }
+ if (!(status & 0x1)) {
+ bcm43xx_generate_beacon_template(bcm, 0x68, 0x18);
+ status |= 0x1;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);
+ }
+ if (!(status & 0x2)) {
+ bcm43xx_generate_beacon_template(bcm, 0x468, 0x1A);
+ status |= 0x2;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS2_BITFIELD, status);
+ }
+}
+
+/* Interrupt handler bottom-half */
+static void bcm43xx_interrupt_tasklet(struct bcm43xx_private *bcm)
+{
+ u32 reason;
+ u32 dma_reason[4];
+ int activity = 0;
+ unsigned long flags;
+
+#ifdef CONFIG_BCM43XX_DEBUG
+ u32 _handled = 0x00000000;
+# define bcmirq_handled(irq) do { _handled |= (irq); } while (0)
+#else
+# define bcmirq_handled(irq) do { /* nothing */ } while (0)
+#endif /* CONFIG_BCM43XX_DEBUG*/
+
+ bcm43xx_lock_mmio(bcm, flags);
+ reason = bcm->irq_reason;
+ dma_reason[0] = bcm->dma_reason[0];
+ dma_reason[1] = bcm->dma_reason[1];
+ dma_reason[2] = bcm->dma_reason[2];
+ dma_reason[3] = bcm->dma_reason[3];
+
+ if (unlikely(reason & BCM43xx_IRQ_XMIT_ERROR)) {
+ /* TX error. We get this when Template Ram is written in wrong endianess
+ * in dummy_tx(). We also get this if something is wrong with the TX header
+ * on DMA or PIO queues.
+ * Maybe we get this in other error conditions, too.
+ */
+ printkl(KERN_ERR PFX "FATAL ERROR: BCM43xx_IRQ_XMIT_ERROR\n");
+ bcmirq_handled(BCM43xx_IRQ_XMIT_ERROR);
+ }
+ if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_FATALMASK) |
+ (dma_reason[1] & BCM43xx_DMAIRQ_FATALMASK) |
+ (dma_reason[2] & BCM43xx_DMAIRQ_FATALMASK) |
+ (dma_reason[3] & BCM43xx_DMAIRQ_FATALMASK))) {
+ printkl(KERN_ERR PFX "FATAL ERROR: Fatal DMA error: "
+ "0x%08X, 0x%08X, 0x%08X, 0x%08X\n",
+ dma_reason[0], dma_reason[1],
+ dma_reason[2], dma_reason[3]);
+ bcm43xx_controller_restart(bcm, "DMA error");
+ bcm43xx_unlock_mmio(bcm, flags);
+ return;
+ }
+ if (unlikely((dma_reason[0] & BCM43xx_DMAIRQ_NONFATALMASK) |
+ (dma_reason[1] & BCM43xx_DMAIRQ_NONFATALMASK) |
+ (dma_reason[2] & BCM43xx_DMAIRQ_NONFATALMASK) |
+ (dma_reason[3] & BCM43xx_DMAIRQ_NONFATALMASK))) {
+ printkl(KERN_ERR PFX "DMA error: "
+ "0x%08X, 0x%08X, 0x%08X, 0x%08X\n",
+ dma_reason[0], dma_reason[1],
+ dma_reason[2], dma_reason[3]);
+ }
+
+ if (reason & BCM43xx_IRQ_PS) {
+ handle_irq_ps(bcm);
+ bcmirq_handled(BCM43xx_IRQ_PS);
+ }
+
+ if (reason & BCM43xx_IRQ_REG124) {
+ handle_irq_reg124(bcm);
+ bcmirq_handled(BCM43xx_IRQ_REG124);
+ }
+
+ if (reason & BCM43xx_IRQ_BEACON) {
+ if (bcm->ieee->iw_mode == IW_MODE_MASTER)
+ handle_irq_beacon(bcm);
+ bcmirq_handled(BCM43xx_IRQ_BEACON);
+ }
+
+ if (reason & BCM43xx_IRQ_PMQ) {
+ handle_irq_pmq(bcm);
+ bcmirq_handled(BCM43xx_IRQ_PMQ);
+ }
+
+ if (reason & BCM43xx_IRQ_SCAN) {
+ /*TODO*/
+ //bcmirq_handled(BCM43xx_IRQ_SCAN);
+ }
+
+ if (reason & BCM43xx_IRQ_NOISE) {
+ handle_irq_noise(bcm);
+ bcmirq_handled(BCM43xx_IRQ_NOISE);
+ }
+
+ /* Check the DMA reason registers for received data. */
+ assert(!(dma_reason[1] & BCM43xx_DMAIRQ_RX_DONE));
+ assert(!(dma_reason[2] & BCM43xx_DMAIRQ_RX_DONE));
+ if (dma_reason[0] & BCM43xx_DMAIRQ_RX_DONE) {
+ if (bcm43xx_using_pio(bcm))
+ bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue0);
+ else
+ bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring0);
+ /* We intentionally don't set "activity" to 1, here. */
+ }
+ if (dma_reason[3] & BCM43xx_DMAIRQ_RX_DONE) {
+ if (bcm43xx_using_pio(bcm))
+ bcm43xx_pio_rx(bcm43xx_current_pio(bcm)->queue3);
+ else
+ bcm43xx_dma_rx(bcm43xx_current_dma(bcm)->rx_ring1);
+ activity = 1;
+ }
+ bcmirq_handled(BCM43xx_IRQ_RX);
+
+ if (reason & BCM43xx_IRQ_XMIT_STATUS) {
+ handle_irq_transmit_status(bcm);
+ activity = 1;
+ //TODO: In AP mode, this also causes sending of powersave responses.
+ bcmirq_handled(BCM43xx_IRQ_XMIT_STATUS);
+ }
+
+ /* IRQ_PIO_WORKAROUND is handled in the top-half. */
+ bcmirq_handled(BCM43xx_IRQ_PIO_WORKAROUND);
+#ifdef CONFIG_BCM43XX_DEBUG
+ if (unlikely(reason & ~_handled)) {
+ printkl(KERN_WARNING PFX
+ "Unhandled IRQ! Reason: 0x%08x, Unhandled: 0x%08x, "
+ "DMA: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+ reason, (reason & ~_handled),
+ dma_reason[0], dma_reason[1],
+ dma_reason[2], dma_reason[3]);
+ }
+#endif
+#undef bcmirq_handled
+
+ if (!modparam_noleds)
+ bcm43xx_leds_update(bcm, activity);
+ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+static void pio_irq_workaround(struct bcm43xx_private *bcm,
+ u16 base, int queueidx)
+{
+ u16 rxctl;
+
+ rxctl = bcm43xx_read16(bcm, base + BCM43xx_PIO_RXCTL);
+ if (rxctl & BCM43xx_PIO_RXCTL_DATAAVAILABLE)
+ bcm->dma_reason[queueidx] |= BCM43xx_DMAIRQ_RX_DONE;
+ else
+ bcm->dma_reason[queueidx] &= ~BCM43xx_DMAIRQ_RX_DONE;
+}
+
+static void bcm43xx_interrupt_ack(struct bcm43xx_private *bcm, u32 reason)
+{
+ if (bcm43xx_using_pio(bcm) &&
+ (bcm->current_core->rev < 3) &&
+ (!(reason & BCM43xx_IRQ_PIO_WORKAROUND))) {
+ /* Apply a PIO specific workaround to the dma_reasons */
+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO1_BASE, 0);
+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO2_BASE, 1);
+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO3_BASE, 2);
+ pio_irq_workaround(bcm, BCM43xx_MMIO_PIO4_BASE, 3);
+ }
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, reason);
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_REASON,
+ bcm->dma_reason[0]);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_REASON,
+ bcm->dma_reason[1]);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_REASON,
+ bcm->dma_reason[2]);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_REASON,
+ bcm->dma_reason[3]);
+}
+
+/* Interrupt handler top-half */
+static irqreturn_t bcm43xx_interrupt_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ irqreturn_t ret = IRQ_HANDLED;
+ struct bcm43xx_private *bcm = dev_id;
+ u32 reason;
+
+ if (!bcm)
+ return IRQ_NONE;
+
+ spin_lock(&bcm->_lock);
+
+ reason = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+ if (reason == 0xffffffff) {
+ /* irq not for us (shared irq) */
+ ret = IRQ_NONE;
+ goto out;
+ }
+ reason &= bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_MASK);
+ if (!reason)
+ goto out;
+
+ bcm->dma_reason[0] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA1_REASON)
+ & 0x0001dc00;
+ bcm->dma_reason[1] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA2_REASON)
+ & 0x0000dc00;
+ bcm->dma_reason[2] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA3_REASON)
+ & 0x0000dc00;
+ bcm->dma_reason[3] = bcm43xx_read32(bcm, BCM43xx_MMIO_DMA4_REASON)
+ & 0x0001dc00;
+
+ bcm43xx_interrupt_ack(bcm, reason);
+
+ /* Only accept IRQs, if we are initialized properly.
+ * This avoids an RX race while initializing.
+ * We should probably not enable IRQs before we are initialized
+ * completely, but some careful work is needed to fix this. I think it
+ * is best to stay with this cheap workaround for now... .
+ */
+ if (likely(bcm->initialized)) {
+ /* disable all IRQs. They are enabled again in the bottom half. */
+ bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ /* save the reason code and call our bottom half. */
+ bcm->irq_reason = reason;
+ tasklet_schedule(&bcm->isr_tasklet);
+ }
+
+out:
+ mmiowb();
+ spin_unlock(&bcm->_lock);
+
+ return ret;
+}
+
+static void bcm43xx_release_firmware(struct bcm43xx_private *bcm, int force)
+{
+ if (bcm->firmware_norelease && !force)
+ return; /* Suspending or controller reset. */
+ release_firmware(bcm->ucode);
+ bcm->ucode = NULL;
+ release_firmware(bcm->pcm);
+ bcm->pcm = NULL;
+ release_firmware(bcm->initvals0);
+ bcm->initvals0 = NULL;
+ release_firmware(bcm->initvals1);
+ bcm->initvals1 = NULL;
+}
+
+static int bcm43xx_request_firmware(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u8 rev = bcm->current_core->rev;
+ int err = 0;
+ int nr;
+ char buf[22 + sizeof(modparam_fwpostfix) - 1] = { 0 };
+
+ if (!bcm->ucode) {
+ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_microcode%d%s.fw",
+ (rev >= 5 ? 5 : rev),
+ modparam_fwpostfix);
+ err = request_firmware(&bcm->ucode, buf, &bcm->pci_dev->dev);
+ if (err) {
+ printk(KERN_ERR PFX
+ "Error: Microcode \"%s\" not available or load failed.\n",
+ buf);
+ goto error;
+ }
+ }
+
+ if (!bcm->pcm) {
+ snprintf(buf, ARRAY_SIZE(buf),
+ "bcm43xx_pcm%d%s.fw",
+ (rev < 5 ? 4 : 5),
+ modparam_fwpostfix);
+ err = request_firmware(&bcm->pcm, buf, &bcm->pci_dev->dev);
+ if (err) {
+ printk(KERN_ERR PFX
+ "Error: PCM \"%s\" not available or load failed.\n",
+ buf);
+ goto error;
+ }
+ }
+
+ if (!bcm->initvals0) {
+ if (rev == 2 || rev == 4) {
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ nr = 3;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ nr = 1;
+ break;
+ default:
+ goto err_noinitval;
+ }
+
+ } else if (rev >= 5) {
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ nr = 7;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ nr = 5;
+ break;
+ default:
+ goto err_noinitval;
+ }
+ } else
+ goto err_noinitval;
+ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",
+ nr, modparam_fwpostfix);
+
+ err = request_firmware(&bcm->initvals0, buf, &bcm->pci_dev->dev);
+ if (err) {
+ printk(KERN_ERR PFX
+ "Error: InitVals \"%s\" not available or load failed.\n",
+ buf);
+ goto error;
+ }
+ if (bcm->initvals0->size % sizeof(struct bcm43xx_initval)) {
+ printk(KERN_ERR PFX "InitVals fileformat error.\n");
+ goto error;
+ }
+ }
+
+ if (!bcm->initvals1) {
+ if (rev >= 5) {
+ u32 sbtmstatehigh;
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ sbtmstatehigh = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
+ if (sbtmstatehigh & 0x00010000)
+ nr = 9;
+ else
+ nr = 10;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ nr = 6;
+ break;
+ default:
+ goto err_noinitval;
+ }
+ snprintf(buf, ARRAY_SIZE(buf), "bcm43xx_initval%02d%s.fw",
+ nr, modparam_fwpostfix);
+
+ err = request_firmware(&bcm->initvals1, buf, &bcm->pci_dev->dev);
+ if (err) {
+ printk(KERN_ERR PFX
+ "Error: InitVals \"%s\" not available or load failed.\n",
+ buf);
+ goto error;
+ }
+ if (bcm->initvals1->size % sizeof(struct bcm43xx_initval)) {
+ printk(KERN_ERR PFX "InitVals fileformat error.\n");
+ goto error;
+ }
+ }
+ }
+
+out:
+ return err;
+error:
+ bcm43xx_release_firmware(bcm, 1);
+ goto out;
+err_noinitval:
+ printk(KERN_ERR PFX "Error: No InitVals available!\n");
+ err = -ENOENT;
+ goto error;
+}
+
+static void bcm43xx_upload_microcode(struct bcm43xx_private *bcm)
+{
+ const u32 *data;
+ unsigned int i, len;
+
+ /* Upload Microcode. */
+ data = (u32 *)(bcm->ucode->data);
+ len = bcm->ucode->size / sizeof(u32);
+ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_UCODE, 0x0000);
+ for (i = 0; i < len; i++) {
+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA,
+ be32_to_cpu(data[i]));
+ udelay(10);
+ }
+
+ /* Upload PCM data. */
+ data = (u32 *)(bcm->pcm->data);
+ len = bcm->pcm->size / sizeof(u32);
+ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01ea);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA, 0x00004000);
+ bcm43xx_shm_control_word(bcm, BCM43xx_SHM_PCM, 0x01eb);
+ for (i = 0; i < len; i++) {
+ bcm43xx_write32(bcm, BCM43xx_MMIO_SHM_DATA,
+ be32_to_cpu(data[i]));
+ udelay(10);
+ }
+}
+
+static int bcm43xx_write_initvals(struct bcm43xx_private *bcm,
+ const struct bcm43xx_initval *data,
+ const unsigned int len)
+{
+ u16 offset, size;
+ u32 value;
+ unsigned int i;
+
+ for (i = 0; i < len; i++) {
+ offset = be16_to_cpu(data[i].offset);
+ size = be16_to_cpu(data[i].size);
+ value = be32_to_cpu(data[i].value);
+
+ if (unlikely(offset >= 0x1000))
+ goto err_format;
+ if (size == 2) {
+ if (unlikely(value & 0xFFFF0000))
+ goto err_format;
+ bcm43xx_write16(bcm, offset, (u16)value);
+ } else if (size == 4) {
+ bcm43xx_write32(bcm, offset, value);
+ } else
+ goto err_format;
+ }
+
+ return 0;
+
+err_format:
+ printk(KERN_ERR PFX "InitVals (bcm43xx_initvalXX.fw) file-format error. "
+ "Please fix your bcm43xx firmware files.\n");
+ return -EPROTO;
+}
+
+static int bcm43xx_upload_initvals(struct bcm43xx_private *bcm)
+{
+ int err;
+
+ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals0->data,
+ bcm->initvals0->size / sizeof(struct bcm43xx_initval));
+ if (err)
+ goto out;
+ if (bcm->initvals1) {
+ err = bcm43xx_write_initvals(bcm, (struct bcm43xx_initval *)bcm->initvals1->data,
+ bcm->initvals1->size / sizeof(struct bcm43xx_initval));
+ if (err)
+ goto out;
+ }
+out:
+ return err;
+}
+
+static int bcm43xx_initialize_irq(struct bcm43xx_private *bcm)
+{
+ int res;
+ unsigned int i;
+ u32 data;
+
+ bcm->irq = bcm->pci_dev->irq;
+#ifdef CONFIG_BCM947XX
+ if (bcm->pci_dev->bus->number == 0) {
+ struct pci_dev *d = NULL;
+ /* FIXME: we will probably need more device IDs here... */
+ d = pci_find_device(PCI_VENDOR_ID_BROADCOM, 0x4324, NULL);
+ if (d != NULL) {
+ bcm->irq = d->irq;
+ }
+ }
+#endif
+ res = request_irq(bcm->irq, bcm43xx_interrupt_handler,
+ SA_SHIRQ, KBUILD_MODNAME, bcm);
+ if (res) {
+ printk(KERN_ERR PFX "Cannot register IRQ%d\n", bcm->irq);
+ return -ENODEV;
+ }
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0xffffffff);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, 0x00020402);
+ i = 0;
+ while (1) {
+ data = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+ if (data == BCM43xx_IRQ_READY)
+ break;
+ i++;
+ if (i >= BCM43xx_IRQWAIT_MAX_RETRIES) {
+ printk(KERN_ERR PFX "Card IRQ register not responding. "
+ "Giving up.\n");
+ free_irq(bcm->irq, bcm);
+ return -ENODEV;
+ }
+ udelay(10);
+ }
+ // dummy read
+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+
+ return 0;
+}
+
+/* Switch to the core used to write the GPIO register.
+ * This is either the ChipCommon, or the PCI core.
+ */
+static int switch_to_gpio_core(struct bcm43xx_private *bcm)
+{
+ int err;
+
+ /* Where to find the GPIO register depends on the chipset.
+ * If it has a ChipCommon, its register at offset 0x6c is the GPIO
+ * control register. Otherwise the register at offset 0x6c in the
+ * PCI core is the GPIO control register.
+ */
+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
+ if (err == -ENODEV) {
+ err = bcm43xx_switch_core(bcm, &bcm->core_pci);
+ if (unlikely(err == -ENODEV)) {
+ printk(KERN_ERR PFX "gpio error: "
+ "Neither ChipCommon nor PCI core available!\n");
+ }
+ }
+
+ return err;
+}
+
+/* Initialize the GPIOs
+ * http://bcm-specs.sipsolutions.net/GPIO
+ */
+static int bcm43xx_gpio_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_coreinfo *old_core;
+ int err;
+ u32 mask, set;
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ & 0xFFFF3FFF);
+
+ bcm43xx_leds_switch_all(bcm, 0);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK) | 0x000F);
+
+ mask = 0x0000001F;
+ set = 0x0000000F;
+ if (bcm->chip_id == 0x4301) {
+ mask |= 0x0060;
+ set |= 0x0060;
+ }
+ if (0 /* FIXME: conditional unknown */) {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK)
+ | 0x0100);
+ mask |= 0x0180;
+ set |= 0x0180;
+ }
+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_GPIO_MASK,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_GPIO_MASK)
+ | 0x0200);
+ mask |= 0x0200;
+ set |= 0x0200;
+ }
+ if (bcm->current_core->rev >= 2)
+ mask |= 0x0010; /* FIXME: This is redundant. */
+
+ old_core = bcm->current_core;
+ err = switch_to_gpio_core(bcm);
+ if (err)
+ goto out;
+ bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL,
+ (bcm43xx_read32(bcm, BCM43xx_GPIO_CONTROL) & mask) | set);
+ err = bcm43xx_switch_core(bcm, old_core);
+out:
+ return err;
+}
+
+/* Turn off all GPIO stuff. Call this on module unload, for example. */
+static int bcm43xx_gpio_cleanup(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_coreinfo *old_core;
+ int err;
+
+ old_core = bcm->current_core;
+ err = switch_to_gpio_core(bcm);
+ if (err)
+ return err;
+ bcm43xx_write32(bcm, BCM43xx_GPIO_CONTROL, 0x00000000);
+ err = bcm43xx_switch_core(bcm, old_core);
+ assert(err == 0);
+
+ return 0;
+}
+
+/* http://bcm-specs.sipsolutions.net/EnableMac */
+void bcm43xx_mac_enable(struct bcm43xx_private *bcm)
+{
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ | BCM43xx_SBF_MAC_ENABLED);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, BCM43xx_IRQ_READY);
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */
+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
+ bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
+}
+
+/* http://bcm-specs.sipsolutions.net/SuspendMAC */
+void bcm43xx_mac_suspend(struct bcm43xx_private *bcm)
+{
+ int i;
+ u32 tmp;
+
+ bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD)
+ & ~BCM43xx_SBF_MAC_ENABLED);
+ bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON); /* dummy read */
+ for (i = 100000; i; i--) {
+ tmp = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+ if (tmp & BCM43xx_IRQ_READY)
+ return;
+ udelay(10);
+ }
+ printkl(KERN_ERR PFX "MAC suspend failed\n");
+}
+
+void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,
+ int iw_mode)
+{
+ unsigned long flags;
+ struct net_device *net_dev = bcm->net_dev;
+ u32 status;
+ u16 value;
+
+ spin_lock_irqsave(&bcm->ieee->lock, flags);
+ bcm->ieee->iw_mode = iw_mode;
+ spin_unlock_irqrestore(&bcm->ieee->lock, flags);
+ if (iw_mode == IW_MODE_MONITOR)
+ net_dev->type = ARPHRD_IEEE80211;
+ else
+ net_dev->type = ARPHRD_ETHER;
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ /* Reset status to infrastructured mode */
+ status &= ~(BCM43xx_SBF_MODE_AP | BCM43xx_SBF_MODE_MONITOR);
+ status &= ~BCM43xx_SBF_MODE_PROMISC;
+ status |= BCM43xx_SBF_MODE_NOTADHOC;
+
+/* FIXME: Always enable promisc mode, until we get the MAC filters working correctly. */
+status |= BCM43xx_SBF_MODE_PROMISC;
+
+ switch (iw_mode) {
+ case IW_MODE_MONITOR:
+ status |= BCM43xx_SBF_MODE_MONITOR;
+ status |= BCM43xx_SBF_MODE_PROMISC;
+ break;
+ case IW_MODE_ADHOC:
+ status &= ~BCM43xx_SBF_MODE_NOTADHOC;
+ break;
+ case IW_MODE_MASTER:
+ status |= BCM43xx_SBF_MODE_AP;
+ break;
+ case IW_MODE_SECOND:
+ case IW_MODE_REPEAT:
+ TODO(); /* TODO */
+ break;
+ case IW_MODE_INFRA:
+ /* nothing to be done here... */
+ break;
+ default:
+ dprintk(KERN_ERR PFX "Unknown mode in set_iwmode: %d\n", iw_mode);
+ }
+ if (net_dev->flags & IFF_PROMISC)
+ status |= BCM43xx_SBF_MODE_PROMISC;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+
+ value = 0x0002;
+ if (iw_mode != IW_MODE_ADHOC && iw_mode != IW_MODE_MASTER) {
+ if (bcm->chip_id == 0x4306 && bcm->chip_rev == 3)
+ value = 0x0064;
+ else
+ value = 0x0032;
+ }
+ bcm43xx_write16(bcm, 0x0612, value);
+}
+
+/* This is the opposite of bcm43xx_chip_init() */
+static void bcm43xx_chip_cleanup(struct bcm43xx_private *bcm)
+{
+ bcm43xx_radio_turn_off(bcm);
+ if (!modparam_noleds)
+ bcm43xx_leds_exit(bcm);
+ bcm43xx_gpio_cleanup(bcm);
+ free_irq(bcm->irq, bcm);
+ bcm43xx_release_firmware(bcm, 0);
+}
+
+/* Initialize the chip
+ * http://bcm-specs.sipsolutions.net/ChipInit
+ */
+static int bcm43xx_chip_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ int err;
+ int tmp;
+ u32 value32;
+ u16 value16;
+
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD,
+ BCM43xx_SBF_CORE_READY
+ | BCM43xx_SBF_400);
+
+ err = bcm43xx_request_firmware(bcm);
+ if (err)
+ goto out;
+ bcm43xx_upload_microcode(bcm);
+
+ err = bcm43xx_initialize_irq(bcm);
+ if (err)
+ goto err_release_fw;
+
+ err = bcm43xx_gpio_init(bcm);
+ if (err)
+ goto err_free_irq;
+
+ err = bcm43xx_upload_initvals(bcm);
+ if (err)
+ goto err_gpio_cleanup;
+ bcm43xx_radio_turn_on(bcm);
+
+ bcm43xx_write16(bcm, 0x03E6, 0x0000);
+ err = bcm43xx_phy_init(bcm);
+ if (err)
+ goto err_radio_off;
+
+ /* Select initial Interference Mitigation. */
+ tmp = radio->interfmode;
+ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;
+ bcm43xx_radio_set_interference_mitigation(bcm, tmp);
+
+ bcm43xx_phy_set_antenna_diversity(bcm);
+ bcm43xx_radio_set_txantenna(bcm, BCM43xx_RADIO_TXANTENNA_DEFAULT);
+ if (phy->type == BCM43xx_PHYTYPE_B) {
+ value16 = bcm43xx_read16(bcm, 0x005E);
+ value16 |= 0x0004;
+ bcm43xx_write16(bcm, 0x005E, value16);
+ }
+ bcm43xx_write32(bcm, 0x0100, 0x01000000);
+ if (bcm->current_core->rev < 5)
+ bcm43xx_write32(bcm, 0x010C, 0x01000000);
+
+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ value32 &= ~ BCM43xx_SBF_MODE_NOTADHOC;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32);
+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ value32 |= BCM43xx_SBF_MODE_NOTADHOC;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32);
+
+ value32 = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ value32 |= 0x100000;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value32);
+
+ if (bcm43xx_using_pio(bcm)) {
+ bcm43xx_write32(bcm, 0x0210, 0x00000100);
+ bcm43xx_write32(bcm, 0x0230, 0x00000100);
+ bcm43xx_write32(bcm, 0x0250, 0x00000100);
+ bcm43xx_write32(bcm, 0x0270, 0x00000100);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0034, 0x0000);
+ }
+
+ /* Probe Response Timeout value */
+ /* FIXME: Default to 0, has to be set by ioctl probably... :-/ */
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0074, 0x0000);
+
+ /* Initially set the wireless operation mode. */
+ bcm43xx_set_iwmode(bcm, bcm->ieee->iw_mode);
+
+ if (bcm->current_core->rev < 3) {
+ bcm43xx_write16(bcm, 0x060E, 0x0000);
+ bcm43xx_write16(bcm, 0x0610, 0x8000);
+ bcm43xx_write16(bcm, 0x0604, 0x0000);
+ bcm43xx_write16(bcm, 0x0606, 0x0200);
+ } else {
+ bcm43xx_write32(bcm, 0x0188, 0x80000000);
+ bcm43xx_write32(bcm, 0x018C, 0x02000000);
+ }
+ bcm43xx_write32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON, 0x00004000);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA1_IRQ_MASK, 0x0001DC00);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA2_IRQ_MASK, 0x0000DC00);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA3_IRQ_MASK, 0x0000DC00);
+ bcm43xx_write32(bcm, BCM43xx_MMIO_DMA4_IRQ_MASK, 0x0001DC00);
+
+ value32 = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ value32 |= 0x00100000;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, value32);
+
+ bcm43xx_write16(bcm, BCM43xx_MMIO_POWERUP_DELAY, bcm43xx_pctl_powerup_delay(bcm));
+
+ assert(err == 0);
+ dprintk(KERN_INFO PFX "Chip initialized\n");
+out:
+ return err;
+
+err_radio_off:
+ bcm43xx_radio_turn_off(bcm);
+err_gpio_cleanup:
+ bcm43xx_gpio_cleanup(bcm);
+err_free_irq:
+ free_irq(bcm->irq, bcm);
+err_release_fw:
+ bcm43xx_release_firmware(bcm, 1);
+ goto out;
+}
+
+/* Validate chip access
+ * http://bcm-specs.sipsolutions.net/ValidateChipAccess */
+static int bcm43xx_validate_chip(struct bcm43xx_private *bcm)
+{
+ u32 value;
+ u32 shm_backup;
+
+ shm_backup = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0xAA5555AA);
+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0xAA5555AA)
+ goto error;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, 0x55AAAA55);
+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0000) != 0x55AAAA55)
+ goto error;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED, 0x0000, shm_backup);
+
+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ if ((value | 0x80000000) != 0x80000400)
+ goto error;
+
+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_GEN_IRQ_REASON);
+ if (value != 0x00000000)
+ goto error;
+
+ return 0;
+error:
+ printk(KERN_ERR PFX "Failed to validate the chipaccess\n");
+ return -ENODEV;
+}
+
+static void bcm43xx_init_struct_phyinfo(struct bcm43xx_phyinfo *phy)
+{
+ /* Initialize a "phyinfo" structure. The structure is already
+ * zeroed out.
+ */
+ phy->antenna_diversity = 0xFFFF;
+ phy->savedpctlreg = 0xFFFF;
+ phy->minlowsig[0] = 0xFFFF;
+ phy->minlowsig[1] = 0xFFFF;
+ spin_lock_init(&phy->lock);
+}
+
+static void bcm43xx_init_struct_radioinfo(struct bcm43xx_radioinfo *radio)
+{
+ /* Initialize a "radioinfo" structure. The structure is already
+ * zeroed out.
+ */
+ radio->interfmode = BCM43xx_RADIO_INTERFMODE_NONE;
+ radio->channel = 0xFF;
+ radio->initial_channel = 0xFF;
+ radio->lofcal = 0xFFFF;
+ radio->initval = 0xFFFF;
+ radio->nrssi[0] = -1000;
+ radio->nrssi[1] = -1000;
+}
+
+static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
+{
+ int err, i;
+ int current_core;
+ u32 core_vendor, core_id, core_rev;
+ u32 sb_id_hi, chip_id_32 = 0;
+ u16 pci_device, chip_id_16;
+ u8 core_count;
+
+ memset(&bcm->core_chipcommon, 0, sizeof(struct bcm43xx_coreinfo));
+ memset(&bcm->core_pci, 0, sizeof(struct bcm43xx_coreinfo));
+ memset(&bcm->core_80211, 0, sizeof(struct bcm43xx_coreinfo)
+ * BCM43xx_MAX_80211_CORES);
+ memset(&bcm->core_80211_ext, 0, sizeof(struct bcm43xx_coreinfo_80211)
+ * BCM43xx_MAX_80211_CORES);
+ bcm->current_80211_core_idx = -1;
+ bcm->nr_80211_available = 0;
+ bcm->current_core = NULL;
+ bcm->active_80211_core = NULL;
+
+ /* map core 0 */
+ err = _switch_core(bcm, 0);
+ if (err)
+ goto out;
+
+ /* fetch sb_id_hi from core information registers */
+ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);
+
+ core_id = (sb_id_hi & 0xFFF0) >> 4;
+ core_rev = (sb_id_hi & 0xF);
+ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;
+
+ /* if present, chipcommon is always core 0; read the chipid from it */
+ if (core_id == BCM43xx_COREID_CHIPCOMMON) {
+ chip_id_32 = bcm43xx_read32(bcm, 0);
+ chip_id_16 = chip_id_32 & 0xFFFF;
+ bcm->core_chipcommon.available = 1;
+ bcm->core_chipcommon.id = core_id;
+ bcm->core_chipcommon.rev = core_rev;
+ bcm->core_chipcommon.index = 0;
+ /* While we are at it, also read the capabilities. */
+ bcm->chipcommon_capabilities = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_CAPABILITIES);
+ } else {
+ /* without a chipCommon, use a hard coded table. */
+ pci_device = bcm->pci_dev->device;
+ if (pci_device == 0x4301)
+ chip_id_16 = 0x4301;
+ else if ((pci_device >= 0x4305) && (pci_device <= 0x4307))
+ chip_id_16 = 0x4307;
+ else if ((pci_device >= 0x4402) && (pci_device <= 0x4403))
+ chip_id_16 = 0x4402;
+ else if ((pci_device >= 0x4610) && (pci_device <= 0x4615))
+ chip_id_16 = 0x4610;
+ else if ((pci_device >= 0x4710) && (pci_device <= 0x4715))
+ chip_id_16 = 0x4710;
+#ifdef CONFIG_BCM947XX
+ else if ((pci_device >= 0x4320) && (pci_device <= 0x4325))
+ chip_id_16 = 0x4309;
+#endif
+ else {
+ printk(KERN_ERR PFX "Could not determine Chip ID\n");
+ return -ENODEV;
+ }
+ }
+
+ /* ChipCommon with Core Rev >=4 encodes number of cores,
+ * otherwise consult hardcoded table */
+ if ((core_id == BCM43xx_COREID_CHIPCOMMON) && (core_rev >= 4)) {
+ core_count = (chip_id_32 & 0x0F000000) >> 24;
+ } else {
+ switch (chip_id_16) {
+ case 0x4610:
+ case 0x4704:
+ case 0x4710:
+ core_count = 9;
+ break;
+ case 0x4310:
+ core_count = 8;
+ break;
+ case 0x5365:
+ core_count = 7;
+ break;
+ case 0x4306:
+ core_count = 6;
+ break;
+ case 0x4301:
+ case 0x4307:
+ core_count = 5;
+ break;
+ case 0x4402:
+ core_count = 3;
+ break;
+ default:
+ /* SOL if we get here */
+ assert(0);
+ core_count = 1;
+ }
+ }
+
+ bcm->chip_id = chip_id_16;
+ bcm->chip_rev = (chip_id_32 & 0x000F0000) >> 16;
+ bcm->chip_package = (chip_id_32 & 0x00F00000) >> 20;
+
+ dprintk(KERN_INFO PFX "Chip ID 0x%x, rev 0x%x\n",
+ bcm->chip_id, bcm->chip_rev);
+ dprintk(KERN_INFO PFX "Number of cores: %d\n", core_count);
+ if (bcm->core_chipcommon.available) {
+ dprintk(KERN_INFO PFX "Core 0: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n",
+ core_id, core_rev, core_vendor,
+ bcm43xx_core_enabled(bcm) ? "enabled" : "disabled");
+ }
+
+ if (bcm->core_chipcommon.available)
+ current_core = 1;
+ else
+ current_core = 0;
+ for ( ; current_core < core_count; current_core++) {
+ struct bcm43xx_coreinfo *core;
+ struct bcm43xx_coreinfo_80211 *ext_80211;
+
+ err = _switch_core(bcm, current_core);
+ if (err)
+ goto out;
+ /* Gather information */
+ /* fetch sb_id_hi from core information registers */
+ sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);
+
+ /* extract core_id, core_rev, core_vendor */
+ core_id = (sb_id_hi & 0xFFF0) >> 4;
+ core_rev = (sb_id_hi & 0xF);
+ core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;
+
+ dprintk(KERN_INFO PFX "Core %d: ID 0x%x, rev 0x%x, vendor 0x%x, %s\n",
+ current_core, core_id, core_rev, core_vendor,
+ bcm43xx_core_enabled(bcm) ? "enabled" : "disabled" );
+
+ core = NULL;
+ switch (core_id) {
+ case BCM43xx_COREID_PCI:
+ core = &bcm->core_pci;
+ if (core->available) {
+ printk(KERN_WARNING PFX "Multiple PCI cores found.\n");
+ continue;
+ }
+ break;
+ case BCM43xx_COREID_80211:
+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
+ core = &(bcm->core_80211[i]);
+ ext_80211 = &(bcm->core_80211_ext[i]);
+ if (!core->available)
+ break;
+ core = NULL;
+ }
+ if (!core) {
+ printk(KERN_WARNING PFX "More than %d cores of type 802.11 found.\n",
+ BCM43xx_MAX_80211_CORES);
+ continue;
+ }
+ if (i != 0) {
+ /* More than one 80211 core is only supported
+ * by special chips.
+ * There are chips with two 80211 cores, but with
+ * dangling pins on the second core. Be careful
+ * and ignore these cores here.
+ */
+ if (bcm->pci_dev->device != 0x4324) {
+ dprintk(KERN_INFO PFX "Ignoring additional 802.11 core.\n");
+ continue;
+ }
+ }
+ switch (core_rev) {
+ case 2:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 9:
+ break;
+ default:
+ printk(KERN_ERR PFX "Error: Unsupported 80211 core revision %u\n",
+ core_rev);
+ err = -ENODEV;
+ goto out;
+ }
+ bcm->nr_80211_available++;
+ bcm43xx_init_struct_phyinfo(&ext_80211->phy);
+ bcm43xx_init_struct_radioinfo(&ext_80211->radio);
+ break;
+ case BCM43xx_COREID_CHIPCOMMON:
+ printk(KERN_WARNING PFX "Multiple CHIPCOMMON cores found.\n");
+ break;
+ }
+ if (core) {
+ core->available = 1;
+ core->id = core_id;
+ core->rev = core_rev;
+ core->index = current_core;
+ }
+ }
+
+ if (!bcm->core_80211[0].available) {
+ printk(KERN_ERR PFX "Error: No 80211 core found!\n");
+ err = -ENODEV;
+ goto out;
+ }
+
+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
+
+ assert(err == 0);
+out:
+ return err;
+}
+
+static void bcm43xx_gen_bssid(struct bcm43xx_private *bcm)
+{
+ const u8 *mac = (const u8*)(bcm->net_dev->dev_addr);
+ u8 *bssid = bcm->ieee->bssid;
+
+ switch (bcm->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ random_ether_addr(bssid);
+ break;
+ case IW_MODE_MASTER:
+ case IW_MODE_INFRA:
+ case IW_MODE_REPEAT:
+ case IW_MODE_SECOND:
+ case IW_MODE_MONITOR:
+ memcpy(bssid, mac, ETH_ALEN);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void bcm43xx_rate_memory_write(struct bcm43xx_private *bcm,
+ u16 rate,
+ int is_ofdm)
+{
+ u16 offset;
+
+ if (is_ofdm) {
+ offset = 0x480;
+ offset += (bcm43xx_plcp_get_ratecode_ofdm(rate) & 0x000F) * 2;
+ }
+ else {
+ offset = 0x4C0;
+ offset += (bcm43xx_plcp_get_ratecode_cck(rate) & 0x000F) * 2;
+ }
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, offset + 0x20,
+ bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, offset));
+}
+
+static void bcm43xx_rate_memory_init(struct bcm43xx_private *bcm)
+{
+ switch (bcm43xx_current_phy(bcm)->type) {
+ case BCM43xx_PHYTYPE_A:
+ case BCM43xx_PHYTYPE_G:
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_6MB, 1);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_12MB, 1);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_18MB, 1);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_24MB, 1);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_36MB, 1);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_48MB, 1);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_OFDM_RATE_54MB, 1);
+ case BCM43xx_PHYTYPE_B:
+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_1MB, 0);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_2MB, 0);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_5MB, 0);
+ bcm43xx_rate_memory_write(bcm, IEEE80211_CCK_RATE_11MB, 0);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void bcm43xx_wireless_core_cleanup(struct bcm43xx_private *bcm)
+{
+ bcm43xx_chip_cleanup(bcm);
+ bcm43xx_pio_free(bcm);
+ bcm43xx_dma_free(bcm);
+
+ bcm->current_core->initialized = 0;
+}
+
+/* http://bcm-specs.sipsolutions.net/80211Init */
+static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u32 ucodeflags;
+ int err;
+ u32 sbimconfiglow;
+ u8 limit;
+
+ if (bcm->chip_rev < 5) {
+ sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);
+ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;
+ sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;
+ if (bcm->bustype == BCM43xx_BUSTYPE_PCI)
+ sbimconfiglow |= 0x32;
+ else if (bcm->bustype == BCM43xx_BUSTYPE_SB)
+ sbimconfiglow |= 0x53;
+ else
+ assert(0);
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow);
+ }
+
+ bcm43xx_phy_calibrate(bcm);
+ err = bcm43xx_chip_init(bcm);
+ if (err)
+ goto out;
+
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0016, bcm->current_core->rev);
+ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, BCM43xx_UCODEFLAGS_OFFSET);
+
+ if (0 /*FIXME: which condition has to be used here? */)
+ ucodeflags |= 0x00000010;
+
+ /* HW decryption needs to be set now */
+ ucodeflags |= 0x40000000;
+
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;
+ if (phy->rev == 1)
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKGPHY;
+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKPACTRL;
+ } else if (phy->type == BCM43xx_PHYTYPE_B) {
+ ucodeflags |= BCM43xx_UCODEFLAG_UNKBGPHY;
+ if (phy->rev >= 2 && radio->version == 0x2050)
+ ucodeflags &= ~BCM43xx_UCODEFLAG_UNKGPHY;
+ }
+
+ if (ucodeflags != bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET)) {
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET, ucodeflags);
+ }
+
+ /* Short/Long Retry Limit.
+ * The retry-limit is a 4-bit counter. Enforce this to avoid overflowing
+ * the chip-internal counter.
+ */
+ limit = limit_value(modparam_short_retry, 0, 0xF);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0006, limit);
+ limit = limit_value(modparam_long_retry, 0, 0xF);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0007, limit);
+
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0044, 3);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0046, 2);
+
+ bcm43xx_rate_memory_init(bcm);
+
+ /* Minimum Contention Window */
+ if (phy->type == BCM43xx_PHYTYPE_B)
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000001f);
+ else
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0003, 0x0000000f);
+ /* Maximum Contention Window */
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_WIRELESS, 0x0004, 0x000003ff);
+
+ bcm43xx_gen_bssid(bcm);
+ bcm43xx_write_mac_bssid_templates(bcm);
+
+ if (bcm->current_core->rev >= 5)
+ bcm43xx_write16(bcm, 0x043C, 0x000C);
+
+ if (bcm43xx_using_pio(bcm))
+ err = bcm43xx_pio_init(bcm);
+ else
+ err = bcm43xx_dma_init(bcm);
+ if (err)
+ goto err_chip_cleanup;
+ bcm43xx_write16(bcm, 0x0612, 0x0050);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0416, 0x0050);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0414, 0x01F4);
+
+ bcm43xx_mac_enable(bcm);
+ bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
+
+ bcm->current_core->initialized = 1;
+out:
+ return err;
+
+err_chip_cleanup:
+ bcm43xx_chip_cleanup(bcm);
+ goto out;
+}
+
+static int bcm43xx_chipset_attach(struct bcm43xx_private *bcm)
+{
+ int err;
+ u16 pci_status;
+
+ err = bcm43xx_pctl_set_crystal(bcm, 1);
+ if (err)
+ goto out;
+ bcm43xx_pci_read_config16(bcm, PCI_STATUS, &pci_status);
+ bcm43xx_pci_write_config16(bcm, PCI_STATUS, pci_status & ~PCI_STATUS_SIG_TARGET_ABORT);
+
+out:
+ return err;
+}
+
+static void bcm43xx_chipset_detach(struct bcm43xx_private *bcm)
+{
+ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
+ bcm43xx_pctl_set_crystal(bcm, 0);
+}
+
+static void bcm43xx_pcicore_broadcast_value(struct bcm43xx_private *bcm,
+ u32 address,
+ u32 data)
+{
+ bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_ADDR, address);
+ bcm43xx_write32(bcm, BCM43xx_PCICORE_BCAST_DATA, data);
+}
+
+static int bcm43xx_pcicore_commit_settings(struct bcm43xx_private *bcm)
+{
+ int err;
+ struct bcm43xx_coreinfo *old_core;
+
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, &bcm->core_pci);
+ if (err)
+ goto out;
+
+ bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000);
+
+ bcm43xx_switch_core(bcm, old_core);
+ assert(err == 0);
+out:
+ return err;
+}
+
+/* Make an I/O Core usable. "core_mask" is the bitmask of the cores to enable.
+ * To enable core 0, pass a core_mask of 1<<0
+ */
+static int bcm43xx_setup_backplane_pci_connection(struct bcm43xx_private *bcm,
+ u32 core_mask)
+{
+ u32 backplane_flag_nr;
+ u32 value;
+ struct bcm43xx_coreinfo *old_core;
+ int err = 0;
+
+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBTPSFLAG);
+ backplane_flag_nr = value & BCM43xx_BACKPLANE_FLAG_NR_MASK;
+
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, &bcm->core_pci);
+ if (err)
+ goto out;
+
+ if (bcm->core_pci.rev < 6) {
+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBINTVEC);
+ value |= (1 << backplane_flag_nr);
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBINTVEC, value);
+ } else {
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCICFG_ICR, &value);
+ if (err) {
+ printk(KERN_ERR PFX "Error: ICR setup failure!\n");
+ goto out_switch_back;
+ }
+ value |= core_mask << 8;
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCICFG_ICR, value);
+ if (err) {
+ printk(KERN_ERR PFX "Error: ICR setup failure!\n");
+ goto out_switch_back;
+ }
+ }
+
+ value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2);
+ value |= BCM43xx_SBTOPCI2_PREFETCH | BCM43xx_SBTOPCI2_BURST;
+ bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value);
+
+ if (bcm->core_pci.rev < 5) {
+ value = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);
+ value |= (2 << BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT)
+ & BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;
+ value |= (3 << BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT)
+ & BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, value);
+ err = bcm43xx_pcicore_commit_settings(bcm);
+ assert(err == 0);
+ }
+
+out_switch_back:
+ err = bcm43xx_switch_core(bcm, old_core);
+out:
+ return err;
+}
+
+static void bcm43xx_softmac_init(struct bcm43xx_private *bcm)
+{
+ ieee80211softmac_start(bcm->net_dev);
+}
+
+static void bcm43xx_periodic_every120sec(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ if (phy->type != BCM43xx_PHYTYPE_G || phy->rev < 2)
+ return;
+
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_phy_lo_g_measure(bcm);
+ bcm43xx_mac_enable(bcm);
+}
+
+static void bcm43xx_periodic_every60sec(struct bcm43xx_private *bcm)
+{
+ bcm43xx_phy_lo_mark_all_unused(bcm);
+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_calc_nrssi_slope(bcm);
+ bcm43xx_mac_enable(bcm);
+ }
+}
+
+static void bcm43xx_periodic_every30sec(struct bcm43xx_private *bcm)
+{
+ /* Update device statistics. */
+ bcm43xx_calculate_link_quality(bcm);
+}
+
+static void bcm43xx_periodic_every15sec(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ //TODO: update_aci_moving_average
+ if (radio->aci_enable && radio->aci_wlan_automatic) {
+ bcm43xx_mac_suspend(bcm);
+ if (!radio->aci_enable && 1 /*TODO: not scanning? */) {
+ if (0 /*TODO: bunch of conditions*/) {
+ bcm43xx_radio_set_interference_mitigation(bcm,
+ BCM43xx_RADIO_INTERFMODE_MANUALWLAN);
+ }
+ } else if (1/*TODO*/) {
+ /*
+ if ((aci_average > 1000) && !(bcm43xx_radio_aci_scan(bcm))) {
+ bcm43xx_radio_set_interference_mitigation(bcm,
+ BCM43xx_RADIO_INTERFMODE_NONE);
+ }
+ */
+ }
+ bcm43xx_mac_enable(bcm);
+ } else if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN &&
+ phy->rev == 1) {
+ //TODO: implement rev1 workaround
+ }
+ }
+ bcm43xx_phy_xmitpower(bcm); //FIXME: unless scanning?
+ //TODO for APHY (temperature?)
+}
+
+static void bcm43xx_periodic_task_handler(unsigned long d)
+{
+ struct bcm43xx_private *bcm = (struct bcm43xx_private *)d;
+ unsigned long flags;
+ unsigned int state;
+
+ bcm43xx_lock_mmio(bcm, flags);
+
+ assert(bcm->initialized);
+ state = bcm->periodic_state;
+ if (state % 8 == 0)
+ bcm43xx_periodic_every120sec(bcm);
+ if (state % 4 == 0)
+ bcm43xx_periodic_every60sec(bcm);
+ if (state % 2 == 0)
+ bcm43xx_periodic_every30sec(bcm);
+ bcm43xx_periodic_every15sec(bcm);
+ bcm->periodic_state = state + 1;
+
+ mod_timer(&bcm->periodic_tasks, jiffies + (HZ * 15));
+
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+static void bcm43xx_periodic_tasks_delete(struct bcm43xx_private *bcm)
+{
+ del_timer_sync(&bcm->periodic_tasks);
+}
+
+static void bcm43xx_periodic_tasks_setup(struct bcm43xx_private *bcm)
+{
+ struct timer_list *timer = &(bcm->periodic_tasks);
+
+ assert(bcm->initialized);
+ setup_timer(timer,
+ bcm43xx_periodic_task_handler,
+ (unsigned long)bcm);
+ timer->expires = jiffies;
+ add_timer(timer);
+}
+
+static void bcm43xx_security_init(struct bcm43xx_private *bcm)
+{
+ bcm->security_offset = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED,
+ 0x0056) * 2;
+ bcm43xx_clear_keys(bcm);
+}
+
+/* This is the opposite of bcm43xx_init_board() */
+static void bcm43xx_free_board(struct bcm43xx_private *bcm)
+{
+ int i, err;
+ unsigned long flags;
+
+ bcm43xx_sysfs_unregister(bcm);
+
+ bcm43xx_periodic_tasks_delete(bcm);
+
+ bcm43xx_lock(bcm, flags);
+ bcm->initialized = 0;
+ bcm->shutting_down = 1;
+ bcm43xx_unlock(bcm, flags);
+
+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
+ if (!bcm->core_80211[i].available)
+ continue;
+ if (!bcm->core_80211[i].initialized)
+ continue;
+
+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
+ assert(err == 0);
+ bcm43xx_wireless_core_cleanup(bcm);
+ }
+
+ bcm43xx_pctl_set_crystal(bcm, 0);
+
+ bcm43xx_lock(bcm, flags);
+ bcm->shutting_down = 0;
+ bcm43xx_unlock(bcm, flags);
+}
+
+static int bcm43xx_init_board(struct bcm43xx_private *bcm)
+{
+ int i, err;
+ int connect_phy;
+ unsigned long flags;
+
+ might_sleep();
+
+ bcm43xx_lock(bcm, flags);
+ bcm->initialized = 0;
+ bcm->shutting_down = 0;
+ bcm43xx_unlock(bcm, flags);
+
+ err = bcm43xx_pctl_set_crystal(bcm, 1);
+ if (err)
+ goto out;
+ err = bcm43xx_pctl_init(bcm);
+ if (err)
+ goto err_crystal_off;
+ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_FAST);
+ if (err)
+ goto err_crystal_off;
+
+ tasklet_enable(&bcm->isr_tasklet);
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
+ assert(err != -ENODEV);
+ if (err)
+ goto err_80211_unwind;
+
+ /* Enable the selected wireless core.
+ * Connect PHY only on the first core.
+ */
+ if (!bcm43xx_core_enabled(bcm)) {
+ if (bcm->nr_80211_available == 1) {
+ connect_phy = bcm43xx_current_phy(bcm)->connected;
+ } else {
+ if (i == 0)
+ connect_phy = 1;
+ else
+ connect_phy = 0;
+ }
+ bcm43xx_wireless_core_reset(bcm, connect_phy);
+ }
+
+ if (i != 0)
+ bcm43xx_wireless_core_mark_inactive(bcm, &bcm->core_80211[0]);
+
+ err = bcm43xx_wireless_core_init(bcm);
+ if (err)
+ goto err_80211_unwind;
+
+ if (i != 0) {
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ bcm43xx_radio_turn_off(bcm);
+ }
+ }
+ bcm->active_80211_core = &bcm->core_80211[0];
+ if (bcm->nr_80211_available >= 2) {
+ bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
+ bcm43xx_mac_enable(bcm);
+ }
+ bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
+ bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));
+ dprintk(KERN_INFO PFX "80211 cores initialized\n");
+ bcm43xx_security_init(bcm);
+ bcm43xx_softmac_init(bcm);
+
+ bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_DYNAMIC);
+
+ if (bcm43xx_current_radio(bcm)->initial_channel != 0xFF) {
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_radio_selectchannel(bcm, bcm43xx_current_radio(bcm)->initial_channel, 0);
+ bcm43xx_mac_enable(bcm);
+ }
+
+ /* Initialization of the board is done. Flag it as such. */
+ bcm43xx_lock(bcm, flags);
+ bcm->initialized = 1;
+ bcm43xx_unlock(bcm, flags);
+
+ bcm43xx_periodic_tasks_setup(bcm);
+ bcm43xx_sysfs_register(bcm);
+ //FIXME: check for bcm43xx_sysfs_register failure. This function is a bit messy regarding unwinding, though...
+
+ assert(err == 0);
+out:
+ return err;
+
+err_80211_unwind:
+ tasklet_disable(&bcm->isr_tasklet);
+ /* unwind all 80211 initialization */
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ if (!bcm->core_80211[i].initialized)
+ continue;
+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ bcm43xx_wireless_core_cleanup(bcm);
+ }
+err_crystal_off:
+ bcm43xx_pctl_set_crystal(bcm, 0);
+ goto out;
+}
+
+static void bcm43xx_detach_board(struct bcm43xx_private *bcm)
+{
+ struct pci_dev *pci_dev = bcm->pci_dev;
+ int i;
+
+ bcm43xx_chipset_detach(bcm);
+ /* Do _not_ access the chip, after it is detached. */
+ iounmap(bcm->mmio_addr);
+
+ pci_release_regions(pci_dev);
+ pci_disable_device(pci_dev);
+
+ /* Free allocated structures/fields */
+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
+ kfree(bcm->core_80211_ext[i].phy._lo_pairs);
+ if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl)
+ kfree(bcm->core_80211_ext[i].phy.tssi2dbm);
+ }
+}
+
+static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 value;
+ u8 phy_version;
+ u8 phy_type;
+ u8 phy_rev;
+ int phy_rev_ok = 1;
+ void *p;
+
+ value = bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER);
+
+ phy_version = (value & 0xF000) >> 12;
+ phy_type = (value & 0x0F00) >> 8;
+ phy_rev = (value & 0x000F);
+
+ dprintk(KERN_INFO PFX "Detected PHY: Version: %x, Type %x, Revision %x\n",
+ phy_version, phy_type, phy_rev);
+
+ switch (phy_type) {
+ case BCM43xx_PHYTYPE_A:
+ if (phy_rev >= 4)
+ phy_rev_ok = 0;
+ /*FIXME: We need to switch the ieee->modulation, etc.. flags,
+ * if we switch 80211 cores after init is done.
+ * As we do not implement on the fly switching between
+ * wireless cores, I will leave this as a future task.
+ */
+ bcm->ieee->modulation = IEEE80211_OFDM_MODULATION;
+ bcm->ieee->mode = IEEE_A;
+ bcm->ieee->freq_band = IEEE80211_52GHZ_BAND |
+ IEEE80211_24GHZ_BAND;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ if (phy_rev != 2 && phy_rev != 4 && phy_rev != 6 && phy_rev != 7)
+ phy_rev_ok = 0;
+ bcm->ieee->modulation = IEEE80211_CCK_MODULATION;
+ bcm->ieee->mode = IEEE_B;
+ bcm->ieee->freq_band = IEEE80211_24GHZ_BAND;
+ break;
+ case BCM43xx_PHYTYPE_G:
+ if (phy_rev > 7)
+ phy_rev_ok = 0;
+ bcm->ieee->modulation = IEEE80211_OFDM_MODULATION |
+ IEEE80211_CCK_MODULATION;
+ bcm->ieee->mode = IEEE_G;
+ bcm->ieee->freq_band = IEEE80211_24GHZ_BAND;
+ break;
+ default:
+ printk(KERN_ERR PFX "Error: Unknown PHY Type %x\n",
+ phy_type);
+ return -ENODEV;
+ };
+ if (!phy_rev_ok) {
+ printk(KERN_WARNING PFX "Invalid PHY Revision %x\n",
+ phy_rev);
+ }
+
+ phy->version = phy_version;
+ phy->type = phy_type;
+ phy->rev = phy_rev;
+ if ((phy_type == BCM43xx_PHYTYPE_B) || (phy_type == BCM43xx_PHYTYPE_G)) {
+ p = kzalloc(sizeof(struct bcm43xx_lopair) * BCM43xx_LO_COUNT,
+ GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+ phy->_lo_pairs = p;
+ }
+
+ return 0;
+}
+
+static int bcm43xx_attach_board(struct bcm43xx_private *bcm)
+{
+ struct pci_dev *pci_dev = bcm->pci_dev;
+ struct net_device *net_dev = bcm->net_dev;
+ int err;
+ int i;
+ unsigned long mmio_start, mmio_flags, mmio_len;
+ u32 coremask;
+
+ err = pci_enable_device(pci_dev);
+ if (err) {
+ printk(KERN_ERR PFX "unable to wake up pci device (%i)\n", err);
+ goto out;
+ }
+ mmio_start = pci_resource_start(pci_dev, 0);
+ mmio_flags = pci_resource_flags(pci_dev, 0);
+ mmio_len = pci_resource_len(pci_dev, 0);
+ if (!(mmio_flags & IORESOURCE_MEM)) {
+ printk(KERN_ERR PFX
+ "%s, region #0 not an MMIO resource, aborting\n",
+ pci_name(pci_dev));
+ err = -ENODEV;
+ goto err_pci_disable;
+ }
+ err = pci_request_regions(pci_dev, KBUILD_MODNAME);
+ if (err) {
+ printk(KERN_ERR PFX
+ "could not access PCI resources (%i)\n", err);
+ goto err_pci_disable;
+ }
+ /* enable PCI bus-mastering */
+ pci_set_master(pci_dev);
+ bcm->mmio_addr = ioremap(mmio_start, mmio_len);
+ if (!bcm->mmio_addr) {
+ printk(KERN_ERR PFX "%s: cannot remap MMIO, aborting\n",
+ pci_name(pci_dev));
+ err = -EIO;
+ goto err_pci_release;
+ }
+ bcm->mmio_len = mmio_len;
+ net_dev->base_addr = (unsigned long)bcm->mmio_addr;
+
+ bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_VENDOR_ID,
+ &bcm->board_vendor);
+ bcm43xx_pci_read_config16(bcm, PCI_SUBSYSTEM_ID,
+ &bcm->board_type);
+ bcm43xx_pci_read_config16(bcm, PCI_REVISION_ID,
+ &bcm->board_revision);
+
+ err = bcm43xx_chipset_attach(bcm);
+ if (err)
+ goto err_iounmap;
+ err = bcm43xx_pctl_init(bcm);
+ if (err)
+ goto err_chipset_detach;
+ err = bcm43xx_probe_cores(bcm);
+ if (err)
+ goto err_chipset_detach;
+
+ /* Attach all IO cores to the backplane. */
+ coremask = 0;
+ for (i = 0; i < bcm->nr_80211_available; i++)
+ coremask |= (1 << bcm->core_80211[i].index);
+ //FIXME: Also attach some non80211 cores?
+ err = bcm43xx_setup_backplane_pci_connection(bcm, coremask);
+ if (err) {
+ printk(KERN_ERR PFX "Backplane->PCI connection failed!\n");
+ goto err_chipset_detach;
+ }
+
+ err = bcm43xx_sprom_extract(bcm);
+ if (err)
+ goto err_chipset_detach;
+ err = bcm43xx_leds_init(bcm);
+ if (err)
+ goto err_chipset_detach;
+
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ err = bcm43xx_switch_core(bcm, &bcm->core_80211[i]);
+ assert(err != -ENODEV);
+ if (err)
+ goto err_80211_unwind;
+
+ /* Enable the selected wireless core.
+ * Connect PHY only on the first core.
+ */
+ bcm43xx_wireless_core_reset(bcm, (i == 0));
+
+ err = bcm43xx_read_phyinfo(bcm);
+ if (err && (i == 0))
+ goto err_80211_unwind;
+
+ err = bcm43xx_read_radioinfo(bcm);
+ if (err && (i == 0))
+ goto err_80211_unwind;
+
+ err = bcm43xx_validate_chip(bcm);
+ if (err && (i == 0))
+ goto err_80211_unwind;
+
+ bcm43xx_radio_turn_off(bcm);
+ err = bcm43xx_phy_init_tssi2dbm_table(bcm);
+ if (err)
+ goto err_80211_unwind;
+ bcm43xx_wireless_core_disable(bcm);
+ }
+ bcm43xx_pctl_set_crystal(bcm, 0);
+
+ /* Set the MAC address in the networking subsystem */
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
+ memcpy(bcm->net_dev->dev_addr, bcm->sprom.et1macaddr, 6);
+ else
+ memcpy(bcm->net_dev->dev_addr, bcm->sprom.il0macaddr, 6);
+
+ bcm43xx_geo_init(bcm);
+
+ snprintf(bcm->nick, IW_ESSID_MAX_SIZE,
+ "Broadcom %04X", bcm->chip_id);
+
+ assert(err == 0);
+out:
+ return err;
+
+err_80211_unwind:
+ for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
+ kfree(bcm->core_80211_ext[i].phy._lo_pairs);
+ if (bcm->core_80211_ext[i].phy.dyn_tssi_tbl)
+ kfree(bcm->core_80211_ext[i].phy.tssi2dbm);
+ }
+err_chipset_detach:
+ bcm43xx_chipset_detach(bcm);
+err_iounmap:
+ iounmap(bcm->mmio_addr);
+err_pci_release:
+ pci_release_regions(pci_dev);
+err_pci_disable:
+ pci_disable_device(pci_dev);
+ goto out;
+}
+
+/* Do the Hardware IO operations to send the txb */
+static inline int bcm43xx_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb)
+{
+ int err = -ENODEV;
+
+ if (bcm43xx_using_pio(bcm))
+ err = bcm43xx_pio_tx(bcm, txb);
+ else
+ err = bcm43xx_dma_tx(bcm, txb);
+
+ return err;
+}
+
+static void bcm43xx_ieee80211_set_chan(struct net_device *net_dev,
+ u8 channel)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct bcm43xx_radioinfo *radio;
+ unsigned long flags;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (bcm->initialized) {
+ bcm43xx_mac_suspend(bcm);
+ bcm43xx_radio_selectchannel(bcm, channel, 0);
+ bcm43xx_mac_enable(bcm);
+ } else {
+ radio = bcm43xx_current_radio(bcm);
+ radio->initial_channel = channel;
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+/* set_security() callback in struct ieee80211_device */
+static void bcm43xx_ieee80211_set_security(struct net_device *net_dev,
+ struct ieee80211_security *sec)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct ieee80211_security *secinfo = &bcm->ieee->sec;
+ unsigned long flags;
+ int keyidx;
+
+ dprintk(KERN_INFO PFX "set security called\n");
+
+ bcm43xx_lock_mmio(bcm, flags);
+
+ for (keyidx = 0; keyidx<WEP_KEYS; keyidx++)
+ if (sec->flags & (1<<keyidx)) {
+ secinfo->encode_alg[keyidx] = sec->encode_alg[keyidx];
+ secinfo->key_sizes[keyidx] = sec->key_sizes[keyidx];
+ memcpy(secinfo->keys[keyidx], sec->keys[keyidx], SCM_KEY_LEN);
+ }
+
+ if (sec->flags & SEC_ACTIVE_KEY) {
+ secinfo->active_key = sec->active_key;
+ dprintk(KERN_INFO PFX " .active_key = %d\n", sec->active_key);
+ }
+ if (sec->flags & SEC_UNICAST_GROUP) {
+ secinfo->unicast_uses_group = sec->unicast_uses_group;
+ dprintk(KERN_INFO PFX " .unicast_uses_group = %d\n", sec->unicast_uses_group);
+ }
+ if (sec->flags & SEC_LEVEL) {
+ secinfo->level = sec->level;
+ dprintk(KERN_INFO PFX " .level = %d\n", sec->level);
+ }
+ if (sec->flags & SEC_ENABLED) {
+ secinfo->enabled = sec->enabled;
+ dprintk(KERN_INFO PFX " .enabled = %d\n", sec->enabled);
+ }
+ if (sec->flags & SEC_ENCRYPT) {
+ secinfo->encrypt = sec->encrypt;
+ dprintk(KERN_INFO PFX " .encrypt = %d\n", sec->encrypt);
+ }
+ if (bcm->initialized && !bcm->ieee->host_encrypt) {
+ if (secinfo->enabled) {
+ /* upload WEP keys to hardware */
+ char null_address[6] = { 0 };
+ u8 algorithm = 0;
+ for (keyidx = 0; keyidx<WEP_KEYS; keyidx++) {
+ if (!(sec->flags & (1<<keyidx)))
+ continue;
+ switch (sec->encode_alg[keyidx]) {
+ case SEC_ALG_NONE: algorithm = BCM43xx_SEC_ALGO_NONE; break;
+ case SEC_ALG_WEP:
+ algorithm = BCM43xx_SEC_ALGO_WEP;
+ if (secinfo->key_sizes[keyidx] == 13)
+ algorithm = BCM43xx_SEC_ALGO_WEP104;
+ break;
+ case SEC_ALG_TKIP:
+ FIXME();
+ algorithm = BCM43xx_SEC_ALGO_TKIP;
+ break;
+ case SEC_ALG_CCMP:
+ FIXME();
+ algorithm = BCM43xx_SEC_ALGO_AES;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ bcm43xx_key_write(bcm, keyidx, algorithm, sec->keys[keyidx], secinfo->key_sizes[keyidx], &null_address[0]);
+ bcm->key[keyidx].enabled = 1;
+ bcm->key[keyidx].algorithm = algorithm;
+ }
+ } else
+ bcm43xx_clear_keys(bcm);
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+/* hard_start_xmit() callback in struct ieee80211_device */
+static int bcm43xx_ieee80211_hard_start_xmit(struct ieee80211_txb *txb,
+ struct net_device *net_dev,
+ int pri)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err = -ENODEV;
+ unsigned long flags;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (likely(bcm->initialized))
+ err = bcm43xx_tx(bcm, txb);
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return err;
+}
+
+static struct net_device_stats * bcm43xx_net_get_stats(struct net_device *net_dev)
+{
+ return &(bcm43xx_priv(net_dev)->ieee->stats);
+}
+
+static void bcm43xx_net_tx_timeout(struct net_device *net_dev)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ bcm43xx_controller_restart(bcm, "TX timeout");
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+#ifdef CONFIG_NET_POLL_CONTROLLER
+static void bcm43xx_net_poll_controller(struct net_device *net_dev)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ local_irq_save(flags);
+ bcm43xx_interrupt_handler(bcm->irq, bcm, NULL);
+ local_irq_restore(flags);
+}
+#endif /* CONFIG_NET_POLL_CONTROLLER */
+
+static int bcm43xx_net_open(struct net_device *net_dev)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+
+ return bcm43xx_init_board(bcm);
+}
+
+static int bcm43xx_net_stop(struct net_device *net_dev)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+
+ ieee80211softmac_stop(net_dev);
+ bcm43xx_disable_interrupts_sync(bcm, NULL);
+ bcm43xx_free_board(bcm);
+
+ return 0;
+}
+
+static int bcm43xx_init_private(struct bcm43xx_private *bcm,
+ struct net_device *net_dev,
+ struct pci_dev *pci_dev)
+{
+ int err;
+
+ bcm->ieee = netdev_priv(net_dev);
+ bcm->softmac = ieee80211_priv(net_dev);
+ bcm->softmac->set_channel = bcm43xx_ieee80211_set_chan;
+
+ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
+ bcm->pci_dev = pci_dev;
+ bcm->net_dev = net_dev;
+ bcm->bad_frames_preempt = modparam_bad_frames_preempt;
+ spin_lock_init(&bcm->_lock);
+ tasklet_init(&bcm->isr_tasklet,
+ (void (*)(unsigned long))bcm43xx_interrupt_tasklet,
+ (unsigned long)bcm);
+ tasklet_disable_nosync(&bcm->isr_tasklet);
+ if (modparam_pio) {
+ bcm->__using_pio = 1;
+ } else {
+ err = pci_set_dma_mask(pci_dev, DMA_30BIT_MASK);
+ err |= pci_set_consistent_dma_mask(pci_dev, DMA_30BIT_MASK);
+ if (err) {
+#ifdef CONFIG_BCM43XX_PIO
+ printk(KERN_WARNING PFX "DMA not supported. Falling back to PIO.\n");
+ bcm->__using_pio = 1;
+#else
+ printk(KERN_ERR PFX "FATAL: DMA not supported and PIO not configured. "
+ "Recompile the driver with PIO support, please.\n");
+ return -ENODEV;
+#endif /* CONFIG_BCM43XX_PIO */
+ }
+ }
+ bcm->rts_threshold = BCM43xx_DEFAULT_RTS_THRESHOLD;
+
+ /* default to sw encryption for now */
+ bcm->ieee->host_build_iv = 0;
+ bcm->ieee->host_encrypt = 1;
+ bcm->ieee->host_decrypt = 1;
+
+ bcm->ieee->iw_mode = BCM43xx_INITIAL_IWMODE;
+ bcm->ieee->tx_headroom = sizeof(struct bcm43xx_txhdr);
+ bcm->ieee->set_security = bcm43xx_ieee80211_set_security;
+ bcm->ieee->hard_start_xmit = bcm43xx_ieee80211_hard_start_xmit;
+
+ return 0;
+}
+
+static int __devinit bcm43xx_init_one(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+{
+ struct net_device *net_dev;
+ struct bcm43xx_private *bcm;
+ int err;
+
+#ifdef CONFIG_BCM947XX
+ if ((pdev->bus->number == 0) && (pdev->device != 0x0800))
+ return -ENODEV;
+#endif
+
+#ifdef DEBUG_SINGLE_DEVICE_ONLY
+ if (strcmp(pci_name(pdev), DEBUG_SINGLE_DEVICE_ONLY))
+ return -ENODEV;
+#endif
+
+ net_dev = alloc_ieee80211softmac(sizeof(*bcm));
+ if (!net_dev) {
+ printk(KERN_ERR PFX
+ "could not allocate ieee80211 device %s\n",
+ pci_name(pdev));
+ err = -ENOMEM;
+ goto out;
+ }
+ /* initialize the net_device struct */
+ SET_MODULE_OWNER(net_dev);
+ SET_NETDEV_DEV(net_dev, &pdev->dev);
+
+ net_dev->open = bcm43xx_net_open;
+ net_dev->stop = bcm43xx_net_stop;
+ net_dev->get_stats = bcm43xx_net_get_stats;
+ net_dev->tx_timeout = bcm43xx_net_tx_timeout;
+#ifdef CONFIG_NET_POLL_CONTROLLER
+ net_dev->poll_controller = bcm43xx_net_poll_controller;
+#endif
+ net_dev->wireless_handlers = &bcm43xx_wx_handlers_def;
+ net_dev->irq = pdev->irq;
+ SET_ETHTOOL_OPS(net_dev, &bcm43xx_ethtool_ops);
+
+ /* initialize the bcm43xx_private struct */
+ bcm = bcm43xx_priv(net_dev);
+ memset(bcm, 0, sizeof(*bcm));
+ err = bcm43xx_init_private(bcm, net_dev, pdev);
+ if (err)
+ goto err_free_netdev;
+
+ pci_set_drvdata(pdev, net_dev);
+
+ err = bcm43xx_attach_board(bcm);
+ if (err)
+ goto err_free_netdev;
+
+ err = register_netdev(net_dev);
+ if (err) {
+ printk(KERN_ERR PFX "Cannot register net device, "
+ "aborting.\n");
+ err = -ENOMEM;
+ goto err_detach_board;
+ }
+
+ bcm43xx_debugfs_add_device(bcm);
+
+ assert(err == 0);
+out:
+ return err;
+
+err_detach_board:
+ bcm43xx_detach_board(bcm);
+err_free_netdev:
+ free_ieee80211softmac(net_dev);
+ goto out;
+}
+
+static void __devexit bcm43xx_remove_one(struct pci_dev *pdev)
+{
+ struct net_device *net_dev = pci_get_drvdata(pdev);
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+
+ bcm43xx_debugfs_remove_device(bcm);
+ unregister_netdev(net_dev);
+ bcm43xx_detach_board(bcm);
+ assert(bcm->ucode == NULL);
+ free_ieee80211softmac(net_dev);
+}
+
+/* Hard-reset the chip. Do not call this directly.
+ * Use bcm43xx_controller_restart()
+ */
+static void bcm43xx_chip_reset(void *_bcm)
+{
+ struct bcm43xx_private *bcm = _bcm;
+ struct net_device *net_dev = bcm->net_dev;
+ struct pci_dev *pci_dev = bcm->pci_dev;
+ int err;
+ int was_initialized = bcm->initialized;
+
+ netif_stop_queue(bcm->net_dev);
+ tasklet_disable(&bcm->isr_tasklet);
+
+ bcm->firmware_norelease = 1;
+ if (was_initialized)
+ bcm43xx_free_board(bcm);
+ bcm->firmware_norelease = 0;
+ bcm43xx_detach_board(bcm);
+ err = bcm43xx_init_private(bcm, net_dev, pci_dev);
+ if (err)
+ goto failure;
+ err = bcm43xx_attach_board(bcm);
+ if (err)
+ goto failure;
+ if (was_initialized) {
+ err = bcm43xx_init_board(bcm);
+ if (err)
+ goto failure;
+ }
+ netif_wake_queue(bcm->net_dev);
+ printk(KERN_INFO PFX "Controller restarted\n");
+
+ return;
+failure:
+ printk(KERN_ERR PFX "Controller restart failed\n");
+}
+
+/* Hard-reset the chip.
+ * This can be called from interrupt or process context.
+ * Make sure to _not_ re-enable device interrupts after this has been called.
+*/
+void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason)
+{
+ bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* dummy read */
+ printk(KERN_ERR PFX "Controller RESET (%s) ...\n", reason);
+ INIT_WORK(&bcm->restart_work, bcm43xx_chip_reset, bcm);
+ schedule_work(&bcm->restart_work);
+}
+
+#ifdef CONFIG_PM
+
+static int bcm43xx_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+ struct net_device *net_dev = pci_get_drvdata(pdev);
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int try_to_shutdown = 0, err;
+
+ dprintk(KERN_INFO PFX "Suspending...\n");
+
+ bcm43xx_lock(bcm, flags);
+ bcm->was_initialized = bcm->initialized;
+ if (bcm->initialized)
+ try_to_shutdown = 1;
+ bcm43xx_unlock(bcm, flags);
+
+ netif_device_detach(net_dev);
+ if (try_to_shutdown) {
+ ieee80211softmac_stop(net_dev);
+ err = bcm43xx_disable_interrupts_sync(bcm, &bcm->irq_savedstate);
+ if (unlikely(err)) {
+ dprintk(KERN_ERR PFX "Suspend failed.\n");
+ return -EAGAIN;
+ }
+ bcm->firmware_norelease = 1;
+ bcm43xx_free_board(bcm);
+ bcm->firmware_norelease = 0;
+ }
+ bcm43xx_chipset_detach(bcm);
+
+ pci_save_state(pdev);
+ pci_disable_device(pdev);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
+
+ dprintk(KERN_INFO PFX "Device suspended.\n");
+
+ return 0;
+}
+
+static int bcm43xx_resume(struct pci_dev *pdev)
+{
+ struct net_device *net_dev = pci_get_drvdata(pdev);
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err = 0;
+
+ dprintk(KERN_INFO PFX "Resuming...\n");
+
+ pci_set_power_state(pdev, 0);
+ pci_enable_device(pdev);
+ pci_restore_state(pdev);
+
+ bcm43xx_chipset_attach(bcm);
+ if (bcm->was_initialized) {
+ bcm->irq_savedstate = BCM43xx_IRQ_INITIAL;
+ err = bcm43xx_init_board(bcm);
+ }
+ if (err) {
+ printk(KERN_ERR PFX "Resume failed!\n");
+ return err;
+ }
+
+ netif_device_attach(net_dev);
+
+ /*FIXME: This should be handled by softmac instead. */
+ schedule_work(&bcm->softmac->associnfo.work);
+
+ dprintk(KERN_INFO PFX "Device resumed.\n");
+
+ return 0;
+}
+
+#endif /* CONFIG_PM */
+
+static struct pci_driver bcm43xx_pci_driver = {
+ .name = KBUILD_MODNAME,
+ .id_table = bcm43xx_pci_tbl,
+ .probe = bcm43xx_init_one,
+ .remove = __devexit_p(bcm43xx_remove_one),
+#ifdef CONFIG_PM
+ .suspend = bcm43xx_suspend,
+ .resume = bcm43xx_resume,
+#endif /* CONFIG_PM */
+};
+
+static int __init bcm43xx_init(void)
+{
+ printk(KERN_INFO KBUILD_MODNAME " driver\n");
+ bcm43xx_debugfs_init();
+ return pci_register_driver(&bcm43xx_pci_driver);
+}
+
+static void __exit bcm43xx_exit(void)
+{
+ pci_unregister_driver(&bcm43xx_pci_driver);
+ bcm43xx_debugfs_exit();
+}
+
+module_init(bcm43xx_init)
+module_exit(bcm43xx_exit)
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.h b/drivers/net/wireless/bcm43xx/bcm43xx_main.h
new file mode 100644
index 000000000000..eca79a38594a
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.h
@@ -0,0 +1,168 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef BCM43xx_MAIN_H_
+#define BCM43xx_MAIN_H_
+
+#include "bcm43xx.h"
+
+#ifdef CONFIG_BCM947XX
+#define atoi(str) simple_strtoul(((str != NULL) ? str : ""), NULL, 0)
+
+static inline void e_aton(char *str, char *dest)
+{
+ int i = 0;
+ u16 *d = (u16 *) dest;
+
+ for (;;) {
+ dest[i++] = (char) simple_strtoul(str, NULL, 16);
+ str += 2;
+ if (!*str++ || i == 6)
+ break;
+ }
+ for (i = 0; i < 3; i++)
+ d[i] = cpu_to_be16(d[i]);
+}
+#endif
+
+#define P4D_BYT3S(magic, nr_bytes) u8 __p4dding##magic[nr_bytes]
+#define P4D_BYTES(line, nr_bytes) P4D_BYT3S(line, nr_bytes)
+/* Magic helper macro to pad structures. Ignore those above. It's magic. */
+#define PAD_BYTES(nr_bytes) P4D_BYTES( __LINE__ , (nr_bytes))
+
+
+/* Lightweight function to convert a frequency (in Mhz) to a channel number. */
+static inline
+u8 bcm43xx_freq_to_channel_a(int freq)
+{
+ return ((freq - 5000) / 5);
+}
+static inline
+u8 bcm43xx_freq_to_channel_bg(int freq)
+{
+ u8 channel;
+
+ if (freq == 2484)
+ channel = 14;
+ else
+ channel = (freq - 2407) / 5;
+
+ return channel;
+}
+static inline
+u8 bcm43xx_freq_to_channel(struct bcm43xx_private *bcm,
+ int freq)
+{
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
+ return bcm43xx_freq_to_channel_a(freq);
+ return bcm43xx_freq_to_channel_bg(freq);
+}
+
+/* Lightweight function to convert a channel number to a frequency (in Mhz). */
+static inline
+int bcm43xx_channel_to_freq_a(u8 channel)
+{
+ return (5000 + (5 * channel));
+}
+static inline
+int bcm43xx_channel_to_freq_bg(u8 channel)
+{
+ int freq;
+
+ if (channel == 14)
+ freq = 2484;
+ else
+ freq = 2407 + (5 * channel);
+
+ return freq;
+}
+static inline
+int bcm43xx_channel_to_freq(struct bcm43xx_private *bcm,
+ u8 channel)
+{
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
+ return bcm43xx_channel_to_freq_a(channel);
+ return bcm43xx_channel_to_freq_bg(channel);
+}
+
+/* Lightweight function to check if a channel number is valid.
+ * Note that this does _NOT_ check for geographical restrictions!
+ */
+static inline
+int bcm43xx_is_valid_channel_a(u8 channel)
+{
+ return (channel <= 200);
+}
+static inline
+int bcm43xx_is_valid_channel_bg(u8 channel)
+{
+ return (channel >= 1 && channel <= 14);
+}
+static inline
+int bcm43xx_is_valid_channel(struct bcm43xx_private *bcm,
+ u8 channel)
+{
+ if (bcm43xx_current_phy(bcm)->type == BCM43xx_PHYTYPE_A)
+ return bcm43xx_is_valid_channel_a(channel);
+ return bcm43xx_is_valid_channel_bg(channel);
+}
+
+void bcm43xx_tsf_read(struct bcm43xx_private *bcm, u64 *tsf);
+void bcm43xx_tsf_write(struct bcm43xx_private *bcm, u64 tsf);
+
+void bcm43xx_set_iwmode(struct bcm43xx_private *bcm,
+ int iw_mode);
+
+u32 bcm43xx_shm_read32(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset);
+u16 bcm43xx_shm_read16(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset);
+void bcm43xx_shm_write32(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset,
+ u32 value);
+void bcm43xx_shm_write16(struct bcm43xx_private *bcm,
+ u16 routing, u16 offset,
+ u16 value);
+
+void bcm43xx_dummy_transmission(struct bcm43xx_private *bcm);
+
+int bcm43xx_switch_core(struct bcm43xx_private *bcm, struct bcm43xx_coreinfo *new_core);
+
+void bcm43xx_wireless_core_reset(struct bcm43xx_private *bcm, int connect_phy);
+
+void bcm43xx_mac_suspend(struct bcm43xx_private *bcm);
+void bcm43xx_mac_enable(struct bcm43xx_private *bcm);
+
+void bcm43xx_controller_restart(struct bcm43xx_private *bcm, const char *reason);
+
+int bcm43xx_sprom_read(struct bcm43xx_private *bcm, u16 *sprom);
+int bcm43xx_sprom_write(struct bcm43xx_private *bcm, const u16 *sprom);
+
+#endif /* BCM43xx_MAIN_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
new file mode 100644
index 000000000000..0a66f43ca0c0
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
@@ -0,0 +1,2345 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; 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/delay.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+
+#include "bcm43xx.h"
+#include "bcm43xx_phy.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_radio.h"
+#include "bcm43xx_ilt.h"
+#include "bcm43xx_power.h"
+
+
+static const s8 bcm43xx_tssi2dbm_b_table[] = {
+ 0x4D, 0x4C, 0x4B, 0x4A,
+ 0x4A, 0x49, 0x48, 0x47,
+ 0x47, 0x46, 0x45, 0x45,
+ 0x44, 0x43, 0x42, 0x42,
+ 0x41, 0x40, 0x3F, 0x3E,
+ 0x3D, 0x3C, 0x3B, 0x3A,
+ 0x39, 0x38, 0x37, 0x36,
+ 0x35, 0x34, 0x32, 0x31,
+ 0x30, 0x2F, 0x2D, 0x2C,
+ 0x2B, 0x29, 0x28, 0x26,
+ 0x25, 0x23, 0x21, 0x1F,
+ 0x1D, 0x1A, 0x17, 0x14,
+ 0x10, 0x0C, 0x06, 0x00,
+ -7, -7, -7, -7,
+ -7, -7, -7, -7,
+ -7, -7, -7, -7,
+};
+
+static const s8 bcm43xx_tssi2dbm_g_table[] = {
+ 77, 77, 77, 76,
+ 76, 76, 75, 75,
+ 74, 74, 73, 73,
+ 73, 72, 72, 71,
+ 71, 70, 70, 69,
+ 68, 68, 67, 67,
+ 66, 65, 65, 64,
+ 63, 63, 62, 61,
+ 60, 59, 58, 57,
+ 56, 55, 54, 53,
+ 52, 50, 49, 47,
+ 45, 43, 40, 37,
+ 33, 28, 22, 14,
+ 5, -7, -20, -20,
+ -20, -20, -20, -20,
+ -20, -20, -20, -20,
+};
+
+static void bcm43xx_phy_initg(struct bcm43xx_private *bcm);
+
+
+void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ assert(irqs_disabled());
+ if (bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD) == 0x00000000) {
+ phy->is_locked = 0;
+ return;
+ }
+ if (bcm->current_core->rev < 3) {
+ bcm43xx_mac_suspend(bcm);
+ spin_lock(&phy->lock);
+ } else {
+ if (bcm->ieee->iw_mode != IW_MODE_MASTER)
+ bcm43xx_power_saving_ctl_bits(bcm, -1, 1);
+ }
+ phy->is_locked = 1;
+}
+
+void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ assert(irqs_disabled());
+ if (bcm->current_core->rev < 3) {
+ if (phy->is_locked) {
+ spin_unlock(&phy->lock);
+ bcm43xx_mac_enable(bcm);
+ }
+ } else {
+ if (bcm->ieee->iw_mode != IW_MODE_MASTER)
+ bcm43xx_power_saving_ctl_bits(bcm, -1, -1);
+ }
+ phy->is_locked = 0;
+}
+
+u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset)
+{
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset);
+ return bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_DATA);
+}
+
+void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val)
+{
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_CONTROL, offset);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_DATA, val);
+}
+
+void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ unsigned long flags;
+
+ bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD); /* Dummy read. */
+ if (phy->calibrated)
+ return;
+ if (phy->type == BCM43xx_PHYTYPE_G && phy->rev == 1) {
+ /* We do not want to be preempted while calibrating
+ * the hardware.
+ */
+ local_irq_save(flags);
+
+ bcm43xx_wireless_core_reset(bcm, 0);
+ bcm43xx_phy_initg(bcm);
+ bcm43xx_wireless_core_reset(bcm, 1);
+
+ local_irq_restore(flags);
+ }
+ phy->calibrated = 1;
+}
+
+/* Connect the PHY
+ * http://bcm-specs.sipsolutions.net/SetPHY
+ */
+int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u32 flags;
+
+ if (bcm->current_core->rev < 5)
+ goto out;
+
+ flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATEHIGH);
+ if (connect) {
+ if (!(flags & 0x00010000))
+ return -ENODEV;
+ flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ flags |= (0x800 << 18);
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
+ } else {
+ if (!(flags & 0x00020000))
+ return -ENODEV;
+ flags = bcm43xx_read32(bcm, BCM43xx_CIR_SBTMSTATELOW);
+ flags &= ~(0x800 << 18);
+ bcm43xx_write32(bcm, BCM43xx_CIR_SBTMSTATELOW, flags);
+ }
+out:
+ phy->connected = connect;
+ if (connect)
+ dprintk(KERN_INFO PFX "PHY connected\n");
+ else
+ dprintk(KERN_INFO PFX "PHY disconnected\n");
+
+ return 0;
+}
+
+/* intialize B PHY power control
+ * as described in http://bcm-specs.sipsolutions.net/InitPowerControl
+ */
+static void bcm43xx_phy_init_pctl(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 saved_batt = 0, saved_ratt = 0, saved_txctl1 = 0;
+ int must_reset_txpower = 0;
+
+ assert(phy->type != BCM43xx_PHYTYPE_A);
+ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
+ (bcm->board_type == 0x0416))
+ return;
+
+ bcm43xx_write16(bcm, 0x03E6, bcm43xx_read16(bcm, 0x03E6) & 0xFFDF);
+ bcm43xx_phy_write(bcm, 0x0028, 0x8018);
+
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ if (!phy->connected)
+ return;
+ bcm43xx_phy_write(bcm, 0x047A, 0xC111);
+ }
+ if (phy->savedpctlreg != 0xFFFF)
+ return;
+
+ if (phy->type == BCM43xx_PHYTYPE_B &&
+ phy->rev >= 2 &&
+ radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0076,
+ bcm43xx_radio_read16(bcm, 0x0076) | 0x0084);
+ } else {
+ saved_batt = radio->baseband_atten;
+ saved_ratt = radio->radio_atten;
+ saved_txctl1 = radio->txctl1;
+ if ((radio->revision >= 6) && (radio->revision <= 8)
+ && /*FIXME: incomplete specs for 5 < revision < 9 */ 0)
+ bcm43xx_radio_set_txpower_bg(bcm, 0xB, 0x1F, 0);
+ else
+ bcm43xx_radio_set_txpower_bg(bcm, 0xB, 9, 0);
+ must_reset_txpower = 1;
+ }
+ bcm43xx_dummy_transmission(bcm);
+
+ phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_PCTL);
+
+ if (must_reset_txpower)
+ bcm43xx_radio_set_txpower_bg(bcm, saved_batt, saved_ratt, saved_txctl1);
+ else
+ bcm43xx_radio_write16(bcm, 0x0076, bcm43xx_radio_read16(bcm, 0x0076) & 0xFF7B);
+ bcm43xx_radio_clear_tssi(bcm);
+}
+
+static void bcm43xx_phy_agcsetup(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 offset = 0x0000;
+
+ if (phy->rev == 1)
+ offset = 0x4C00;
+
+ bcm43xx_ilt_write(bcm, offset, 0x00FE);
+ bcm43xx_ilt_write(bcm, offset + 1, 0x000D);
+ bcm43xx_ilt_write(bcm, offset + 2, 0x0013);
+ bcm43xx_ilt_write(bcm, offset + 3, 0x0019);
+
+ if (phy->rev == 1) {
+ bcm43xx_ilt_write(bcm, 0x1800, 0x2710);
+ bcm43xx_ilt_write(bcm, 0x1801, 0x9B83);
+ bcm43xx_ilt_write(bcm, 0x1802, 0x9B83);
+ bcm43xx_ilt_write(bcm, 0x1803, 0x0F8D);
+ bcm43xx_phy_write(bcm, 0x0455, 0x0004);
+ }
+
+ bcm43xx_phy_write(bcm, 0x04A5, (bcm43xx_phy_read(bcm, 0x04A5) & 0x00FF) | 0x5700);
+ bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xFF80) | 0x000F);
+ bcm43xx_phy_write(bcm, 0x041A, (bcm43xx_phy_read(bcm, 0x041A) & 0xC07F) | 0x2B80);
+ bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xF0FF) | 0x0300);
+
+ bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0008);
+
+ bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xFFF0) | 0x0008);
+ bcm43xx_phy_write(bcm, 0x04A1, (bcm43xx_phy_read(bcm, 0x04A1) & 0xF0FF) | 0x0600);
+ bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xF0FF) | 0x0700);
+ bcm43xx_phy_write(bcm, 0x04A0, (bcm43xx_phy_read(bcm, 0x04A0) & 0xF0FF) | 0x0100);
+
+ if (phy->rev == 1)
+ bcm43xx_phy_write(bcm, 0x04A2, (bcm43xx_phy_read(bcm, 0x04A2) & 0xFFF0) | 0x0007);
+
+ bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xFF00) | 0x001C);
+ bcm43xx_phy_write(bcm, 0x0488, (bcm43xx_phy_read(bcm, 0x0488) & 0xC0FF) | 0x0200);
+ bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0xFF00) | 0x001C);
+ bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xFF00) | 0x0020);
+ bcm43xx_phy_write(bcm, 0x0489, (bcm43xx_phy_read(bcm, 0x0489) & 0xC0FF) | 0x0200);
+ bcm43xx_phy_write(bcm, 0x0482, (bcm43xx_phy_read(bcm, 0x0482) & 0xFF00) | 0x002E);
+ bcm43xx_phy_write(bcm, 0x0496, (bcm43xx_phy_read(bcm, 0x0496) & 0x00FF) | 0x1A00);
+ bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0xFF00) | 0x0028);
+ bcm43xx_phy_write(bcm, 0x0481, (bcm43xx_phy_read(bcm, 0x0481) & 0x00FF) | 0x2C00);
+
+ if (phy->rev == 1) {
+ bcm43xx_phy_write(bcm, 0x0430, 0x092B);
+ bcm43xx_phy_write(bcm, 0x041B, (bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1) | 0x0002);
+ } else {
+ bcm43xx_phy_write(bcm, 0x041B, bcm43xx_phy_read(bcm, 0x041B) & 0xFFE1);
+ bcm43xx_phy_write(bcm, 0x041F, 0x287A);
+ bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0xFFF0) | 0x0004);
+ }
+
+ if (phy->rev > 2) {
+ bcm43xx_phy_write(bcm, 0x0422, 0x287A);
+ bcm43xx_phy_write(bcm, 0x0420, (bcm43xx_phy_read(bcm, 0x0420) & 0x0FFF) | 0x3000);
+ }
+
+ bcm43xx_phy_write(bcm, 0x04A8, (bcm43xx_phy_read(bcm, 0x04A8) & 0x8080) | 0x7874);
+ bcm43xx_phy_write(bcm, 0x048E, 0x1C00);
+
+ if (phy->rev == 1) {
+ bcm43xx_phy_write(bcm, 0x04AB, (bcm43xx_phy_read(bcm, 0x04AB) & 0xF0FF) | 0x0600);
+ bcm43xx_phy_write(bcm, 0x048B, 0x005E);
+ bcm43xx_phy_write(bcm, 0x048C, (bcm43xx_phy_read(bcm, 0x048C) & 0xFF00) | 0x001E);
+ bcm43xx_phy_write(bcm, 0x048D, 0x0002);
+ }
+
+ bcm43xx_ilt_write(bcm, offset + 0x0800, 0);
+ bcm43xx_ilt_write(bcm, offset + 0x0801, 7);
+ bcm43xx_ilt_write(bcm, offset + 0x0802, 16);
+ bcm43xx_ilt_write(bcm, offset + 0x0803, 28);
+}
+
+static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 i;
+
+ assert(phy->type == BCM43xx_PHYTYPE_G);
+ if (phy->rev == 1) {
+ bcm43xx_phy_write(bcm, 0x0406, 0x4F19);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0xFC3F) | 0x0340);
+ bcm43xx_phy_write(bcm, 0x042C, 0x005A);
+ bcm43xx_phy_write(bcm, 0x0427, 0x001A);
+
+ for (i = 0; i < BCM43xx_ILT_FINEFREQG_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqg[i]);
+ for (i = 0; i < BCM43xx_ILT_NOISEG1_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg1[i]);
+ for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]);
+ } else {
+ /* nrssi values are signed 6-bit values. Not sure why we write 0x7654 here... */
+ bcm43xx_nrssi_hw_write(bcm, 0xBA98, (s16)0x7654);
+
+ if (phy->rev == 2) {
+ bcm43xx_phy_write(bcm, 0x04C0, 0x1861);
+ bcm43xx_phy_write(bcm, 0x04C1, 0x0271);
+ } else if (phy->rev > 2) {
+ bcm43xx_phy_write(bcm, 0x04C0, 0x0098);
+ bcm43xx_phy_write(bcm, 0x04C1, 0x0070);
+ bcm43xx_phy_write(bcm, 0x04C9, 0x0080);
+ }
+ bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x800);
+
+ for (i = 0; i < 64; i++)
+ bcm43xx_ilt_write(bcm, 0x4000 + i, i);
+ for (i = 0; i < BCM43xx_ILT_NOISEG2_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noiseg2[i]);
+ }
+
+ if (phy->rev <= 2)
+ for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]);
+ else if ((phy->rev == 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
+ for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]);
+ else
+ for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg2[i]);
+
+ if (phy->rev == 2)
+ for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
+ else if ((phy->rev > 2) && (phy->rev <= 7))
+ for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]);
+
+ if (phy->rev == 1) {
+ for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]);
+ for (i = 0; i < 4; i++) {
+ bcm43xx_ilt_write(bcm, 0x5404 + i, 0x0020);
+ bcm43xx_ilt_write(bcm, 0x5408 + i, 0x0020);
+ bcm43xx_ilt_write(bcm, 0x540C + i, 0x0020);
+ bcm43xx_ilt_write(bcm, 0x5410 + i, 0x0020);
+ }
+ bcm43xx_phy_agcsetup(bcm);
+
+ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
+ (bcm->board_type == 0x0416) &&
+ (bcm->board_revision == 0x0017))
+ return;
+
+ bcm43xx_ilt_write(bcm, 0x5001, 0x0002);
+ bcm43xx_ilt_write(bcm, 0x5002, 0x0001);
+ } else {
+ for (i = 0; i <= 0x2F; i++)
+ bcm43xx_ilt_write(bcm, 0x1000 + i, 0x0820);
+ bcm43xx_phy_agcsetup(bcm);
+ bcm43xx_phy_read(bcm, 0x0400); /* dummy read */
+ bcm43xx_phy_write(bcm, 0x0403, 0x1000);
+ bcm43xx_ilt_write(bcm, 0x3C02, 0x000F);
+ bcm43xx_ilt_write(bcm, 0x3C03, 0x0014);
+
+ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM) &&
+ (bcm->board_type == 0x0416) &&
+ (bcm->board_revision == 0x0017))
+ return;
+
+ bcm43xx_ilt_write(bcm, 0x0401, 0x0002);
+ bcm43xx_ilt_write(bcm, 0x0402, 0x0001);
+ }
+}
+
+/* Initialize the noisescaletable for APHY */
+static void bcm43xx_phy_init_noisescaletbl(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ int i;
+
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_CTRL, 0x1400);
+ for (i = 0; i < 12; i++) {
+ if (phy->rev == 2)
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767);
+ else
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323);
+ }
+ if (phy->rev == 2)
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6700);
+ else
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2300);
+ for (i = 0; i < 11; i++) {
+ if (phy->rev == 2)
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x6767);
+ else
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x2323);
+ }
+ if (phy->rev == 2)
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0067);
+ else
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_ILT_A_DATA1, 0x0023);
+}
+
+static void bcm43xx_phy_setupa(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 i;
+
+ assert(phy->type == BCM43xx_PHYTYPE_A);
+ switch (phy->rev) {
+ case 2:
+ bcm43xx_phy_write(bcm, 0x008E, 0x3800);
+ bcm43xx_phy_write(bcm, 0x0035, 0x03FF);
+ bcm43xx_phy_write(bcm, 0x0036, 0x0400);
+
+ bcm43xx_ilt_write(bcm, 0x3807, 0x0051);
+
+ bcm43xx_phy_write(bcm, 0x001C, 0x0FF9);
+ bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F);
+ bcm43xx_ilt_write(bcm, 0x3C0C, 0x07BF);
+ bcm43xx_radio_write16(bcm, 0x0002, 0x07BF);
+
+ bcm43xx_phy_write(bcm, 0x0024, 0x4680);
+ bcm43xx_phy_write(bcm, 0x0020, 0x0003);
+ bcm43xx_phy_write(bcm, 0x001D, 0x0F40);
+ bcm43xx_phy_write(bcm, 0x001F, 0x1C00);
+
+ bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400);
+ bcm43xx_phy_write(bcm, 0x002B, bcm43xx_phy_read(bcm, 0x002B) & 0xFBFF);
+ bcm43xx_phy_write(bcm, 0x008E, 0x58C1);
+
+ bcm43xx_ilt_write(bcm, 0x0803, 0x000F);
+ bcm43xx_ilt_write(bcm, 0x0804, 0x001F);
+ bcm43xx_ilt_write(bcm, 0x0805, 0x002A);
+ bcm43xx_ilt_write(bcm, 0x0805, 0x0030);
+ bcm43xx_ilt_write(bcm, 0x0807, 0x003A);
+
+ bcm43xx_ilt_write(bcm, 0x0000, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0001, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0002, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0003, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0004, 0x0015);
+ bcm43xx_ilt_write(bcm, 0x0005, 0x0015);
+ bcm43xx_ilt_write(bcm, 0x0006, 0x0019);
+
+ bcm43xx_ilt_write(bcm, 0x0404, 0x0003);
+ bcm43xx_ilt_write(bcm, 0x0405, 0x0003);
+ bcm43xx_ilt_write(bcm, 0x0406, 0x0007);
+
+ for (i = 0; i < 16; i++)
+ bcm43xx_ilt_write(bcm, 0x4000 + i, (0x8 + i) & 0x000F);
+
+ bcm43xx_ilt_write(bcm, 0x3003, 0x1044);
+ bcm43xx_ilt_write(bcm, 0x3004, 0x7201);
+ bcm43xx_ilt_write(bcm, 0x3006, 0x0040);
+ bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008);
+
+ for (i = 0; i < BCM43xx_ILT_FINEFREQA_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x5800 + i, bcm43xx_ilt_finefreqa[i]);
+ for (i = 0; i < BCM43xx_ILT_NOISEA2_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea2[i]);
+ for (i = 0; i < BCM43xx_ILT_ROTOR_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x2000 + i, bcm43xx_ilt_rotor[i]);
+ bcm43xx_phy_init_noisescaletbl(bcm);
+ for (i = 0; i < BCM43xx_ILT_RETARD_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x2400 + i, bcm43xx_ilt_retard[i]);
+ break;
+ case 3:
+ for (i = 0; i < 64; i++)
+ bcm43xx_ilt_write(bcm, 0x4000 + i, i);
+
+ bcm43xx_ilt_write(bcm, 0x3807, 0x0051);
+
+ bcm43xx_phy_write(bcm, 0x001C, 0x0FF9);
+ bcm43xx_phy_write(bcm, 0x0020, bcm43xx_phy_read(bcm, 0x0020) & 0xFF0F);
+ bcm43xx_radio_write16(bcm, 0x0002, 0x07BF);
+
+ bcm43xx_phy_write(bcm, 0x0024, 0x4680);
+ bcm43xx_phy_write(bcm, 0x0020, 0x0003);
+ bcm43xx_phy_write(bcm, 0x001D, 0x0F40);
+ bcm43xx_phy_write(bcm, 0x001F, 0x1C00);
+ bcm43xx_phy_write(bcm, 0x002A, (bcm43xx_phy_read(bcm, 0x002A) & 0x00FF) | 0x0400);
+
+ bcm43xx_ilt_write(bcm, 0x3001, (bcm43xx_ilt_read(bcm, 0x3001) & 0x0010) | 0x0008);
+ for (i = 0; i < BCM43xx_ILT_NOISEA3_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x1800 + i, bcm43xx_ilt_noisea3[i]);
+ bcm43xx_phy_init_noisescaletbl(bcm);
+ for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
+ bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
+
+ bcm43xx_phy_write(bcm, 0x0003, 0x1808);
+
+ bcm43xx_ilt_write(bcm, 0x0803, 0x000F);
+ bcm43xx_ilt_write(bcm, 0x0804, 0x001F);
+ bcm43xx_ilt_write(bcm, 0x0805, 0x002A);
+ bcm43xx_ilt_write(bcm, 0x0805, 0x0030);
+ bcm43xx_ilt_write(bcm, 0x0807, 0x003A);
+
+ bcm43xx_ilt_write(bcm, 0x0000, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0001, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0002, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0003, 0x0013);
+ bcm43xx_ilt_write(bcm, 0x0004, 0x0015);
+ bcm43xx_ilt_write(bcm, 0x0005, 0x0015);
+ bcm43xx_ilt_write(bcm, 0x0006, 0x0019);
+
+ bcm43xx_ilt_write(bcm, 0x0404, 0x0003);
+ bcm43xx_ilt_write(bcm, 0x0405, 0x0003);
+ bcm43xx_ilt_write(bcm, 0x0406, 0x0007);
+
+ bcm43xx_ilt_write(bcm, 0x3C02, 0x000F);
+ bcm43xx_ilt_write(bcm, 0x3C03, 0x0014);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+/* Initialize APHY. This is also called for the GPHY in some cases. */
+static void bcm43xx_phy_inita(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 tval;
+
+ if (phy->type == BCM43xx_PHYTYPE_A) {
+ bcm43xx_phy_setupa(bcm);
+ } else {
+ bcm43xx_phy_setupg(bcm);
+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
+ bcm43xx_phy_write(bcm, 0x046E, 0x03CF);
+ return;
+ }
+
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS,
+ (bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) & 0xF83C) | 0x0340);
+ bcm43xx_phy_write(bcm, 0x0034, 0x0001);
+
+ TODO();//TODO: RSSI AGC
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_A_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_A_CRS) | (1 << 14));
+ bcm43xx_radio_init2060(bcm);
+
+ if ((bcm->board_vendor == PCI_VENDOR_ID_BROADCOM)
+ && ((bcm->board_type == 0x0416) || (bcm->board_type == 0x040A))) {
+ if (radio->lofcal == 0xFFFF) {
+ TODO();//TODO: LOF Cal
+ bcm43xx_radio_set_tx_iq(bcm);
+ } else
+ bcm43xx_radio_write16(bcm, 0x001E, radio->lofcal);
+ }
+
+ bcm43xx_phy_write(bcm, 0x007A, 0xF111);
+
+ if (phy->savedpctlreg == 0xFFFF) {
+ bcm43xx_radio_write16(bcm, 0x0019, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x0017, 0x0020);
+
+ tval = bcm43xx_ilt_read(bcm, 0x3001);
+ if (phy->rev == 1) {
+ bcm43xx_ilt_write(bcm, 0x3001,
+ (bcm43xx_ilt_read(bcm, 0x3001) & 0xFF87)
+ | 0x0058);
+ } else {
+ bcm43xx_ilt_write(bcm, 0x3001,
+ (bcm43xx_ilt_read(bcm, 0x3001) & 0xFFC3)
+ | 0x002C);
+ }
+ bcm43xx_dummy_transmission(bcm);
+ phy->savedpctlreg = bcm43xx_phy_read(bcm, BCM43xx_PHY_A_PCTL);
+ bcm43xx_ilt_write(bcm, 0x3001, tval);
+
+ bcm43xx_radio_set_txpower_a(bcm, 0x0018);
+ }
+ bcm43xx_radio_clear_tssi(bcm);
+}
+
+static void bcm43xx_phy_initb2(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 offset, val;
+
+ bcm43xx_write16(bcm, 0x03EC, 0x3F22);
+ bcm43xx_phy_write(bcm, 0x0020, 0x301C);
+ bcm43xx_phy_write(bcm, 0x0026, 0x0000);
+ bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
+ bcm43xx_phy_write(bcm, 0x0088, 0x3E00);
+ val = 0x3C3D;
+ for (offset = 0x0089; offset < 0x00A7; offset++) {
+ bcm43xx_phy_write(bcm, offset, val);
+ val -= 0x0202;
+ }
+ bcm43xx_phy_write(bcm, 0x03E4, 0x3000);
+ if (radio->channel == 0xFF)
+ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
+ else
+ bcm43xx_radio_selectchannel(bcm, radio->channel, 0);
+ if (radio->version != 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
+ bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
+ }
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
+ if (radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
+ bcm43xx_radio_write16(bcm, 0x007A, 0x000F);
+ bcm43xx_phy_write(bcm, 0x0038, 0x0677);
+ bcm43xx_radio_init2050(bcm);
+ }
+ bcm43xx_phy_write(bcm, 0x0014, 0x0080);
+ bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
+ bcm43xx_phy_write(bcm, 0x0032, 0x00CC);
+ bcm43xx_phy_write(bcm, 0x0035, 0x07C2);
+ bcm43xx_phy_lo_b_measure(bcm);
+ bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
+ if (radio->version != 0x2050)
+ bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1000);
+ bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
+ if (radio->version != 0x2050)
+ bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
+ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
+ bcm43xx_phy_init_pctl(bcm);
+}
+
+static void bcm43xx_phy_initb4(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 offset, val;
+
+ bcm43xx_write16(bcm, 0x03EC, 0x3F22);
+ bcm43xx_phy_write(bcm, 0x0020, 0x301C);
+ bcm43xx_phy_write(bcm, 0x0026, 0x0000);
+ bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
+ bcm43xx_phy_write(bcm, 0x0088, 0x3E00);
+ val = 0x3C3D;
+ for (offset = 0x0089; offset < 0x00A7; offset++) {
+ bcm43xx_phy_write(bcm, offset, val);
+ val -= 0x0202;
+ }
+ bcm43xx_phy_write(bcm, 0x03E4, 0x3000);
+ if (radio->channel == 0xFF)
+ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
+ else
+ bcm43xx_radio_selectchannel(bcm, radio->channel, 0);
+ if (radio->version != 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
+ bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
+ }
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
+ if (radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
+ bcm43xx_radio_write16(bcm, 0x007A, 0x000F);
+ bcm43xx_phy_write(bcm, 0x0038, 0x0677);
+ bcm43xx_radio_init2050(bcm);
+ }
+ bcm43xx_phy_write(bcm, 0x0014, 0x0080);
+ bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
+ if (radio->version == 0x2050)
+ bcm43xx_phy_write(bcm, 0x0032, 0x00E0);
+ bcm43xx_phy_write(bcm, 0x0035, 0x07C2);
+
+ bcm43xx_phy_lo_b_measure(bcm);
+
+ bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
+ if (radio->version == 0x2050)
+ bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x1100);
+ bcm43xx_phy_write(bcm, 0x002A, 0x88A3);
+ if (radio->version == 0x2050)
+ bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
+ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
+ bcm43xx_calc_nrssi_slope(bcm);
+ bcm43xx_calc_nrssi_threshold(bcm);
+ }
+ bcm43xx_phy_init_pctl(bcm);
+}
+
+static void bcm43xx_phy_initb5(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 offset;
+
+ if (phy->version == 1 &&
+ radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A)
+ | 0x0050);
+ }
+ if ((bcm->board_vendor != PCI_VENDOR_ID_BROADCOM) &&
+ (bcm->board_type != 0x0416)) {
+ for (offset = 0x00A8 ; offset < 0x00C7; offset++) {
+ bcm43xx_phy_write(bcm, offset,
+ (bcm43xx_phy_read(bcm, offset) + 0x2020)
+ & 0x3F3F);
+ }
+ }
+ bcm43xx_phy_write(bcm, 0x0035,
+ (bcm43xx_phy_read(bcm, 0x0035) & 0xF0FF)
+ | 0x0700);
+ if (radio->version == 0x2050)
+ bcm43xx_phy_write(bcm, 0x0038, 0x0667);
+
+ if (phy->connected) {
+ if (radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A)
+ | 0x0020);
+ bcm43xx_radio_write16(bcm, 0x0051,
+ bcm43xx_radio_read16(bcm, 0x0051)
+ | 0x0004);
+ }
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO, 0x0000);
+
+ bcm43xx_phy_write(bcm, 0x0802, bcm43xx_phy_read(bcm, 0x0802) | 0x0100);
+ bcm43xx_phy_write(bcm, 0x042B, bcm43xx_phy_read(bcm, 0x042B) | 0x2000);
+
+ bcm43xx_phy_write(bcm, 0x001C, 0x186A);
+
+ bcm43xx_phy_write(bcm, 0x0013, (bcm43xx_phy_read(bcm, 0x0013) & 0x00FF) | 0x1900);
+ bcm43xx_phy_write(bcm, 0x0035, (bcm43xx_phy_read(bcm, 0x0035) & 0xFFC0) | 0x0064);
+ bcm43xx_phy_write(bcm, 0x005D, (bcm43xx_phy_read(bcm, 0x005D) & 0xFF80) | 0x000A);
+ }
+
+ if (bcm->bad_frames_preempt) {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | (1 << 11));
+ }
+
+ if (phy->version == 1 && radio->version == 0x2050) {
+ bcm43xx_phy_write(bcm, 0x0026, 0xCE00);
+ bcm43xx_phy_write(bcm, 0x0021, 0x3763);
+ bcm43xx_phy_write(bcm, 0x0022, 0x1BC3);
+ bcm43xx_phy_write(bcm, 0x0023, 0x06F9);
+ bcm43xx_phy_write(bcm, 0x0024, 0x037E);
+ } else
+ bcm43xx_phy_write(bcm, 0x0026, 0xCC00);
+ bcm43xx_phy_write(bcm, 0x0030, 0x00C6);
+ bcm43xx_write16(bcm, 0x03EC, 0x3F22);
+
+ if (phy->version == 1 && radio->version == 0x2050)
+ bcm43xx_phy_write(bcm, 0x0020, 0x3E1C);
+ else
+ bcm43xx_phy_write(bcm, 0x0020, 0x301C);
+
+ if (phy->version == 0)
+ bcm43xx_write16(bcm, 0x03E4, 0x3000);
+
+ /* Force to channel 7, even if not supported. */
+ bcm43xx_radio_selectchannel(bcm, 7, 0);
+
+ if (radio->version != 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0075, 0x0080);
+ bcm43xx_radio_write16(bcm, 0x0079, 0x0081);
+ }
+
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
+
+ if (radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
+ }
+
+ bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
+
+ bcm43xx_radio_write16(bcm, 0x007A, bcm43xx_radio_read16(bcm, 0x007A) | 0x0007);
+
+ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
+
+ bcm43xx_phy_write(bcm, 0x0014, 0x0080);
+ bcm43xx_phy_write(bcm, 0x0032, 0x00CA);
+ bcm43xx_phy_write(bcm, 0x88A3, 0x002A);
+
+ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
+
+ if (radio->version == 0x2050)
+ bcm43xx_radio_write16(bcm, 0x005D, 0x000D);
+
+ bcm43xx_write16(bcm, 0x03E4, (bcm43xx_read16(bcm, 0x03E4) & 0xFFC0) | 0x0004);
+}
+
+static void bcm43xx_phy_initb6(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 offset, val;
+
+ bcm43xx_phy_write(bcm, 0x003E, 0x817A);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ (bcm43xx_radio_read16(bcm, 0x007A) | 0x0058));
+ if ((radio->manufact == 0x17F) &&
+ (radio->version == 0x2050) &&
+ (radio->revision == 3 ||
+ radio->revision == 4 ||
+ radio->revision == 5)) {
+ bcm43xx_radio_write16(bcm, 0x0051, 0x001F);
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0040);
+ bcm43xx_radio_write16(bcm, 0x0053, 0x005B);
+ bcm43xx_radio_write16(bcm, 0x0054, 0x0098);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005D, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005E, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x007D, 0x0088);
+ }
+ if ((radio->manufact == 0x17F) &&
+ (radio->version == 0x2050) &&
+ (radio->revision == 6)) {
+ bcm43xx_radio_write16(bcm, 0x0051, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0040);
+ bcm43xx_radio_write16(bcm, 0x0053, 0x00B7);
+ bcm43xx_radio_write16(bcm, 0x0054, 0x0098);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x008B);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x00B5);
+ bcm43xx_radio_write16(bcm, 0x005D, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005E, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x007D, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x007C, 0x0001);
+ bcm43xx_radio_write16(bcm, 0x007E, 0x0008);
+ }
+ if ((radio->manufact == 0x17F) &&
+ (radio->version == 0x2050) &&
+ (radio->revision == 7)) {
+ bcm43xx_radio_write16(bcm, 0x0051, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0040);
+ bcm43xx_radio_write16(bcm, 0x0053, 0x00B7);
+ bcm43xx_radio_write16(bcm, 0x0054, 0x0098);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x00A8);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x0075);
+ bcm43xx_radio_write16(bcm, 0x005D, 0x00F5);
+ bcm43xx_radio_write16(bcm, 0x005E, 0x00B8);
+ bcm43xx_radio_write16(bcm, 0x007D, 0x00E8);
+ bcm43xx_radio_write16(bcm, 0x007C, 0x0001);
+ bcm43xx_radio_write16(bcm, 0x007E, 0x0008);
+ bcm43xx_radio_write16(bcm, 0x007B, 0x0000);
+ }
+ if ((radio->manufact == 0x17F) &&
+ (radio->version == 0x2050) &&
+ (radio->revision == 8)) {
+ bcm43xx_radio_write16(bcm, 0x0051, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0040);
+ bcm43xx_radio_write16(bcm, 0x0053, 0x00B7);
+ bcm43xx_radio_write16(bcm, 0x0054, 0x0098);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0088);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x006B);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x000F);
+ if (bcm->sprom.boardflags & 0x8000) {
+ bcm43xx_radio_write16(bcm, 0x005D, 0x00FA);
+ bcm43xx_radio_write16(bcm, 0x005E, 0x00D8);
+ } else {
+ bcm43xx_radio_write16(bcm, 0x005D, 0x00F5);
+ bcm43xx_radio_write16(bcm, 0x005E, 0x00B8);
+ }
+ bcm43xx_radio_write16(bcm, 0x0073, 0x0003);
+ bcm43xx_radio_write16(bcm, 0x007D, 0x00A8);
+ bcm43xx_radio_write16(bcm, 0x007C, 0x0001);
+ bcm43xx_radio_write16(bcm, 0x007E, 0x0008);
+ }
+ val = 0x1E1F;
+ for (offset = 0x0088; offset < 0x0098; offset++) {
+ bcm43xx_phy_write(bcm, offset, val);
+ val -= 0x0202;
+ }
+ val = 0x3E3F;
+ for (offset = 0x0098; offset < 0x00A8; offset++) {
+ bcm43xx_phy_write(bcm, offset, val);
+ val -= 0x0202;
+ }
+ val = 0x2120;
+ for (offset = 0x00A8; offset < 0x00C8; offset++) {
+ bcm43xx_phy_write(bcm, offset, (val & 0x3F3F));
+ val += 0x0202;
+ }
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x0020);
+ bcm43xx_radio_write16(bcm, 0x0051,
+ bcm43xx_radio_read16(bcm, 0x0051) | 0x0004);
+ bcm43xx_phy_write(bcm, 0x0802,
+ bcm43xx_phy_read(bcm, 0x0802) | 0x0100);
+ bcm43xx_phy_write(bcm, 0x042B,
+ bcm43xx_phy_read(bcm, 0x042B) | 0x2000);
+ }
+
+ /* Force to channel 7, even if not supported. */
+ bcm43xx_radio_selectchannel(bcm, 7, 0);
+
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0023);
+ udelay(40);
+ bcm43xx_radio_write16(bcm, 0x007C, (bcm43xx_radio_read16(bcm, 0x007C) | 0x0002));
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ if (radio->manufact == 0x17F &&
+ radio->version == 0x2050 &&
+ radio->revision <= 2) {
+ bcm43xx_radio_write16(bcm, 0x0050, 0x0020);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0070);
+ bcm43xx_radio_write16(bcm, 0x005B, 0x007B);
+ bcm43xx_radio_write16(bcm, 0x005C, 0x00B0);
+ }
+ bcm43xx_radio_write16(bcm, 0x007A,
+ (bcm43xx_radio_read16(bcm, 0x007A) & 0x00F8) | 0x0007);
+
+ bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 0);
+
+ bcm43xx_phy_write(bcm, 0x0014, 0x0200);
+ if (radio->version == 0x2050){
+ if (radio->revision == 3 ||
+ radio->revision == 4 ||
+ radio->revision == 5)
+ bcm43xx_phy_write(bcm, 0x002A, 0x8AC0);
+ else
+ bcm43xx_phy_write(bcm, 0x002A, 0x88C2);
+ }
+ bcm43xx_phy_write(bcm, 0x0038, 0x0668);
+ bcm43xx_radio_set_txpower_bg(bcm, 0xFFFF, 0xFFFF, 0xFFFF);
+ if (radio->version == 0x2050) {
+ if (radio->revision == 3 ||
+ radio->revision == 4 ||
+ radio->revision == 5)
+ bcm43xx_phy_write(bcm, 0x005D, bcm43xx_phy_read(bcm, 0x005D) | 0x0003);
+ else if (radio->revision <= 2)
+ bcm43xx_radio_write16(bcm, 0x005D, 0x000D);
+ }
+
+ if (phy->rev == 4)
+ bcm43xx_phy_write(bcm, 0x0002, (bcm43xx_phy_read(bcm, 0x0002) & 0xFFC0) | 0x0004);
+ else
+ bcm43xx_write16(bcm, 0x03E4, 0x0009);
+ if (phy->type == BCM43xx_PHYTYPE_B) {
+ bcm43xx_write16(bcm, 0x03E6, 0x8140);
+ bcm43xx_phy_write(bcm, 0x0016, 0x0410);
+ bcm43xx_phy_write(bcm, 0x0017, 0x0820);
+ bcm43xx_phy_write(bcm, 0x0062, 0x0007);
+ (void) bcm43xx_radio_calibrationvalue(bcm);
+ bcm43xx_phy_lo_b_measure(bcm);
+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
+ bcm43xx_calc_nrssi_slope(bcm);
+ bcm43xx_calc_nrssi_threshold(bcm);
+ }
+ bcm43xx_phy_init_pctl(bcm);
+ } else
+ bcm43xx_write16(bcm, 0x03E6, 0x0);
+}
+
+static void bcm43xx_calc_loopback_gain(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 backup_phy[15];
+ u16 backup_radio[3];
+ u16 backup_bband;
+ u16 i;
+ u16 loop1_cnt, loop1_done, loop1_omitted;
+ u16 loop2_done;
+
+ backup_phy[0] = bcm43xx_phy_read(bcm, 0x0429);
+ backup_phy[1] = bcm43xx_phy_read(bcm, 0x0001);
+ backup_phy[2] = bcm43xx_phy_read(bcm, 0x0811);
+ backup_phy[3] = bcm43xx_phy_read(bcm, 0x0812);
+ backup_phy[4] = bcm43xx_phy_read(bcm, 0x0814);
+ backup_phy[5] = bcm43xx_phy_read(bcm, 0x0815);
+ backup_phy[6] = bcm43xx_phy_read(bcm, 0x005A);
+ backup_phy[7] = bcm43xx_phy_read(bcm, 0x0059);
+ backup_phy[8] = bcm43xx_phy_read(bcm, 0x0058);
+ backup_phy[9] = bcm43xx_phy_read(bcm, 0x000A);
+ backup_phy[10] = bcm43xx_phy_read(bcm, 0x0003);
+ backup_phy[11] = bcm43xx_phy_read(bcm, 0x080F);
+ backup_phy[12] = bcm43xx_phy_read(bcm, 0x0810);
+ backup_phy[13] = bcm43xx_phy_read(bcm, 0x002B);
+ backup_phy[14] = bcm43xx_phy_read(bcm, 0x0015);
+ bcm43xx_phy_read(bcm, 0x002D); /* dummy read */
+ backup_bband = radio->baseband_atten;
+ backup_radio[0] = bcm43xx_radio_read16(bcm, 0x0052);
+ backup_radio[1] = bcm43xx_radio_read16(bcm, 0x0043);
+ backup_radio[2] = bcm43xx_radio_read16(bcm, 0x007A);
+
+ bcm43xx_phy_write(bcm, 0x0429,
+ bcm43xx_phy_read(bcm, 0x0429) & 0x3FFF);
+ bcm43xx_phy_write(bcm, 0x0001,
+ bcm43xx_phy_read(bcm, 0x0001) & 0x8000);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x0002);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) & 0xFFFD);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x0001);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) & 0xFFFE);
+ bcm43xx_phy_write(bcm, 0x0814,
+ bcm43xx_phy_read(bcm, 0x0814) | 0x0001);
+ bcm43xx_phy_write(bcm, 0x0815,
+ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE);
+ bcm43xx_phy_write(bcm, 0x0814,
+ bcm43xx_phy_read(bcm, 0x0814) | 0x0002);
+ bcm43xx_phy_write(bcm, 0x0815,
+ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFD);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) | 0x000C);
+
+ bcm43xx_phy_write(bcm, 0x0811,
+ (bcm43xx_phy_read(bcm, 0x0811)
+ & 0xFFCF) | 0x0030);
+ bcm43xx_phy_write(bcm, 0x0812,
+ (bcm43xx_phy_read(bcm, 0x0812)
+ & 0xFFCF) | 0x0010);
+
+ bcm43xx_phy_write(bcm, 0x005A, 0x0780);
+ bcm43xx_phy_write(bcm, 0x0059, 0xC810);
+ bcm43xx_phy_write(bcm, 0x0058, 0x000D);
+ if (phy->version == 0) {
+ bcm43xx_phy_write(bcm, 0x0003, 0x0122);
+ } else {
+ bcm43xx_phy_write(bcm, 0x000A,
+ bcm43xx_phy_read(bcm, 0x000A)
+ | 0x2000);
+ }
+ bcm43xx_phy_write(bcm, 0x0814,
+ bcm43xx_phy_read(bcm, 0x0814) | 0x0004);
+ bcm43xx_phy_write(bcm, 0x0815,
+ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB);
+ bcm43xx_phy_write(bcm, 0x0003,
+ (bcm43xx_phy_read(bcm, 0x0003)
+ & 0xFF9F) | 0x0040);
+ if (radio->version == 0x2050 && radio->revision == 2) {
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x0043,
+ (bcm43xx_radio_read16(bcm, 0x0043)
+ & 0xFFF0) | 0x0009);
+ loop1_cnt = 9;
+ } else if (radio->revision == 8) {
+ bcm43xx_radio_write16(bcm, 0x0043, 0x000F);
+ loop1_cnt = 15;
+ } else
+ loop1_cnt = 0;
+
+ bcm43xx_phy_set_baseband_attenuation(bcm, 11);
+
+ if (phy->rev >= 3)
+ bcm43xx_phy_write(bcm, 0x080F, 0xC020);
+ else
+ bcm43xx_phy_write(bcm, 0x080F, 0x8020);
+ bcm43xx_phy_write(bcm, 0x0810, 0x0000);
+
+ bcm43xx_phy_write(bcm, 0x002B,
+ (bcm43xx_phy_read(bcm, 0x002B)
+ & 0xFFC0) | 0x0001);
+ bcm43xx_phy_write(bcm, 0x002B,
+ (bcm43xx_phy_read(bcm, 0x002B)
+ & 0xC0FF) | 0x0800);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x0100);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) & 0xCFFF);
+ if (bcm->sprom.boardflags & BCM43xx_BFL_EXTLNA) {
+ if (phy->rev >= 7) {
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811)
+ | 0x0800);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812)
+ | 0x8000);
+ }
+ }
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A)
+ & 0x00F7);
+
+ for (i = 0; i < loop1_cnt; i++) {
+ bcm43xx_radio_write16(bcm, 0x0043, loop1_cnt);
+ bcm43xx_phy_write(bcm, 0x0812,
+ (bcm43xx_phy_read(bcm, 0x0812)
+ & 0xF0FF) | (i << 8));
+ bcm43xx_phy_write(bcm, 0x0015,
+ (bcm43xx_phy_read(bcm, 0x0015)
+ & 0x0FFF) | 0xA000);
+ bcm43xx_phy_write(bcm, 0x0015,
+ (bcm43xx_phy_read(bcm, 0x0015)
+ & 0x0FFF) | 0xF000);
+ udelay(20);
+ if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC)
+ break;
+ }
+ loop1_done = i;
+ loop1_omitted = loop1_cnt - loop1_done;
+
+ loop2_done = 0;
+ if (loop1_done >= 8) {
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812)
+ | 0x0030);
+ for (i = loop1_done - 8; i < 16; i++) {
+ bcm43xx_phy_write(bcm, 0x0812,
+ (bcm43xx_phy_read(bcm, 0x0812)
+ & 0xF0FF) | (i << 8));
+ bcm43xx_phy_write(bcm, 0x0015,
+ (bcm43xx_phy_read(bcm, 0x0015)
+ & 0x0FFF) | 0xA000);
+ bcm43xx_phy_write(bcm, 0x0015,
+ (bcm43xx_phy_read(bcm, 0x0015)
+ & 0x0FFF) | 0xF000);
+ udelay(20);
+ if (bcm43xx_phy_read(bcm, 0x002D) >= 0x0DFC)
+ break;
+ }
+ }
+
+ bcm43xx_phy_write(bcm, 0x0814, backup_phy[4]);
+ bcm43xx_phy_write(bcm, 0x0815, backup_phy[5]);
+ bcm43xx_phy_write(bcm, 0x005A, backup_phy[6]);
+ bcm43xx_phy_write(bcm, 0x0059, backup_phy[7]);
+ bcm43xx_phy_write(bcm, 0x0058, backup_phy[8]);
+ bcm43xx_phy_write(bcm, 0x000A, backup_phy[9]);
+ bcm43xx_phy_write(bcm, 0x0003, backup_phy[10]);
+ bcm43xx_phy_write(bcm, 0x080F, backup_phy[11]);
+ bcm43xx_phy_write(bcm, 0x0810, backup_phy[12]);
+ bcm43xx_phy_write(bcm, 0x002B, backup_phy[13]);
+ bcm43xx_phy_write(bcm, 0x0015, backup_phy[14]);
+
+ bcm43xx_phy_set_baseband_attenuation(bcm, backup_bband);
+
+ bcm43xx_radio_write16(bcm, 0x0052, backup_radio[0]);
+ bcm43xx_radio_write16(bcm, 0x0043, backup_radio[1]);
+ bcm43xx_radio_write16(bcm, 0x007A, backup_radio[2]);
+
+ bcm43xx_phy_write(bcm, 0x0811, backup_phy[2] | 0x0003);
+ udelay(10);
+ bcm43xx_phy_write(bcm, 0x0811, backup_phy[2]);
+ bcm43xx_phy_write(bcm, 0x0812, backup_phy[3]);
+ bcm43xx_phy_write(bcm, 0x0429, backup_phy[0]);
+ bcm43xx_phy_write(bcm, 0x0001, backup_phy[1]);
+
+ phy->loopback_gain[0] = ((loop1_done * 6) - (loop1_omitted * 4)) - 11;
+ phy->loopback_gain[1] = (24 - (3 * loop2_done)) * 2;
+}
+
+static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 tmp;
+
+ if (phy->rev == 1)
+ bcm43xx_phy_initb5(bcm);
+ else if (phy->rev >= 2 && phy->rev <= 7)
+ bcm43xx_phy_initb6(bcm);
+ if (phy->rev >= 2 || phy->connected)
+ bcm43xx_phy_inita(bcm);
+
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x0814, 0x0000);
+ bcm43xx_phy_write(bcm, 0x0815, 0x0000);
+ if (phy->rev == 2)
+ bcm43xx_phy_write(bcm, 0x0811, 0x0000);
+ else if (phy->rev >= 3)
+ bcm43xx_phy_write(bcm, 0x0811, 0x0400);
+ bcm43xx_phy_write(bcm, 0x0015, 0x00C0);
+ if (phy->connected) {
+ tmp = bcm43xx_phy_read(bcm, 0x0400) & 0xFF;
+ if (tmp < 6) {
+ bcm43xx_phy_write(bcm, 0x04C2, 0x1816);
+ bcm43xx_phy_write(bcm, 0x04C3, 0x8006);
+ if (tmp != 3) {
+ bcm43xx_phy_write(bcm, 0x04CC,
+ (bcm43xx_phy_read(bcm, 0x04CC)
+ & 0x00FF) | 0x1F00);
+ }
+ }
+ }
+ }
+ if (phy->rev < 3 && phy->connected)
+ bcm43xx_phy_write(bcm, 0x047E, 0x0078);
+ if (phy->rev >= 6 && phy->rev <= 8) {
+ bcm43xx_phy_write(bcm, 0x0801, bcm43xx_phy_read(bcm, 0x0801) | 0x0080);
+ bcm43xx_phy_write(bcm, 0x043E, bcm43xx_phy_read(bcm, 0x043E) | 0x0004);
+ }
+ if (phy->rev >= 2 && phy->connected)
+ bcm43xx_calc_loopback_gain(bcm);
+ if (radio->revision != 8) {
+ if (radio->initval == 0xFFFF)
+ radio->initval = bcm43xx_radio_init2050(bcm);
+ else
+ bcm43xx_radio_write16(bcm, 0x0078, radio->initval);
+ }
+ if (radio->txctl2 == 0xFFFF) {
+ bcm43xx_phy_lo_g_measure(bcm);
+ } else {
+ if (radio->version == 0x2050 && radio->revision == 8) {
+ //FIXME
+ } else {
+ bcm43xx_radio_write16(bcm, 0x0052,
+ (bcm43xx_radio_read16(bcm, 0x0052)
+ & 0xFFF0) | radio->txctl1);
+ }
+ if (phy->rev >= 6) {
+ /*
+ bcm43xx_phy_write(bcm, 0x0036,
+ (bcm43xx_phy_read(bcm, 0x0036)
+ & 0xF000) | (FIXME << 12));
+ */
+ }
+ if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
+ bcm43xx_phy_write(bcm, 0x002E, 0x8075);
+ else
+ bcm43xx_phy_write(bcm, 0x003E, 0x807F);
+ if (phy->rev < 2)
+ bcm43xx_phy_write(bcm, 0x002F, 0x0101);
+ else
+ bcm43xx_phy_write(bcm, 0x002F, 0x0202);
+ }
+ if (phy->connected) {
+ bcm43xx_phy_lo_adjust(bcm, 0);
+ bcm43xx_phy_write(bcm, 0x080F, 0x8078);
+ }
+
+ if (!(bcm->sprom.boardflags & BCM43xx_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.
+ * Essentially, what we do here is resetting all NRSSI LT
+ * entries to -32 (see the limit_value() in nrssi_hw_update())
+ */
+ bcm43xx_nrssi_hw_update(bcm, 0xFFFF);
+ bcm43xx_calc_nrssi_threshold(bcm);
+ } else if (phy->connected) {
+ if (radio->nrssi[0] == -1000) {
+ assert(radio->nrssi[1] == -1000);
+ bcm43xx_calc_nrssi_slope(bcm);
+ } else {
+ assert(radio->nrssi[1] != -1000);
+ bcm43xx_calc_nrssi_threshold(bcm);
+ }
+ }
+ if (radio->revision == 8)
+ bcm43xx_phy_write(bcm, 0x0805, 0x3230);
+ bcm43xx_phy_init_pctl(bcm);
+ if (bcm->chip_id == 0x4306 && bcm->chip_package != 2) {
+ bcm43xx_phy_write(bcm, 0x0429,
+ bcm43xx_phy_read(bcm, 0x0429) & 0xBFFF);
+ bcm43xx_phy_write(bcm, 0x04C3,
+ bcm43xx_phy_read(bcm, 0x04C3) & 0x7FFF);
+ }
+}
+
+static u16 bcm43xx_phy_lo_b_r15_loop(struct bcm43xx_private *bcm)
+{
+ int i;
+ u16 ret = 0;
+
+ for (i = 0; i < 10; i++){
+ bcm43xx_phy_write(bcm, 0x0015, 0xAFA0);
+ udelay(1);
+ bcm43xx_phy_write(bcm, 0x0015, 0xEFA0);
+ udelay(10);
+ bcm43xx_phy_write(bcm, 0x0015, 0xFFA0);
+ udelay(40);
+ ret += bcm43xx_phy_read(bcm, 0x002C);
+ }
+
+ return ret;
+}
+
+void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 regstack[12] = { 0 };
+ u16 mls;
+ u16 fval;
+ int i, j;
+
+ regstack[0] = bcm43xx_phy_read(bcm, 0x0015);
+ regstack[1] = bcm43xx_radio_read16(bcm, 0x0052) & 0xFFF0;
+
+ if (radio->version == 0x2053) {
+ regstack[2] = bcm43xx_phy_read(bcm, 0x000A);
+ regstack[3] = bcm43xx_phy_read(bcm, 0x002A);
+ regstack[4] = bcm43xx_phy_read(bcm, 0x0035);
+ regstack[5] = bcm43xx_phy_read(bcm, 0x0003);
+ regstack[6] = bcm43xx_phy_read(bcm, 0x0001);
+ regstack[7] = bcm43xx_phy_read(bcm, 0x0030);
+
+ regstack[8] = bcm43xx_radio_read16(bcm, 0x0043);
+ regstack[9] = bcm43xx_radio_read16(bcm, 0x007A);
+ regstack[10] = bcm43xx_read16(bcm, 0x03EC);
+ regstack[11] = bcm43xx_radio_read16(bcm, 0x0052) & 0x00F0;
+
+ bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
+ bcm43xx_write16(bcm, 0x03EC, 0x3F3F);
+ bcm43xx_phy_write(bcm, 0x0035, regstack[4] & 0xFF7F);
+ bcm43xx_radio_write16(bcm, 0x007A, regstack[9] & 0xFFF0);
+ }
+ bcm43xx_phy_write(bcm, 0x0015, 0xB000);
+ bcm43xx_phy_write(bcm, 0x002B, 0x0004);
+
+ if (radio->version == 0x2053) {
+ bcm43xx_phy_write(bcm, 0x002B, 0x0203);
+ bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
+ }
+
+ phy->minlowsig[0] = 0xFFFF;
+
+ for (i = 0; i < 4; i++) {
+ bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i);
+ bcm43xx_phy_lo_b_r15_loop(bcm);
+ }
+ for (i = 0; i < 10; i++) {
+ bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | i);
+ mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10;
+ if (mls < phy->minlowsig[0]) {
+ phy->minlowsig[0] = mls;
+ phy->minlowsigpos[0] = i;
+ }
+ }
+ bcm43xx_radio_write16(bcm, 0x0052, regstack[1] | phy->minlowsigpos[0]);
+
+ phy->minlowsig[1] = 0xFFFF;
+
+ for (i = -4; i < 5; i += 2) {
+ for (j = -4; j < 5; j += 2) {
+ if (j < 0)
+ fval = (0x0100 * i) + j + 0x0100;
+ else
+ fval = (0x0100 * i) + j;
+ bcm43xx_phy_write(bcm, 0x002F, fval);
+ mls = bcm43xx_phy_lo_b_r15_loop(bcm) / 10;
+ if (mls < phy->minlowsig[1]) {
+ phy->minlowsig[1] = mls;
+ phy->minlowsigpos[1] = fval;
+ }
+ }
+ }
+ phy->minlowsigpos[1] += 0x0101;
+
+ bcm43xx_phy_write(bcm, 0x002F, phy->minlowsigpos[1]);
+ if (radio->version == 0x2053) {
+ bcm43xx_phy_write(bcm, 0x000A, regstack[2]);
+ bcm43xx_phy_write(bcm, 0x002A, regstack[3]);
+ bcm43xx_phy_write(bcm, 0x0035, regstack[4]);
+ bcm43xx_phy_write(bcm, 0x0003, regstack[5]);
+ bcm43xx_phy_write(bcm, 0x0001, regstack[6]);
+ bcm43xx_phy_write(bcm, 0x0030, regstack[7]);
+
+ bcm43xx_radio_write16(bcm, 0x0043, regstack[8]);
+ bcm43xx_radio_write16(bcm, 0x007A, regstack[9]);
+
+ bcm43xx_radio_write16(bcm, 0x0052,
+ (bcm43xx_radio_read16(bcm, 0x0052) & 0x000F)
+ | regstack[11]);
+
+ bcm43xx_write16(bcm, 0x03EC, regstack[10]);
+ }
+ bcm43xx_phy_write(bcm, 0x0015, regstack[0]);
+}
+
+static inline
+u16 bcm43xx_phy_lo_g_deviation_subval(struct bcm43xx_private *bcm, u16 control)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ if (phy->connected) {
+ bcm43xx_phy_write(bcm, 0x15, 0xE300);
+ control <<= 8;
+ bcm43xx_phy_write(bcm, 0x0812, control | 0x00B0);
+ udelay(5);
+ bcm43xx_phy_write(bcm, 0x0812, control | 0x00B2);
+ udelay(2);
+ bcm43xx_phy_write(bcm, 0x0812, control | 0x00B3);
+ udelay(4);
+ bcm43xx_phy_write(bcm, 0x0015, 0xF300);
+ udelay(8);
+ } else {
+ bcm43xx_phy_write(bcm, 0x0015, control | 0xEFA0);
+ udelay(2);
+ bcm43xx_phy_write(bcm, 0x0015, control | 0xEFE0);
+ udelay(4);
+ bcm43xx_phy_write(bcm, 0x0015, control | 0xFFE0);
+ udelay(8);
+ }
+
+ return bcm43xx_phy_read(bcm, 0x002D);
+}
+
+static u32 bcm43xx_phy_lo_g_singledeviation(struct bcm43xx_private *bcm, u16 control)
+{
+ int i;
+ u32 ret = 0;
+
+ for (i = 0; i < 8; i++)
+ ret += bcm43xx_phy_lo_g_deviation_subval(bcm, control);
+
+ return ret;
+}
+
+/* Write the LocalOscillator CONTROL */
+static inline
+void bcm43xx_lo_write(struct bcm43xx_private *bcm,
+ struct bcm43xx_lopair *pair)
+{
+ u16 value;
+
+ value = (u8)(pair->low);
+ value |= ((u8)(pair->high)) << 8;
+
+#ifdef CONFIG_BCM43XX_DEBUG
+ /* Sanity check. */
+ if (pair->low < -8 || pair->low > 8 ||
+ pair->high < -8 || pair->high > 8) {
+ printk(KERN_WARNING PFX
+ "WARNING: Writing invalid LOpair "
+ "(low: %d, high: %d, index: %lu)\n",
+ pair->low, pair->high,
+ (unsigned long)(pair - bcm43xx_current_phy(bcm)->_lo_pairs));
+ dump_stack();
+ }
+#endif
+
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, value);
+}
+
+static inline
+struct bcm43xx_lopair * bcm43xx_find_lopair(struct bcm43xx_private *bcm,
+ u16 baseband_attenuation,
+ u16 radio_attenuation,
+ u16 tx)
+{
+ static const u8 dict[10] = { 11, 10, 11, 12, 13, 12, 13, 12, 13, 12 };
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ if (baseband_attenuation > 6)
+ baseband_attenuation = 6;
+ assert(radio_attenuation < 10);
+
+ if (tx == 3) {
+ return bcm43xx_get_lopair(phy,
+ radio_attenuation,
+ baseband_attenuation);
+ }
+ return bcm43xx_get_lopair(phy, dict[radio_attenuation], baseband_attenuation);
+}
+
+static inline
+struct bcm43xx_lopair * bcm43xx_current_lopair(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ return bcm43xx_find_lopair(bcm,
+ radio->baseband_atten,
+ radio->radio_atten,
+ radio->txctl1);
+}
+
+/* Adjust B/G LO */
+void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed)
+{
+ struct bcm43xx_lopair *pair;
+
+ if (fixed) {
+ /* Use fixed values. Only for initialization. */
+ pair = bcm43xx_find_lopair(bcm, 2, 3, 0);
+ } else
+ pair = bcm43xx_current_lopair(bcm);
+ bcm43xx_lo_write(bcm, pair);
+}
+
+static void bcm43xx_phy_lo_g_measure_txctl2(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 txctl2 = 0, i;
+ u32 smallest, tmp;
+
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
+ udelay(10);
+ smallest = bcm43xx_phy_lo_g_singledeviation(bcm, 0);
+ for (i = 0; i < 16; i++) {
+ bcm43xx_radio_write16(bcm, 0x0052, i);
+ udelay(10);
+ tmp = bcm43xx_phy_lo_g_singledeviation(bcm, 0);
+ if (tmp < smallest) {
+ smallest = tmp;
+ txctl2 = i;
+ }
+ }
+ radio->txctl2 = txctl2;
+}
+
+static
+void bcm43xx_phy_lo_g_state(struct bcm43xx_private *bcm,
+ const struct bcm43xx_lopair *in_pair,
+ struct bcm43xx_lopair *out_pair,
+ u16 r27)
+{
+ static const struct bcm43xx_lopair transitions[8] = {
+ { .high = 1, .low = 1, },
+ { .high = 1, .low = 0, },
+ { .high = 1, .low = -1, },
+ { .high = 0, .low = -1, },
+ { .high = -1, .low = -1, },
+ { .high = -1, .low = 0, },
+ { .high = -1, .low = 1, },
+ { .high = 0, .low = 1, },
+ };
+ struct bcm43xx_lopair lowest_transition = {
+ .high = in_pair->high,
+ .low = in_pair->low,
+ };
+ struct bcm43xx_lopair tmp_pair;
+ struct bcm43xx_lopair transition;
+ int i = 12;
+ int state = 0;
+ int found_lower;
+ int j, begin, end;
+ u32 lowest_deviation;
+ u32 tmp;
+
+ /* Note that in_pair and out_pair can point to the same pair. Be careful. */
+
+ bcm43xx_lo_write(bcm, &lowest_transition);
+ lowest_deviation = bcm43xx_phy_lo_g_singledeviation(bcm, r27);
+ do {
+ found_lower = 0;
+ assert(state >= 0 && state <= 8);
+ if (state == 0) {
+ begin = 1;
+ end = 8;
+ } else if (state % 2 == 0) {
+ begin = state - 1;
+ end = state + 1;
+ } else {
+ begin = state - 2;
+ end = state + 2;
+ }
+ if (begin < 1)
+ begin += 8;
+ if (end > 8)
+ end -= 8;
+
+ j = begin;
+ tmp_pair.high = lowest_transition.high;
+ tmp_pair.low = lowest_transition.low;
+ while (1) {
+ assert(j >= 1 && j <= 8);
+ transition.high = tmp_pair.high + transitions[j - 1].high;
+ transition.low = tmp_pair.low + transitions[j - 1].low;
+ if ((abs(transition.low) < 9) && (abs(transition.high) < 9)) {
+ bcm43xx_lo_write(bcm, &transition);
+ tmp = bcm43xx_phy_lo_g_singledeviation(bcm, r27);
+ if (tmp < lowest_deviation) {
+ lowest_deviation = tmp;
+ state = j;
+ found_lower = 1;
+
+ lowest_transition.high = transition.high;
+ lowest_transition.low = transition.low;
+ }
+ }
+ if (j == end)
+ break;
+ if (j == 8)
+ j = 1;
+ else
+ j++;
+ }
+ } while (i-- && found_lower);
+
+ out_pair->high = lowest_transition.high;
+ out_pair->low = lowest_transition.low;
+}
+
+/* Set the baseband attenuation value on chip. */
+void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm,
+ u16 baseband_attenuation)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 value;
+
+ if (phy->version == 0) {
+ value = (bcm43xx_read16(bcm, 0x03E6) & 0xFFF0);
+ value |= (baseband_attenuation & 0x000F);
+ bcm43xx_write16(bcm, 0x03E6, value);
+ return;
+ }
+
+ if (phy->version > 1) {
+ value = bcm43xx_phy_read(bcm, 0x0060) & ~0x003C;
+ value |= (baseband_attenuation << 2) & 0x003C;
+ } else {
+ value = bcm43xx_phy_read(bcm, 0x0060) & ~0x0078;
+ value |= (baseband_attenuation << 3) & 0x0078;
+ }
+ bcm43xx_phy_write(bcm, 0x0060, value);
+}
+
+/* http://bcm-specs.sipsolutions.net/LocalOscillator/Measure */
+void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm)
+{
+ static const u8 pairorder[10] = { 3, 1, 5, 7, 9, 2, 0, 4, 6, 8 };
+ const int is_initializing = bcm43xx_is_initializing(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 h, i, oldi = 0, j;
+ struct bcm43xx_lopair control;
+ struct bcm43xx_lopair *tmp_control;
+ u16 tmp;
+ u16 regstack[16] = { 0 };
+ u8 oldchannel;
+
+ //XXX: What are these?
+ u8 r27 = 0, r31;
+
+ oldchannel = radio->channel;
+ /* Setup */
+ if (phy->connected) {
+ regstack[0] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS);
+ regstack[1] = bcm43xx_phy_read(bcm, 0x0802);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF);
+ bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC);
+ }
+ regstack[3] = bcm43xx_read16(bcm, 0x03E2);
+ bcm43xx_write16(bcm, 0x03E2, regstack[3] | 0x8000);
+ regstack[4] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
+ regstack[5] = bcm43xx_phy_read(bcm, 0x15);
+ regstack[6] = bcm43xx_phy_read(bcm, 0x2A);
+ regstack[7] = bcm43xx_phy_read(bcm, 0x35);
+ regstack[8] = bcm43xx_phy_read(bcm, 0x60);
+ regstack[9] = bcm43xx_radio_read16(bcm, 0x43);
+ regstack[10] = bcm43xx_radio_read16(bcm, 0x7A);
+ regstack[11] = bcm43xx_radio_read16(bcm, 0x52);
+ if (phy->connected) {
+ regstack[12] = bcm43xx_phy_read(bcm, 0x0811);
+ regstack[13] = bcm43xx_phy_read(bcm, 0x0812);
+ regstack[14] = bcm43xx_phy_read(bcm, 0x0814);
+ regstack[15] = bcm43xx_phy_read(bcm, 0x0815);
+ }
+ bcm43xx_radio_selectchannel(bcm, 6, 0);
+ if (phy->connected) {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0] & 0x7FFF);
+ bcm43xx_phy_write(bcm, 0x0802, regstack[1] & 0xFFFC);
+ bcm43xx_dummy_transmission(bcm);
+ }
+ bcm43xx_radio_write16(bcm, 0x0043, 0x0006);
+
+ bcm43xx_phy_set_baseband_attenuation(bcm, 2);
+
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, 0x0000);
+ bcm43xx_phy_write(bcm, 0x002E, 0x007F);
+ bcm43xx_phy_write(bcm, 0x080F, 0x0078);
+ bcm43xx_phy_write(bcm, 0x0035, regstack[7] & ~(1 << 7));
+ bcm43xx_radio_write16(bcm, 0x007A, regstack[10] & 0xFFF0);
+ bcm43xx_phy_write(bcm, 0x002B, 0x0203);
+ bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
+ if (phy->connected) {
+ bcm43xx_phy_write(bcm, 0x0814, regstack[14] | 0x0003);
+ bcm43xx_phy_write(bcm, 0x0815, regstack[15] & 0xFFFC);
+ bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
+ bcm43xx_phy_write(bcm, 0x0812, 0x00B2);
+ }
+ if (is_initializing)
+ bcm43xx_phy_lo_g_measure_txctl2(bcm);
+ bcm43xx_phy_write(bcm, 0x080F, 0x8078);
+
+ /* Measure */
+ control.low = 0;
+ control.high = 0;
+ for (h = 0; h < 10; h++) {
+ /* Loop over each possible RadioAttenuation (0-9) */
+ i = pairorder[h];
+ if (is_initializing) {
+ if (i == 3) {
+ control.low = 0;
+ control.high = 0;
+ } else if (((i % 2 == 1) && (oldi % 2 == 1)) ||
+ ((i % 2 == 0) && (oldi % 2 == 0))) {
+ tmp_control = bcm43xx_get_lopair(phy, oldi, 0);
+ memcpy(&control, tmp_control, sizeof(control));
+ } else {
+ tmp_control = bcm43xx_get_lopair(phy, 3, 0);
+ memcpy(&control, tmp_control, sizeof(control));
+ }
+ }
+ /* Loop over each possible BasebandAttenuation/2 */
+ for (j = 0; j < 4; j++) {
+ if (is_initializing) {
+ tmp = i * 2 + j;
+ r27 = 0;
+ r31 = 0;
+ if (tmp > 14) {
+ r31 = 1;
+ if (tmp > 17)
+ r27 = 1;
+ if (tmp > 19)
+ r27 = 2;
+ }
+ } else {
+ tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
+ if (!tmp_control->used)
+ continue;
+ memcpy(&control, tmp_control, sizeof(control));
+ r27 = 3;
+ r31 = 0;
+ }
+ bcm43xx_radio_write16(bcm, 0x43, i);
+ bcm43xx_radio_write16(bcm, 0x52, radio->txctl2);
+ udelay(10);
+
+ bcm43xx_phy_set_baseband_attenuation(bcm, j * 2);
+
+ tmp = (regstack[10] & 0xFFF0);
+ if (r31)
+ tmp |= 0x0008;
+ bcm43xx_radio_write16(bcm, 0x007A, tmp);
+
+ tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
+ bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27);
+ }
+ oldi = i;
+ }
+ /* Loop over each possible RadioAttenuation (10-13) */
+ for (i = 10; i < 14; i++) {
+ /* Loop over each possible BasebandAttenuation/2 */
+ for (j = 0; j < 4; j++) {
+ if (is_initializing) {
+ tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2);
+ memcpy(&control, tmp_control, sizeof(control));
+ tmp = (i - 9) * 2 + j - 5;//FIXME: This is wrong, as the following if statement can never trigger.
+ r27 = 0;
+ r31 = 0;
+ if (tmp > 14) {
+ r31 = 1;
+ if (tmp > 17)
+ r27 = 1;
+ if (tmp > 19)
+ r27 = 2;
+ }
+ } else {
+ tmp_control = bcm43xx_get_lopair(phy, i - 9, j * 2);
+ if (!tmp_control->used)
+ continue;
+ memcpy(&control, tmp_control, sizeof(control));
+ r27 = 3;
+ r31 = 0;
+ }
+ bcm43xx_radio_write16(bcm, 0x43, i - 9);
+ bcm43xx_radio_write16(bcm, 0x52,
+ radio->txctl2
+ | (3/*txctl1*/ << 4));//FIXME: shouldn't txctl1 be zero here and 3 in the loop above?
+ udelay(10);
+
+ bcm43xx_phy_set_baseband_attenuation(bcm, j * 2);
+
+ tmp = (regstack[10] & 0xFFF0);
+ if (r31)
+ tmp |= 0x0008;
+ bcm43xx_radio_write16(bcm, 0x7A, tmp);
+
+ tmp_control = bcm43xx_get_lopair(phy, i, j * 2);
+ bcm43xx_phy_lo_g_state(bcm, &control, tmp_control, r27);
+ }
+ }
+
+ /* Restoration */
+ if (phy->connected) {
+ bcm43xx_phy_write(bcm, 0x0015, 0xE300);
+ bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA0);
+ udelay(5);
+ bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA2);
+ udelay(2);
+ bcm43xx_phy_write(bcm, 0x0812, (r27 << 8) | 0xA3);
+ } else
+ bcm43xx_phy_write(bcm, 0x0015, r27 | 0xEFA0);
+ bcm43xx_phy_lo_adjust(bcm, is_initializing);
+ bcm43xx_phy_write(bcm, 0x002E, 0x807F);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x002F, 0x0202);
+ else
+ bcm43xx_phy_write(bcm, 0x002F, 0x0101);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, regstack[4]);
+ bcm43xx_phy_write(bcm, 0x0015, regstack[5]);
+ bcm43xx_phy_write(bcm, 0x002A, regstack[6]);
+ bcm43xx_phy_write(bcm, 0x0035, regstack[7]);
+ bcm43xx_phy_write(bcm, 0x0060, regstack[8]);
+ bcm43xx_radio_write16(bcm, 0x0043, regstack[9]);
+ bcm43xx_radio_write16(bcm, 0x007A, regstack[10]);
+ regstack[11] &= 0x00F0;
+ regstack[11] |= (bcm43xx_radio_read16(bcm, 0x52) & 0x000F);
+ bcm43xx_radio_write16(bcm, 0x52, regstack[11]);
+ bcm43xx_write16(bcm, 0x03E2, regstack[3]);
+ if (phy->connected) {
+ bcm43xx_phy_write(bcm, 0x0811, regstack[12]);
+ bcm43xx_phy_write(bcm, 0x0812, regstack[13]);
+ bcm43xx_phy_write(bcm, 0x0814, regstack[14]);
+ bcm43xx_phy_write(bcm, 0x0815, regstack[15]);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, regstack[0]);
+ bcm43xx_phy_write(bcm, 0x0802, regstack[1]);
+ }
+ bcm43xx_radio_selectchannel(bcm, oldchannel, 1);
+
+#ifdef CONFIG_BCM43XX_DEBUG
+ {
+ /* Sanity check for all lopairs. */
+ for (i = 0; i < BCM43xx_LO_COUNT; i++) {
+ tmp_control = phy->_lo_pairs + i;
+ if (tmp_control->low < -8 || tmp_control->low > 8 ||
+ tmp_control->high < -8 || tmp_control->high > 8) {
+ printk(KERN_WARNING PFX
+ "WARNING: Invalid LOpair (low: %d, high: %d, index: %d)\n",
+ tmp_control->low, tmp_control->high, i);
+ }
+ }
+ }
+#endif /* CONFIG_BCM43XX_DEBUG */
+}
+
+static
+void bcm43xx_phy_lo_mark_current_used(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_lopair *pair;
+
+ pair = bcm43xx_current_lopair(bcm);
+ pair->used = 1;
+}
+
+void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_lopair *pair;
+ int i;
+
+ for (i = 0; i < BCM43xx_LO_COUNT; i++) {
+ pair = phy->_lo_pairs + i;
+ pair->used = 0;
+ }
+}
+
+/* http://bcm-specs.sipsolutions.net/EstimatePowerOut
+ * This function converts a TSSI value to dBm in Q5.2
+ */
+static s8 bcm43xx_phy_estimate_power_out(struct bcm43xx_private *bcm, s8 tssi)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ s8 dbm = 0;
+ s32 tmp;
+
+ tmp = phy->idle_tssi;
+ tmp += tssi;
+ tmp -= phy->savedpctlreg;
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ tmp += 0x80;
+ tmp = limit_value(tmp, 0x00, 0xFF);
+ dbm = phy->tssi2dbm[tmp];
+ TODO(); //TODO: There's a FIXME on the specs
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ tmp = limit_value(tmp, 0x00, 0x3F);
+ dbm = phy->tssi2dbm[tmp];
+ break;
+ default:
+ assert(0);
+ }
+
+ return dbm;
+}
+
+/* http://bcm-specs.sipsolutions.net/RecalculateTransmissionPower */
+void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ if (phy->savedpctlreg == 0xFFFF)
+ return;
+ if ((bcm->board_type == 0x0416) &&
+ (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM))
+ return;
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A: {
+
+ TODO(); //TODO: Nothing for A PHYs yet :-/
+
+ break;
+ }
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G: {
+ u16 tmp;
+ u16 txpower;
+ s8 v0, v1, v2, v3;
+ s8 average;
+ u8 max_pwr;
+ s16 desired_pwr, estimated_pwr, pwr_adjust;
+ s16 radio_att_delta, baseband_att_delta;
+ s16 radio_attenuation, baseband_attenuation;
+ unsigned long phylock_flags;
+
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0058);
+ v0 = (s8)(tmp & 0x00FF);
+ v1 = (s8)((tmp & 0xFF00) >> 8);
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005A);
+ v2 = (s8)(tmp & 0x00FF);
+ v3 = (s8)((tmp & 0xFF00) >> 8);
+ tmp = 0;
+
+ if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F) {
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0070);
+ v0 = (s8)(tmp & 0x00FF);
+ v1 = (s8)((tmp & 0xFF00) >> 8);
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0072);
+ v2 = (s8)(tmp & 0x00FF);
+ v3 = (s8)((tmp & 0xFF00) >> 8);
+ if (v0 == 0x7F || v1 == 0x7F || v2 == 0x7F || v3 == 0x7F)
+ return;
+ v0 = (v0 + 0x20) & 0x3F;
+ v1 = (v1 + 0x20) & 0x3F;
+ v2 = (v2 + 0x20) & 0x3F;
+ v3 = (v3 + 0x20) & 0x3F;
+ tmp = 1;
+ }
+ bcm43xx_radio_clear_tssi(bcm);
+
+ average = (v0 + v1 + v2 + v3 + 2) / 4;
+
+ if (tmp && (bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x005E) & 0x8))
+ average -= 13;
+
+ estimated_pwr = bcm43xx_phy_estimate_power_out(bcm, average);
+
+ max_pwr = bcm->sprom.maxpower_bgphy;
+
+ if ((bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) &&
+ (phy->type == BCM43xx_PHYTYPE_G))
+ max_pwr -= 0x3;
+
+ /*TODO:
+ max_pwr = min(REG - bcm->sprom.antennagain_bgphy - 0x6, max_pwr)
+ where REG is the max power as per the regulatory domain
+ */
+
+ desired_pwr = limit_value(radio->txpower_desired, 0, max_pwr);
+ /* Check if we need to adjust the current power. */
+ pwr_adjust = desired_pwr - estimated_pwr;
+ radio_att_delta = -(pwr_adjust + 7) >> 3;
+ baseband_att_delta = -(pwr_adjust >> 1) - (4 * radio_att_delta);
+ if ((radio_att_delta == 0) && (baseband_att_delta == 0)) {
+ bcm43xx_phy_lo_mark_current_used(bcm);
+ return;
+ }
+
+ /* Calculate the new attenuation values. */
+ baseband_attenuation = radio->baseband_atten;
+ baseband_attenuation += baseband_att_delta;
+ radio_attenuation = radio->radio_atten;
+ radio_attenuation += radio_att_delta;
+
+ /* Get baseband and radio attenuation values into their permitted ranges.
+ * baseband 0-11, radio 0-9.
+ * Radio attenuation affects power level 4 times as much as baseband.
+ */
+ if (radio_attenuation < 0) {
+ baseband_attenuation -= (4 * -radio_attenuation);
+ radio_attenuation = 0;
+ } else if (radio_attenuation > 9) {
+ baseband_attenuation += (4 * (radio_attenuation - 9));
+ radio_attenuation = 9;
+ } else {
+ while (baseband_attenuation < 0 && radio_attenuation > 0) {
+ baseband_attenuation += 4;
+ radio_attenuation--;
+ }
+ while (baseband_attenuation > 11 && radio_attenuation < 9) {
+ baseband_attenuation -= 4;
+ radio_attenuation++;
+ }
+ }
+ baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
+
+ txpower = radio->txctl1;
+ if ((radio->version == 0x2050) && (radio->revision == 2)) {
+ if (radio_attenuation <= 1) {
+ if (txpower == 0) {
+ txpower = 3;
+ radio_attenuation += 2;
+ baseband_attenuation += 2;
+ } else if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL) {
+ baseband_attenuation += 4 * (radio_attenuation - 2);
+ radio_attenuation = 2;
+ }
+ } else if (radio_attenuation > 4 && txpower != 0) {
+ txpower = 0;
+ if (baseband_attenuation < 3) {
+ radio_attenuation -= 3;
+ baseband_attenuation += 2;
+ } else {
+ radio_attenuation -= 2;
+ baseband_attenuation -= 2;
+ }
+ }
+ }
+ radio->txctl1 = txpower;
+ baseband_attenuation = limit_value(baseband_attenuation, 0, 11);
+ radio_attenuation = limit_value(radio_attenuation, 0, 9);
+
+ bcm43xx_phy_lock(bcm, phylock_flags);
+ bcm43xx_radio_lock(bcm);
+ bcm43xx_radio_set_txpower_bg(bcm, baseband_attenuation,
+ radio_attenuation, txpower);
+ bcm43xx_phy_lo_mark_current_used(bcm);
+ bcm43xx_radio_unlock(bcm);
+ bcm43xx_phy_unlock(bcm, phylock_flags);
+ break;
+ }
+ default:
+ assert(0);
+ }
+}
+
+static inline
+s32 bcm43xx_tssi2dbm_ad(s32 num, s32 den)
+{
+ if (num < 0)
+ return num/den;
+ else
+ return (num+den/2)/den;
+}
+
+static inline
+s8 bcm43xx_tssi2dbm_entry(s8 entry [], u8 index, s16 pab0, s16 pab1, s16 pab2)
+{
+ s32 m1, m2, f = 256, q, delta;
+ s8 i = 0;
+
+ m1 = bcm43xx_tssi2dbm_ad(16 * pab0 + index * pab1, 32);
+ m2 = max(bcm43xx_tssi2dbm_ad(32768 + index * pab2, 256), 1);
+ do {
+ if (i > 15)
+ return -EINVAL;
+ q = bcm43xx_tssi2dbm_ad(f * 4096 -
+ bcm43xx_tssi2dbm_ad(m2 * f, 16) * f, 2048);
+ delta = abs(q - f);
+ f = q;
+ i++;
+ } while (delta >= 2);
+ entry[index] = limit_value(bcm43xx_tssi2dbm_ad(m1 * f, 8192), -127, 128);
+ return 0;
+}
+
+/* http://bcm-specs.sipsolutions.net/TSSI_to_DBM_Table */
+int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ s16 pab0, pab1, pab2;
+ u8 idx;
+ s8 *dyn_tssi2dbm;
+
+ if (phy->type == BCM43xx_PHYTYPE_A) {
+ pab0 = (s16)(bcm->sprom.pa1b0);
+ pab1 = (s16)(bcm->sprom.pa1b1);
+ pab2 = (s16)(bcm->sprom.pa1b2);
+ } else {
+ pab0 = (s16)(bcm->sprom.pa0b0);
+ pab1 = (s16)(bcm->sprom.pa0b1);
+ pab2 = (s16)(bcm->sprom.pa0b2);
+ }
+
+ if ((bcm->chip_id == 0x4301) && (radio->version != 0x2050)) {
+ phy->idle_tssi = 0x34;
+ phy->tssi2dbm = bcm43xx_tssi2dbm_b_table;
+ return 0;
+ }
+
+ if (pab0 != 0 && pab1 != 0 && pab2 != 0 &&
+ pab0 != -1 && pab1 != -1 && pab2 != -1) {
+ /* The pabX values are set in SPROM. Use them. */
+ if (phy->type == BCM43xx_PHYTYPE_A) {
+ if ((s8)bcm->sprom.idle_tssi_tgt_aphy != 0 &&
+ (s8)bcm->sprom.idle_tssi_tgt_aphy != -1)
+ phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_aphy);
+ else
+ phy->idle_tssi = 62;
+ } else {
+ if ((s8)bcm->sprom.idle_tssi_tgt_bgphy != 0 &&
+ (s8)bcm->sprom.idle_tssi_tgt_bgphy != -1)
+ phy->idle_tssi = (s8)(bcm->sprom.idle_tssi_tgt_bgphy);
+ else
+ phy->idle_tssi = 62;
+ }
+ dyn_tssi2dbm = kmalloc(64, GFP_KERNEL);
+ if (dyn_tssi2dbm == NULL) {
+ printk(KERN_ERR PFX "Could not allocate memory"
+ "for tssi2dbm table\n");
+ return -ENOMEM;
+ }
+ for (idx = 0; idx < 64; idx++)
+ if (bcm43xx_tssi2dbm_entry(dyn_tssi2dbm, idx, pab0, pab1, pab2)) {
+ phy->tssi2dbm = NULL;
+ printk(KERN_ERR PFX "Could not generate "
+ "tssi2dBm table\n");
+ return -ENODEV;
+ }
+ phy->tssi2dbm = dyn_tssi2dbm;
+ phy->dyn_tssi_tbl = 1;
+ } else {
+ /* pabX values not set in SPROM. */
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ /* APHY needs a generated table. */
+ phy->tssi2dbm = NULL;
+ printk(KERN_ERR PFX "Could not generate tssi2dBm "
+ "table (wrong SPROM info)!\n");
+ return -ENODEV;
+ case BCM43xx_PHYTYPE_B:
+ phy->idle_tssi = 0x34;
+ phy->tssi2dbm = bcm43xx_tssi2dbm_b_table;
+ break;
+ case BCM43xx_PHYTYPE_G:
+ phy->idle_tssi = 0x34;
+ phy->tssi2dbm = bcm43xx_tssi2dbm_g_table;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int bcm43xx_phy_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ int err = -ENODEV;
+ unsigned long flags;
+
+ /* We do not want to be preempted while calibrating
+ * the hardware.
+ */
+ local_irq_save(flags);
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ if (phy->rev == 2 || phy->rev == 3) {
+ bcm43xx_phy_inita(bcm);
+ err = 0;
+ }
+ break;
+ case BCM43xx_PHYTYPE_B:
+ switch (phy->rev) {
+ case 2:
+ bcm43xx_phy_initb2(bcm);
+ err = 0;
+ break;
+ case 4:
+ bcm43xx_phy_initb4(bcm);
+ err = 0;
+ break;
+ case 5:
+ bcm43xx_phy_initb5(bcm);
+ err = 0;
+ break;
+ case 6:
+ bcm43xx_phy_initb6(bcm);
+ err = 0;
+ break;
+ }
+ break;
+ case BCM43xx_PHYTYPE_G:
+ bcm43xx_phy_initg(bcm);
+ err = 0;
+ break;
+ }
+ local_irq_restore(flags);
+ if (err)
+ printk(KERN_WARNING PFX "Unknown PHYTYPE found!\n");
+
+ return err;
+}
+
+void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 antennadiv;
+ u16 offset;
+ u16 value;
+ u32 ucodeflags;
+
+ antennadiv = phy->antenna_diversity;
+
+ if (antennadiv == 0xFFFF)
+ antennadiv = 3;
+ assert(antennadiv <= 3);
+
+ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET,
+ ucodeflags & ~BCM43xx_UCODEFLAG_AUTODIV);
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ case BCM43xx_PHYTYPE_G:
+ if (phy->type == BCM43xx_PHYTYPE_A)
+ offset = 0x0000;
+ else
+ offset = 0x0400;
+
+ if (antennadiv == 2)
+ value = (3/*automatic*/ << 7);
+ else
+ value = (antennadiv << 7);
+ bcm43xx_phy_write(bcm, offset + 1,
+ (bcm43xx_phy_read(bcm, offset + 1)
+ & 0x7E7F) | value);
+
+ if (antennadiv >= 2) {
+ if (antennadiv == 2)
+ value = (antennadiv << 7);
+ else
+ value = (0/*force0*/ << 7);
+ bcm43xx_phy_write(bcm, offset + 0x2B,
+ (bcm43xx_phy_read(bcm, offset + 0x2B)
+ & 0xFEFF) | value);
+ }
+
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ if (antennadiv >= 2)
+ bcm43xx_phy_write(bcm, 0x048C,
+ bcm43xx_phy_read(bcm, 0x048C)
+ | 0x2000);
+ else
+ bcm43xx_phy_write(bcm, 0x048C,
+ bcm43xx_phy_read(bcm, 0x048C)
+ & ~0x2000);
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x0461,
+ bcm43xx_phy_read(bcm, 0x0461)
+ | 0x0010);
+ bcm43xx_phy_write(bcm, 0x04AD,
+ (bcm43xx_phy_read(bcm, 0x04AD)
+ & 0x00FF) | 0x0015);
+ if (phy->rev == 2)
+ bcm43xx_phy_write(bcm, 0x0427, 0x0008);
+ else
+ bcm43xx_phy_write(bcm, 0x0427,
+ (bcm43xx_phy_read(bcm, 0x0427)
+ & 0x00FF) | 0x0008);
+ }
+ else if (phy->rev >= 6)
+ bcm43xx_phy_write(bcm, 0x049B, 0x00DC);
+ } else {
+ if (phy->rev < 3)
+ bcm43xx_phy_write(bcm, 0x002B,
+ (bcm43xx_phy_read(bcm, 0x002B)
+ & 0x00FF) | 0x0024);
+ else {
+ bcm43xx_phy_write(bcm, 0x0061,
+ bcm43xx_phy_read(bcm, 0x0061)
+ | 0x0010);
+ if (phy->rev == 3) {
+ bcm43xx_phy_write(bcm, 0x0093, 0x001D);
+ bcm43xx_phy_write(bcm, 0x0027, 0x0008);
+ } else {
+ bcm43xx_phy_write(bcm, 0x0093, 0x003A);
+ bcm43xx_phy_write(bcm, 0x0027,
+ (bcm43xx_phy_read(bcm, 0x0027)
+ & 0x00FF) | 0x0008);
+ }
+ }
+ }
+ break;
+ case BCM43xx_PHYTYPE_B:
+ if (bcm->current_core->rev == 2)
+ value = (3/*automatic*/ << 7);
+ else
+ value = (antennadiv << 7);
+ bcm43xx_phy_write(bcm, 0x03E2,
+ (bcm43xx_phy_read(bcm, 0x03E2)
+ & 0xFE7F) | value);
+ break;
+ default:
+ assert(0);
+ }
+
+ if (antennadiv >= 2) {
+ ucodeflags = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET);
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET,
+ ucodeflags | BCM43xx_UCODEFLAG_AUTODIV);
+ }
+
+ phy->antenna_diversity = antennadiv;
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.h b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h
new file mode 100644
index 000000000000..1f321ef42be8
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.h
@@ -0,0 +1,74 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef BCM43xx_PHY_H_
+#define BCM43xx_PHY_H_
+
+#include <linux/types.h>
+
+struct bcm43xx_private;
+
+void bcm43xx_raw_phy_lock(struct bcm43xx_private *bcm);
+#define bcm43xx_phy_lock(bcm, flags) \
+ do { \
+ local_irq_save(flags); \
+ bcm43xx_raw_phy_lock(bcm); \
+ } while (0)
+void bcm43xx_raw_phy_unlock(struct bcm43xx_private *bcm);
+#define bcm43xx_phy_unlock(bcm, flags) \
+ do { \
+ bcm43xx_raw_phy_unlock(bcm); \
+ local_irq_restore(flags); \
+ } while (0)
+
+u16 bcm43xx_phy_read(struct bcm43xx_private *bcm, u16 offset);
+void bcm43xx_phy_write(struct bcm43xx_private *bcm, u16 offset, u16 val);
+
+int bcm43xx_phy_init_tssi2dbm_table(struct bcm43xx_private *bcm);
+int bcm43xx_phy_init(struct bcm43xx_private *bcm);
+
+void bcm43xx_phy_set_antenna_diversity(struct bcm43xx_private *bcm);
+void bcm43xx_phy_calibrate(struct bcm43xx_private *bcm);
+int bcm43xx_phy_connect(struct bcm43xx_private *bcm, int connect);
+
+void bcm43xx_phy_lo_b_measure(struct bcm43xx_private *bcm);
+void bcm43xx_phy_lo_g_measure(struct bcm43xx_private *bcm);
+void bcm43xx_phy_xmitpower(struct bcm43xx_private *bcm);
+
+/* Adjust the LocalOscillator to the saved values.
+ * "fixed" is only set to 1 once in initialization. Set to 0 otherwise.
+ */
+void bcm43xx_phy_lo_adjust(struct bcm43xx_private *bcm, int fixed);
+void bcm43xx_phy_lo_mark_all_unused(struct bcm43xx_private *bcm);
+
+void bcm43xx_phy_set_baseband_attenuation(struct bcm43xx_private *bcm,
+ u16 baseband_attenuation);
+
+#endif /* BCM43xx_PHY_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.c b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c
new file mode 100644
index 000000000000..c59ddd40680d
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.c
@@ -0,0 +1,606 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ PIO Transmission
+
+ Copyright (c) 2005 Michael Buesch <mbuesch@freenet.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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "bcm43xx.h"
+#include "bcm43xx_pio.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_xmit.h"
+
+#include <linux/delay.h>
+
+
+static void tx_start(struct bcm43xx_pioqueue *queue)
+{
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
+ BCM43xx_PIO_TXCTL_INIT);
+}
+
+static void tx_octet(struct bcm43xx_pioqueue *queue,
+ u8 octet)
+{
+ if (queue->need_workarounds) {
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
+ octet);
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
+ BCM43xx_PIO_TXCTL_WRITEHI);
+ } else {
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
+ BCM43xx_PIO_TXCTL_WRITEHI);
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
+ octet);
+ }
+}
+
+static u16 tx_get_next_word(struct bcm43xx_txhdr *txhdr,
+ const u8 *packet,
+ unsigned int *pos)
+{
+ const u8 *source;
+ unsigned int i = *pos;
+ u16 ret;
+
+ if (i < sizeof(*txhdr)) {
+ source = (const u8 *)txhdr;
+ } else {
+ source = packet;
+ i -= sizeof(*txhdr);
+ }
+ ret = le16_to_cpu( *((u16 *)(source + i)) );
+ *pos += 2;
+
+ return ret;
+}
+
+static void tx_data(struct bcm43xx_pioqueue *queue,
+ struct bcm43xx_txhdr *txhdr,
+ const u8 *packet,
+ unsigned int octets)
+{
+ u16 data;
+ unsigned int i = 0;
+
+ if (queue->need_workarounds) {
+ data = tx_get_next_word(txhdr, packet, &i);
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data);
+ }
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
+ BCM43xx_PIO_TXCTL_WRITELO |
+ BCM43xx_PIO_TXCTL_WRITEHI);
+ while (i < octets - 1) {
+ data = tx_get_next_word(txhdr, packet, &i);
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA, data);
+ }
+ if (octets % 2)
+ tx_octet(queue, packet[octets - sizeof(*txhdr) - 1]);
+}
+
+static void tx_complete(struct bcm43xx_pioqueue *queue,
+ struct sk_buff *skb)
+{
+ if (queue->need_workarounds) {
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXDATA,
+ skb->data[skb->len - 1]);
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
+ BCM43xx_PIO_TXCTL_WRITEHI |
+ BCM43xx_PIO_TXCTL_COMPLETE);
+ } else {
+ bcm43xx_pio_write(queue, BCM43xx_PIO_TXCTL,
+ BCM43xx_PIO_TXCTL_COMPLETE);
+ }
+}
+
+static u16 generate_cookie(struct bcm43xx_pioqueue *queue,
+ int packetindex)
+{
+ u16 cookie = 0x0000;
+
+ /* We use the upper 4 bits for the PIO
+ * controller ID and the lower 12 bits
+ * for the packet index (in the cache).
+ */
+ switch (queue->mmio_base) {
+ case BCM43xx_MMIO_PIO1_BASE:
+ break;
+ case BCM43xx_MMIO_PIO2_BASE:
+ cookie = 0x1000;
+ break;
+ case BCM43xx_MMIO_PIO3_BASE:
+ cookie = 0x2000;
+ break;
+ case BCM43xx_MMIO_PIO4_BASE:
+ cookie = 0x3000;
+ break;
+ default:
+ assert(0);
+ }
+ assert(((u16)packetindex & 0xF000) == 0x0000);
+ cookie |= (u16)packetindex;
+
+ return cookie;
+}
+
+static
+struct bcm43xx_pioqueue * parse_cookie(struct bcm43xx_private *bcm,
+ u16 cookie,
+ struct bcm43xx_pio_txpacket **packet)
+{
+ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm);
+ struct bcm43xx_pioqueue *queue = NULL;
+ int packetindex;
+
+ switch (cookie & 0xF000) {
+ case 0x0000:
+ queue = pio->queue0;
+ break;
+ case 0x1000:
+ queue = pio->queue1;
+ break;
+ case 0x2000:
+ queue = pio->queue2;
+ break;
+ case 0x3000:
+ queue = pio->queue3;
+ break;
+ default:
+ assert(0);
+ }
+ packetindex = (cookie & 0x0FFF);
+ assert(packetindex >= 0 && packetindex < BCM43xx_PIO_MAXTXPACKETS);
+ *packet = &(queue->tx_packets_cache[packetindex]);
+
+ return queue;
+}
+
+static void pio_tx_write_fragment(struct bcm43xx_pioqueue *queue,
+ struct sk_buff *skb,
+ struct bcm43xx_pio_txpacket *packet)
+{
+ struct bcm43xx_txhdr txhdr;
+ unsigned int octets;
+
+ assert(skb_shinfo(skb)->nr_frags == 0);
+ bcm43xx_generate_txhdr(queue->bcm,
+ &txhdr, skb->data, skb->len,
+ (packet->xmitted_frags == 0),
+ generate_cookie(queue, pio_txpacket_getindex(packet)));
+
+ tx_start(queue);
+ octets = skb->len + sizeof(txhdr);
+ if (queue->need_workarounds)
+ octets--;
+ tx_data(queue, &txhdr, (u8 *)skb->data, octets);
+ tx_complete(queue, skb);
+}
+
+static void free_txpacket(struct bcm43xx_pio_txpacket *packet,
+ int irq_context)
+{
+ struct bcm43xx_pioqueue *queue = packet->queue;
+
+ ieee80211_txb_free(packet->txb);
+ list_move(&packet->list, &queue->txfree);
+ queue->nr_txfree++;
+
+ assert(queue->tx_devq_used >= packet->xmitted_octets);
+ assert(queue->tx_devq_packets >= packet->xmitted_frags);
+ queue->tx_devq_used -= packet->xmitted_octets;
+ queue->tx_devq_packets -= packet->xmitted_frags;
+}
+
+static int pio_tx_packet(struct bcm43xx_pio_txpacket *packet)
+{
+ struct bcm43xx_pioqueue *queue = packet->queue;
+ struct ieee80211_txb *txb = packet->txb;
+ struct sk_buff *skb;
+ u16 octets;
+ int i;
+
+ for (i = packet->xmitted_frags; i < txb->nr_frags; i++) {
+ skb = txb->fragments[i];
+
+ octets = (u16)skb->len + sizeof(struct bcm43xx_txhdr);
+ assert(queue->tx_devq_size >= octets);
+ assert(queue->tx_devq_packets <= BCM43xx_PIO_MAXTXDEVQPACKETS);
+ assert(queue->tx_devq_used <= queue->tx_devq_size);
+ /* Check if there is sufficient free space on the device
+ * TX queue. If not, return and let the TX tasklet
+ * retry later.
+ */
+ if (queue->tx_devq_packets == BCM43xx_PIO_MAXTXDEVQPACKETS)
+ return -EBUSY;
+ if (queue->tx_devq_used + octets > queue->tx_devq_size)
+ return -EBUSY;
+ /* Now poke the device. */
+ pio_tx_write_fragment(queue, skb, packet);
+
+ /* Account for the packet size.
+ * (We must not overflow the device TX queue)
+ */
+ queue->tx_devq_packets++;
+ queue->tx_devq_used += octets;
+
+ assert(packet->xmitted_frags <= packet->txb->nr_frags);
+ packet->xmitted_frags++;
+ packet->xmitted_octets += octets;
+ }
+ list_move_tail(&packet->list, &queue->txrunning);
+
+ return 0;
+}
+
+static void tx_tasklet(unsigned long d)
+{
+ struct bcm43xx_pioqueue *queue = (struct bcm43xx_pioqueue *)d;
+ struct bcm43xx_private *bcm = queue->bcm;
+ unsigned long flags;
+ struct bcm43xx_pio_txpacket *packet, *tmp_packet;
+ int err;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list) {
+ assert(packet->xmitted_frags < packet->txb->nr_frags);
+ if (packet->xmitted_frags == 0) {
+ int i;
+ struct sk_buff *skb;
+
+ /* Check if the device queue is big
+ * enough for every fragment. If not, drop the
+ * whole packet.
+ */
+ for (i = 0; i < packet->txb->nr_frags; i++) {
+ skb = packet->txb->fragments[i];
+ if (unlikely(skb->len > queue->tx_devq_size)) {
+ dprintkl(KERN_ERR PFX "PIO TX device queue too small. "
+ "Dropping packet.\n");
+ free_txpacket(packet, 1);
+ goto next_packet;
+ }
+ }
+ }
+ /* Try to transmit the packet.
+ * This may not completely succeed.
+ */
+ err = pio_tx_packet(packet);
+ if (err)
+ break;
+ next_packet:
+ continue;
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
+}
+
+static void setup_txqueues(struct bcm43xx_pioqueue *queue)
+{
+ struct bcm43xx_pio_txpacket *packet;
+ int i;
+
+ queue->nr_txfree = BCM43xx_PIO_MAXTXPACKETS;
+ for (i = 0; i < BCM43xx_PIO_MAXTXPACKETS; i++) {
+ packet = &(queue->tx_packets_cache[i]);
+
+ packet->queue = queue;
+ INIT_LIST_HEAD(&packet->list);
+
+ list_add(&packet->list, &queue->txfree);
+ }
+}
+
+static
+struct bcm43xx_pioqueue * bcm43xx_setup_pioqueue(struct bcm43xx_private *bcm,
+ u16 pio_mmio_base)
+{
+ struct bcm43xx_pioqueue *queue;
+ u32 value;
+ u16 qsize;
+
+ queue = kzalloc(sizeof(*queue), GFP_KERNEL);
+ if (!queue)
+ goto out;
+
+ queue->bcm = bcm;
+ queue->mmio_base = pio_mmio_base;
+ queue->need_workarounds = (bcm->current_core->rev < 3);
+
+ INIT_LIST_HEAD(&queue->txfree);
+ INIT_LIST_HEAD(&queue->txqueue);
+ INIT_LIST_HEAD(&queue->txrunning);
+ tasklet_init(&queue->txtask, tx_tasklet,
+ (unsigned long)queue);
+
+ value = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ value |= BCM43xx_SBF_XFER_REG_BYTESWAP;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, value);
+
+ qsize = bcm43xx_read16(bcm, queue->mmio_base + BCM43xx_PIO_TXQBUFSIZE);
+ if (qsize <= BCM43xx_PIO_TXQADJUST) {
+ printk(KERN_ERR PFX "PIO tx device-queue too small (%u)\n", qsize);
+ goto err_freequeue;
+ }
+ qsize -= BCM43xx_PIO_TXQADJUST;
+ queue->tx_devq_size = qsize;
+
+ setup_txqueues(queue);
+
+out:
+ return queue;
+
+err_freequeue:
+ kfree(queue);
+ queue = NULL;
+ goto out;
+}
+
+static void cancel_transfers(struct bcm43xx_pioqueue *queue)
+{
+ struct bcm43xx_pio_txpacket *packet, *tmp_packet;
+
+ netif_tx_disable(queue->bcm->net_dev);
+ assert(queue->bcm->shutting_down);
+ tasklet_disable(&queue->txtask);
+
+ list_for_each_entry_safe(packet, tmp_packet, &queue->txrunning, list)
+ free_txpacket(packet, 0);
+ list_for_each_entry_safe(packet, tmp_packet, &queue->txqueue, list)
+ free_txpacket(packet, 0);
+}
+
+static void bcm43xx_destroy_pioqueue(struct bcm43xx_pioqueue *queue)
+{
+ if (!queue)
+ return;
+
+ cancel_transfers(queue);
+ kfree(queue);
+}
+
+void bcm43xx_pio_free(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_pio *pio;
+
+ if (!bcm43xx_using_pio(bcm))
+ return;
+ pio = bcm43xx_current_pio(bcm);
+
+ bcm43xx_destroy_pioqueue(pio->queue3);
+ pio->queue3 = NULL;
+ bcm43xx_destroy_pioqueue(pio->queue2);
+ pio->queue2 = NULL;
+ bcm43xx_destroy_pioqueue(pio->queue1);
+ pio->queue1 = NULL;
+ bcm43xx_destroy_pioqueue(pio->queue0);
+ pio->queue0 = NULL;
+}
+
+int bcm43xx_pio_init(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_pio *pio = bcm43xx_current_pio(bcm);
+ struct bcm43xx_pioqueue *queue;
+ int err = -ENOMEM;
+
+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO1_BASE);
+ if (!queue)
+ goto out;
+ pio->queue0 = queue;
+
+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO2_BASE);
+ if (!queue)
+ goto err_destroy0;
+ pio->queue1 = queue;
+
+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO3_BASE);
+ if (!queue)
+ goto err_destroy1;
+ pio->queue2 = queue;
+
+ queue = bcm43xx_setup_pioqueue(bcm, BCM43xx_MMIO_PIO4_BASE);
+ if (!queue)
+ goto err_destroy2;
+ pio->queue3 = queue;
+
+ if (bcm->current_core->rev < 3)
+ bcm->irq_savedstate |= BCM43xx_IRQ_PIO_WORKAROUND;
+
+ dprintk(KERN_INFO PFX "PIO initialized\n");
+ err = 0;
+out:
+ return err;
+
+err_destroy2:
+ bcm43xx_destroy_pioqueue(pio->queue2);
+ pio->queue2 = NULL;
+err_destroy1:
+ bcm43xx_destroy_pioqueue(pio->queue1);
+ pio->queue1 = NULL;
+err_destroy0:
+ bcm43xx_destroy_pioqueue(pio->queue0);
+ pio->queue0 = NULL;
+ goto out;
+}
+
+int bcm43xx_pio_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb)
+{
+ struct bcm43xx_pioqueue *queue = bcm43xx_current_pio(bcm)->queue1;
+ struct bcm43xx_pio_txpacket *packet;
+ u16 tmp;
+
+ assert(!queue->tx_suspended);
+ assert(!list_empty(&queue->txfree));
+
+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_TXCTL);
+ if (tmp & BCM43xx_PIO_TXCTL_SUSPEND)
+ return -EBUSY;
+
+ packet = list_entry(queue->txfree.next, struct bcm43xx_pio_txpacket, list);
+ packet->txb = txb;
+ packet->xmitted_frags = 0;
+ packet->xmitted_octets = 0;
+ list_move_tail(&packet->list, &queue->txqueue);
+ queue->nr_txfree--;
+ assert(queue->nr_txfree < BCM43xx_PIO_MAXTXPACKETS);
+
+ /* Suspend TX, if we are out of packets in the "free" queue. */
+ if (unlikely(list_empty(&queue->txfree))) {
+ netif_stop_queue(queue->bcm->net_dev);
+ queue->tx_suspended = 1;
+ }
+
+ tasklet_schedule(&queue->txtask);
+
+ return 0;
+}
+
+void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status)
+{
+ struct bcm43xx_pioqueue *queue;
+ struct bcm43xx_pio_txpacket *packet;
+
+ queue = parse_cookie(bcm, status->cookie, &packet);
+ assert(queue);
+//TODO
+if (!queue)
+return;
+ free_txpacket(packet, 1);
+ if (unlikely(queue->tx_suspended)) {
+ queue->tx_suspended = 0;
+ netif_wake_queue(queue->bcm->net_dev);
+ }
+ /* If there are packets on the txqueue, poke the tasklet. */
+ if (!list_empty(&queue->txqueue))
+ tasklet_schedule(&queue->txtask);
+}
+
+static void pio_rx_error(struct bcm43xx_pioqueue *queue,
+ int clear_buffers,
+ const char *error)
+{
+ int i;
+
+ printkl("PIO RX error: %s\n", error);
+ bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL,
+ BCM43xx_PIO_RXCTL_READY);
+ if (clear_buffers) {
+ assert(queue->mmio_base == BCM43xx_MMIO_PIO1_BASE);
+ for (i = 0; i < 15; i++) {
+ /* Dummy read. */
+ bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
+ }
+ }
+}
+
+void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue)
+{
+ u16 preamble[21] = { 0 };
+ struct bcm43xx_rxhdr *rxhdr;
+ u16 tmp, len, rxflags2;
+ int i, preamble_readwords;
+ struct sk_buff *skb;
+
+return;
+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL);
+ if (!(tmp & BCM43xx_PIO_RXCTL_DATAAVAILABLE)) {
+ dprintkl(KERN_ERR PFX "PIO RX: No data available\n");//TODO: remove this printk.
+ return;
+ }
+ bcm43xx_pio_write(queue, BCM43xx_PIO_RXCTL,
+ BCM43xx_PIO_RXCTL_DATAAVAILABLE);
+
+ for (i = 0; i < 10; i++) {
+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXCTL);
+ if (tmp & BCM43xx_PIO_RXCTL_READY)
+ goto data_ready;
+ udelay(10);
+ }
+ dprintkl(KERN_ERR PFX "PIO RX timed out\n");
+ return;
+data_ready:
+
+//FIXME: endianess in this function.
+ len = le16_to_cpu(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA));
+ if (unlikely(len > 0x700)) {
+ pio_rx_error(queue, 0, "len > 0x700");
+ return;
+ }
+ if (unlikely(len == 0 && queue->mmio_base != BCM43xx_MMIO_PIO4_BASE)) {
+ pio_rx_error(queue, 0, "len == 0");
+ return;
+ }
+ preamble[0] = cpu_to_le16(len);
+ if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE)
+ preamble_readwords = 14 / sizeof(u16);
+ else
+ preamble_readwords = 18 / sizeof(u16);
+ for (i = 0; i < preamble_readwords; i++) {
+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
+ preamble[i + 1] = cpu_to_be16(tmp);//FIXME?
+ }
+ rxhdr = (struct bcm43xx_rxhdr *)preamble;
+ rxflags2 = le16_to_cpu(rxhdr->flags2);
+ if (unlikely(rxflags2 & BCM43xx_RXHDR_FLAGS2_INVALIDFRAME)) {
+ pio_rx_error(queue,
+ (queue->mmio_base == BCM43xx_MMIO_PIO1_BASE),
+ "invalid frame");
+ return;
+ }
+ if (queue->mmio_base == BCM43xx_MMIO_PIO4_BASE) {
+ /* We received an xmit status. */
+ struct bcm43xx_hwxmitstatus *hw;
+ struct bcm43xx_xmitstatus stat;
+
+ hw = (struct bcm43xx_hwxmitstatus *)(preamble + 1);
+ stat.cookie = le16_to_cpu(hw->cookie);
+ stat.flags = hw->flags;
+ stat.cnt1 = hw->cnt1;
+ stat.cnt2 = hw->cnt2;
+ stat.seq = le16_to_cpu(hw->seq);
+ stat.unknown = le16_to_cpu(hw->unknown);
+
+ bcm43xx_debugfs_log_txstat(queue->bcm, &stat);
+ bcm43xx_pio_handle_xmitstatus(queue->bcm, &stat);
+
+ return;
+ }
+
+ skb = dev_alloc_skb(len);
+ if (unlikely(!skb)) {
+ pio_rx_error(queue, 1, "OOM");
+ return;
+ }
+ skb_put(skb, len);
+ for (i = 0; i < len - 1; i += 2) {
+ tmp = cpu_to_be16(bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA));
+ *((u16 *)(skb->data + i)) = tmp;
+ }
+ if (len % 2) {
+ tmp = bcm43xx_pio_read(queue, BCM43xx_PIO_RXDATA);
+ skb->data[len - 1] = (tmp & 0x00FF);
+ if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME)
+ skb->data[0x20] = (tmp & 0xFF00) >> 8;
+ else
+ skb->data[0x1E] = (tmp & 0xFF00) >> 8;
+ }
+ bcm43xx_rx(queue->bcm, skb, rxhdr);
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_pio.h b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h
new file mode 100644
index 000000000000..970627bc1769
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_pio.h
@@ -0,0 +1,138 @@
+#ifndef BCM43xx_PIO_H_
+#define BCM43xx_PIO_H_
+
+#include "bcm43xx.h"
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+
+
+#define BCM43xx_PIO_TXCTL 0x00
+#define BCM43xx_PIO_TXDATA 0x02
+#define BCM43xx_PIO_TXQBUFSIZE 0x04
+#define BCM43xx_PIO_RXCTL 0x08
+#define BCM43xx_PIO_RXDATA 0x0A
+
+#define BCM43xx_PIO_TXCTL_WRITEHI (1 << 0)
+#define BCM43xx_PIO_TXCTL_WRITELO (1 << 1)
+#define BCM43xx_PIO_TXCTL_COMPLETE (1 << 2)
+#define BCM43xx_PIO_TXCTL_INIT (1 << 3)
+#define BCM43xx_PIO_TXCTL_SUSPEND (1 << 7)
+
+#define BCM43xx_PIO_RXCTL_DATAAVAILABLE (1 << 0)
+#define BCM43xx_PIO_RXCTL_READY (1 << 1)
+
+/* PIO constants */
+#define BCM43xx_PIO_MAXTXDEVQPACKETS 31
+#define BCM43xx_PIO_TXQADJUST 80
+
+/* PIO tuning knobs */
+#define BCM43xx_PIO_MAXTXPACKETS 256
+
+
+
+#ifdef CONFIG_BCM43XX_PIO
+
+
+struct bcm43xx_pioqueue;
+struct bcm43xx_xmitstatus;
+
+struct bcm43xx_pio_txpacket {
+ struct bcm43xx_pioqueue *queue;
+ struct ieee80211_txb *txb;
+ struct list_head list;
+
+ u8 xmitted_frags;
+ u16 xmitted_octets;
+};
+
+#define pio_txpacket_getindex(packet) ((int)((packet) - (packet)->queue->tx_packets_cache))
+
+struct bcm43xx_pioqueue {
+ struct bcm43xx_private *bcm;
+ u16 mmio_base;
+
+ u8 tx_suspended:1,
+ need_workarounds:1; /* Workarounds needed for core.rev < 3 */
+
+ /* Adjusted size of the device internal TX buffer. */
+ u16 tx_devq_size;
+ /* Used octets of the device internal TX buffer. */
+ u16 tx_devq_used;
+ /* Used packet slots in the device internal TX buffer. */
+ u8 tx_devq_packets;
+ /* Packets from the txfree list can
+ * be taken on incoming TX requests.
+ */
+ struct list_head txfree;
+ unsigned int nr_txfree;
+ /* Packets on the txqueue are queued,
+ * but not completely written to the chip, yet.
+ */
+ struct list_head txqueue;
+ /* Packets on the txrunning queue are completely
+ * posted to the device. We are waiting for the txstatus.
+ */
+ struct list_head txrunning;
+ /* Total number or packets sent.
+ * (This counter can obviously wrap).
+ */
+ unsigned int nr_tx_packets;
+ struct tasklet_struct txtask;
+ struct bcm43xx_pio_txpacket tx_packets_cache[BCM43xx_PIO_MAXTXPACKETS];
+};
+
+static inline
+u16 bcm43xx_pio_read(struct bcm43xx_pioqueue *queue,
+ u16 offset)
+{
+ return bcm43xx_read16(queue->bcm, queue->mmio_base + offset);
+}
+
+static inline
+void bcm43xx_pio_write(struct bcm43xx_pioqueue *queue,
+ u16 offset, u16 value)
+{
+ bcm43xx_write16(queue->bcm, queue->mmio_base + offset, value);
+}
+
+
+int bcm43xx_pio_init(struct bcm43xx_private *bcm);
+void bcm43xx_pio_free(struct bcm43xx_private *bcm);
+
+int bcm43xx_pio_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb);
+void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status);
+void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue);
+
+#else /* CONFIG_BCM43XX_PIO */
+
+static inline
+int bcm43xx_pio_init(struct bcm43xx_private *bcm)
+{
+ return 0;
+}
+static inline
+void bcm43xx_pio_free(struct bcm43xx_private *bcm)
+{
+}
+static inline
+int bcm43xx_pio_tx(struct bcm43xx_private *bcm,
+ struct ieee80211_txb *txb)
+{
+ return 0;
+}
+static inline
+void bcm43xx_pio_handle_xmitstatus(struct bcm43xx_private *bcm,
+ struct bcm43xx_xmitstatus *status)
+{
+}
+static inline
+void bcm43xx_pio_rx(struct bcm43xx_pioqueue *queue)
+{
+}
+
+#endif /* CONFIG_BCM43XX_PIO */
+#endif /* BCM43xx_PIO_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_power.c b/drivers/net/wireless/bcm43xx/bcm43xx_power.c
new file mode 100644
index 000000000000..3c92b62807c5
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_power.c
@@ -0,0 +1,358 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; 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/delay.h>
+
+#include "bcm43xx.h"
+#include "bcm43xx_power.h"
+#include "bcm43xx_main.h"
+
+
+/* Get max/min slowclock frequency
+ * as described in http://bcm-specs.sipsolutions.net/PowerControl
+ */
+static int bcm43xx_pctl_clockfreqlimit(struct bcm43xx_private *bcm,
+ int get_max)
+{
+ int limit = 0;
+ int divisor;
+ int selection;
+ int err;
+ u32 tmp;
+ struct bcm43xx_coreinfo *old_core;
+
+ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
+ goto out;
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
+ if (err)
+ goto out;
+
+ if (bcm->current_core->rev < 6) {
+ if ((bcm->bustype == BCM43xx_BUSTYPE_PCMCIA) ||
+ (bcm->bustype == BCM43xx_BUSTYPE_SB)) {
+ selection = 1;
+ divisor = 32;
+ } else {
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &tmp);
+ if (err) {
+ printk(KERN_ERR PFX "clockfreqlimit pcicfg read failure\n");
+ goto out_switchback;
+ }
+ if (tmp & 0x10) {
+ /* PCI */
+ selection = 2;
+ divisor = 64;
+ } else {
+ /* XTAL */
+ selection = 1;
+ divisor = 32;
+ }
+ }
+ } else if (bcm->current_core->rev < 10) {
+ selection = (tmp & 0x07);
+ if (selection) {
+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
+ divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16));
+ } else
+ divisor = 1;
+ } else {
+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL);
+ divisor = 4 * (1 + ((tmp & 0xFFFF0000) >> 16));
+ selection = 1;
+ }
+
+ switch (selection) {
+ case 0:
+ /* LPO */
+ if (get_max)
+ limit = 43000;
+ else
+ limit = 25000;
+ break;
+ case 1:
+ /* XTAL */
+ if (get_max)
+ limit = 20200000;
+ else
+ limit = 19800000;
+ break;
+ case 2:
+ /* PCI */
+ if (get_max)
+ limit = 34000000;
+ else
+ limit = 25000000;
+ break;
+ default:
+ assert(0);
+ }
+ limit /= divisor;
+
+out_switchback:
+ err = bcm43xx_switch_core(bcm, old_core);
+ assert(err == 0);
+
+out:
+ return limit;
+}
+
+/* init power control
+ * as described in http://bcm-specs.sipsolutions.net/PowerControl
+ */
+int bcm43xx_pctl_init(struct bcm43xx_private *bcm)
+{
+ int err, maxfreq;
+ struct bcm43xx_coreinfo *old_core;
+
+ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
+ return 0;
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
+ if (err == -ENODEV)
+ return 0;
+ if (err)
+ goto out;
+
+ maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);
+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,
+ (maxfreq * 150 + 999999) / 1000000);
+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,
+ (maxfreq * 15 + 999999) / 1000000);
+
+ err = bcm43xx_switch_core(bcm, old_core);
+ assert(err == 0);
+
+out:
+ return err;
+}
+
+u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm)
+{
+ u16 delay = 0;
+ int err;
+ u32 pll_on_delay;
+ struct bcm43xx_coreinfo *old_core;
+ int minfreq;
+
+ if (bcm->bustype != BCM43xx_BUSTYPE_PCI)
+ goto out;
+ if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
+ goto out;
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
+ if (err == -ENODEV)
+ goto out;
+
+ minfreq = bcm43xx_pctl_clockfreqlimit(bcm, 0);
+ pll_on_delay = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY);
+ delay = (((pll_on_delay + 2) * 1000000) + (minfreq - 1)) / minfreq;
+
+ err = bcm43xx_switch_core(bcm, old_core);
+ assert(err == 0);
+
+out:
+ return delay;
+}
+
+/* set the powercontrol clock
+ * as described in http://bcm-specs.sipsolutions.net/PowerControl
+ */
+int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode)
+{
+ int err;
+ struct bcm43xx_coreinfo *old_core;
+ u32 tmp;
+
+ old_core = bcm->current_core;
+ err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
+ if (err == -ENODEV)
+ return 0;
+ if (err)
+ goto out;
+
+ if (bcm->core_chipcommon.rev < 6) {
+ if (mode == BCM43xx_PCTL_CLK_FAST) {
+ err = bcm43xx_pctl_set_crystal(bcm, 1);
+ if (err)
+ goto out;
+ }
+ } else {
+ if ((bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) &&
+ (bcm->core_chipcommon.rev < 10)) {
+ switch (mode) {
+ case BCM43xx_PCTL_CLK_FAST:
+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
+ tmp = (tmp & ~BCM43xx_PCTL_FORCE_SLOW) | BCM43xx_PCTL_FORCE_PLL;
+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
+ break;
+ case BCM43xx_PCTL_CLK_SLOW:
+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
+ tmp |= BCM43xx_PCTL_FORCE_SLOW;
+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
+ break;
+ case BCM43xx_PCTL_CLK_DYNAMIC:
+ tmp = bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL);
+ tmp &= ~BCM43xx_PCTL_FORCE_SLOW;
+ tmp |= BCM43xx_PCTL_FORCE_PLL;
+ tmp &= ~BCM43xx_PCTL_DYN_XTAL;
+ bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SLOWCLKCTL, tmp);
+ }
+ }
+ }
+
+ err = bcm43xx_switch_core(bcm, old_core);
+ assert(err == 0);
+
+out:
+ return err;
+}
+
+int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on)
+{
+ int err;
+ u32 in, out, outenable;
+
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_IN, &in);
+ if (err)
+ goto err_pci;
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUT, &out);
+ if (err)
+ goto err_pci;
+ err = bcm43xx_pci_read_config32(bcm, BCM43xx_PCTL_OUTENABLE, &outenable);
+ if (err)
+ goto err_pci;
+
+ outenable |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
+
+ if (on) {
+ if (in & 0x40)
+ return 0;
+
+ out |= (BCM43xx_PCTL_XTAL_POWERUP | BCM43xx_PCTL_PLL_POWERDOWN);
+
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
+ if (err)
+ goto err_pci;
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
+ if (err)
+ goto err_pci;
+ udelay(1000);
+
+ out &= ~BCM43xx_PCTL_PLL_POWERDOWN;
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
+ if (err)
+ goto err_pci;
+ udelay(5000);
+ } else {
+ if (bcm->current_core->rev < 5)
+ return 0;
+ if (bcm->sprom.boardflags & BCM43xx_BFL_XTAL_NOSLOW)
+ return 0;
+
+/* XXX: Why BCM43xx_MMIO_RADIO_HWENABLED_xx can't be read at this time?
+ * err = bcm43xx_switch_core(bcm, bcm->active_80211_core);
+ * if (err)
+ * return err;
+ * if (((bcm->current_core->rev >= 3) &&
+ * (bcm43xx_read32(bcm, BCM43xx_MMIO_RADIO_HWENABLED_HI) & (1 << 16))) ||
+ * ((bcm->current_core->rev < 3) &&
+ * !(bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_HWENABLED_LO) & (1 << 4))))
+ * return 0;
+ * err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
+ * if (err)
+ * return err;
+ */
+
+ err = bcm43xx_pctl_set_clock(bcm, BCM43xx_PCTL_CLK_SLOW);
+ if (err)
+ goto out;
+ out &= ~BCM43xx_PCTL_XTAL_POWERUP;
+ out |= BCM43xx_PCTL_PLL_POWERDOWN;
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUT, out);
+ if (err)
+ goto err_pci;
+ err = bcm43xx_pci_write_config32(bcm, BCM43xx_PCTL_OUTENABLE, outenable);
+ if (err)
+ goto err_pci;
+ }
+
+out:
+ return err;
+
+err_pci:
+ printk(KERN_ERR PFX "Error: pctl_set_clock() could not access PCI config space!\n");
+ err = -EBUSY;
+ goto out;
+}
+
+/* Set the PowerSavingControlBits.
+ * Bitvalues:
+ * 0 => unset the bit
+ * 1 => set the bit
+ * -1 => calculate the bit
+ */
+void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,
+ int bit25, int bit26)
+{
+ int i;
+ u32 status;
+
+//FIXME: Force 25 to off and 26 to on for now:
+bit25 = 0;
+bit26 = 1;
+
+ if (bit25 == -1) {
+ //TODO: If powersave is not off and FIXME is not set and we are not in adhoc
+ // and thus is not an AP and we are associated, set bit 25
+ }
+ if (bit26 == -1) {
+ //TODO: If the device is awake or this is an AP, or we are scanning, or FIXME,
+ // or we are associated, or FIXME, or the latest PS-Poll packet sent was
+ // successful, set bit26
+ }
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ if (bit25)
+ status |= BCM43xx_SBF_PS1;
+ else
+ status &= ~BCM43xx_SBF_PS1;
+ if (bit26)
+ status |= BCM43xx_SBF_PS2;
+ else
+ status &= ~BCM43xx_SBF_PS2;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+ if (bit26 && bcm->current_core->rev >= 5) {
+ for (i = 0; i < 100; i++) {
+ if (bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED, 0x0040) != 4)
+ break;
+ udelay(10);
+ }
+ }
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_power.h b/drivers/net/wireless/bcm43xx/bcm43xx_power.h
new file mode 100644
index 000000000000..5f63640810bd
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_power.h
@@ -0,0 +1,47 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef BCM43xx_POWER_H_
+#define BCM43xx_POWER_H_
+
+#include <linux/types.h>
+
+
+struct bcm43xx_private;
+
+int bcm43xx_pctl_init(struct bcm43xx_private *bcm);
+int bcm43xx_pctl_set_clock(struct bcm43xx_private *bcm, u16 mode);
+int bcm43xx_pctl_set_crystal(struct bcm43xx_private *bcm, int on);
+u16 bcm43xx_pctl_powerup_delay(struct bcm43xx_private *bcm);
+
+void bcm43xx_power_saving_ctl_bits(struct bcm43xx_private *bcm,
+ int bit25, int bit26);
+
+#endif /* BCM43xx_POWER_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.c b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
new file mode 100644
index 000000000000..af5c0bff1696
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.c
@@ -0,0 +1,2026 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; 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/delay.h>
+
+#include "bcm43xx.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_phy.h"
+#include "bcm43xx_radio.h"
+#include "bcm43xx_ilt.h"
+
+
+/* Table for bcm43xx_radio_calibrationvalue() */
+static const u16 rcc_table[16] = {
+ 0x0002, 0x0003, 0x0001, 0x000F,
+ 0x0006, 0x0007, 0x0005, 0x000F,
+ 0x000A, 0x000B, 0x0009, 0x000F,
+ 0x000E, 0x000F, 0x000D, 0x000F,
+};
+
+/* Reverse the bits of a 4bit value.
+ * Example: 1101 is flipped 1011
+ */
+static u16 flip_4bit(u16 value)
+{
+ u16 flipped = 0x0000;
+
+ assert((value & ~0x000F) == 0x0000);
+
+ flipped |= (value & 0x0001) << 3;
+ flipped |= (value & 0x0002) << 1;
+ flipped |= (value & 0x0004) >> 1;
+ flipped |= (value & 0x0008) >> 3;
+
+ return flipped;
+}
+
+/* Get the freq, as it has to be written to the device. */
+static inline
+u16 channel2freq_bg(u8 channel)
+{
+ /* Frequencies are given as frequencies_bg[index] + 2.4GHz
+ * Starting with channel 1
+ */
+ static const u16 frequencies_bg[14] = {
+ 12, 17, 22, 27,
+ 32, 37, 42, 47,
+ 52, 57, 62, 67,
+ 72, 84,
+ };
+
+ assert(channel >= 1 && channel <= 14);
+
+ return frequencies_bg[channel - 1];
+}
+
+/* Get the freq, as it has to be written to the device. */
+static inline
+u16 channel2freq_a(u8 channel)
+{
+ assert(channel <= 200);
+
+ return (5000 + 5 * channel);
+}
+
+void bcm43xx_radio_lock(struct bcm43xx_private *bcm)
+{
+ u32 status;
+
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ status |= BCM43xx_SBF_RADIOREG_LOCK;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+ mmiowb();
+ udelay(10);
+}
+
+void bcm43xx_radio_unlock(struct bcm43xx_private *bcm)
+{
+ u32 status;
+
+ bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_VER); /* dummy read */
+ status = bcm43xx_read32(bcm, BCM43xx_MMIO_STATUS_BITFIELD);
+ status &= ~BCM43xx_SBF_RADIOREG_LOCK;
+ bcm43xx_write32(bcm, BCM43xx_MMIO_STATUS_BITFIELD, status);
+ mmiowb();
+}
+
+u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ offset |= 0x0040;
+ break;
+ case BCM43xx_PHYTYPE_B:
+ if (radio->version == 0x2053) {
+ if (offset < 0x70)
+ offset += 0x80;
+ else if (offset < 0x80)
+ offset += 0x70;
+ } else if (radio->version == 0x2050) {
+ offset |= 0x80;
+ } else
+ assert(0);
+ break;
+ case BCM43xx_PHYTYPE_G:
+ offset |= 0x80;
+ break;
+ }
+
+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset);
+ return bcm43xx_read16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW);
+}
+
+void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val)
+{
+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_CONTROL, offset);
+ mmiowb();
+ bcm43xx_write16(bcm, BCM43xx_MMIO_RADIO_DATA_LOW, val);
+}
+
+static void bcm43xx_set_all_gains(struct bcm43xx_private *bcm,
+ s16 first, s16 second, s16 third)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 i;
+ u16 start = 0x08, end = 0x18;
+ u16 offset = 0x0400;
+ u16 tmp;
+
+ if (phy->rev <= 1) {
+ offset = 0x5000;
+ start = 0x10;
+ end = 0x20;
+ }
+
+ for (i = 0; i < 4; i++)
+ bcm43xx_ilt_write(bcm, offset + i, first);
+
+ for (i = start; i < end; i++)
+ bcm43xx_ilt_write(bcm, offset + i, second);
+
+ if (third != -1) {
+ tmp = ((u16)third << 14) | ((u16)third << 6);
+ bcm43xx_phy_write(bcm, 0x04A0,
+ (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | tmp);
+ bcm43xx_phy_write(bcm, 0x04A1,
+ (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | tmp);
+ bcm43xx_phy_write(bcm, 0x04A2,
+ (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | tmp);
+ }
+ bcm43xx_dummy_transmission(bcm);
+}
+
+static void bcm43xx_set_original_gains(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 i, tmp;
+ u16 offset = 0x0400;
+ u16 start = 0x0008, end = 0x0018;
+
+ if (phy->rev <= 1) {
+ offset = 0x5000;
+ start = 0x0010;
+ end = 0x0020;
+ }
+
+ for (i = 0; i < 4; i++) {
+ tmp = (i & 0xFFFC);
+ tmp |= (i & 0x0001) << 1;
+ tmp |= (i & 0x0002) >> 1;
+
+ bcm43xx_ilt_write(bcm, offset + i, tmp);
+ }
+
+ for (i = start; i < end; i++)
+ bcm43xx_ilt_write(bcm, offset + i, i - start);
+
+ bcm43xx_phy_write(bcm, 0x04A0,
+ (bcm43xx_phy_read(bcm, 0x04A0) & 0xBFBF) | 0x4040);
+ bcm43xx_phy_write(bcm, 0x04A1,
+ (bcm43xx_phy_read(bcm, 0x04A1) & 0xBFBF) | 0x4040);
+ bcm43xx_phy_write(bcm, 0x04A2,
+ (bcm43xx_phy_read(bcm, 0x04A2) & 0xBFBF) | 0x4000);
+ bcm43xx_dummy_transmission(bcm);
+}
+
+/* Synthetic PU workaround */
+static void bcm43xx_synth_pu_workaround(struct bcm43xx_private *bcm, u8 channel)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ if (radio->version != 0x2050 || radio->revision >= 6) {
+ /* We do not need the workaround. */
+ return;
+ }
+
+ if (channel <= 10) {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
+ channel2freq_bg(channel + 4));
+ } else {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
+ channel2freq_bg(1));
+ }
+ udelay(100);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
+ channel2freq_bg(channel));
+}
+
+u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u8 ret = 0;
+ u16 saved, rssi, temp;
+ int i, j = 0;
+
+ saved = bcm43xx_phy_read(bcm, 0x0403);
+ bcm43xx_radio_selectchannel(bcm, channel, 0);
+ bcm43xx_phy_write(bcm, 0x0403, (saved & 0xFFF8) | 5);
+ if (radio->aci_hw_rssi)
+ rssi = bcm43xx_phy_read(bcm, 0x048A) & 0x3F;
+ else
+ rssi = saved & 0x3F;
+ /* clamp temp to signed 5bit */
+ if (rssi > 32)
+ rssi -= 64;
+ for (i = 0;i < 100; i++) {
+ temp = (bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x3F;
+ if (temp > 32)
+ temp -= 64;
+ if (temp < rssi)
+ j++;
+ if (j >= 20)
+ ret = 1;
+ }
+ bcm43xx_phy_write(bcm, 0x0403, saved);
+
+ return ret;
+}
+
+u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u8 ret[13];
+ unsigned int channel = radio->channel;
+ unsigned int i, j, start, end;
+ unsigned long phylock_flags;
+
+ if (!((phy->type == BCM43xx_PHYTYPE_G) && (phy->rev > 0)))
+ return 0;
+
+ bcm43xx_phy_lock(bcm, phylock_flags);
+ bcm43xx_radio_lock(bcm);
+ bcm43xx_phy_write(bcm, 0x0802,
+ bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF);
+ bcm43xx_set_all_gains(bcm, 3, 8, 1);
+
+ start = (channel - 5 > 0) ? channel - 5 : 1;
+ end = (channel + 5 < 14) ? channel + 5 : 13;
+
+ for (i = start; i <= end; i++) {
+ if (abs(channel - i) > 2)
+ ret[i-1] = bcm43xx_radio_aci_detect(bcm, i);
+ }
+ bcm43xx_radio_selectchannel(bcm, channel, 0);
+ bcm43xx_phy_write(bcm, 0x0802,
+ (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC) | 0x0003);
+ bcm43xx_phy_write(bcm, 0x0403,
+ bcm43xx_phy_read(bcm, 0x0403) & 0xFFF8);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000);
+ bcm43xx_set_original_gains(bcm);
+ for (i = 0; i < 13; i++) {
+ if (!ret[i])
+ continue;
+ end = (i + 5 < 13) ? i + 5 : 13;
+ for (j = i; j < end; j++)
+ ret[j] = 1;
+ }
+ bcm43xx_radio_unlock(bcm);
+ bcm43xx_phy_unlock(bcm, phylock_flags);
+
+ return ret[channel - 1];
+}
+
+/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
+void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val)
+{
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset);
+ mmiowb();
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_DATA, (u16)val);
+}
+
+/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
+s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset)
+{
+ u16 val;
+
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_NRSSILT_CTRL, offset);
+ val = bcm43xx_phy_read(bcm, BCM43xx_PHY_NRSSILT_DATA);
+
+ return (s16)val;
+}
+
+/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
+void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val)
+{
+ u16 i;
+ s16 tmp;
+
+ for (i = 0; i < 64; i++) {
+ tmp = bcm43xx_nrssi_hw_read(bcm, i);
+ tmp -= val;
+ tmp = limit_value(tmp, -32, 31);
+ bcm43xx_nrssi_hw_write(bcm, i, tmp);
+ }
+}
+
+/* http://bcm-specs.sipsolutions.net/NRSSILookupTable */
+void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ s16 i, delta;
+ s32 tmp;
+
+ delta = 0x1F - radio->nrssi[0];
+ for (i = 0; i < 64; i++) {
+ tmp = (i - delta) * radio->nrssislope;
+ tmp /= 0x10000;
+ tmp += 0x3A;
+ tmp = limit_value(tmp, 0, 0x3F);
+ radio->nrssi_lt[i] = tmp;
+ }
+}
+
+static void bcm43xx_calc_nrssi_offset(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ u16 backup[20] = { 0 };
+ s16 v47F;
+ u16 i;
+ u16 saved = 0xFFFF;
+
+ backup[0] = bcm43xx_phy_read(bcm, 0x0001);
+ backup[1] = bcm43xx_phy_read(bcm, 0x0811);
+ backup[2] = bcm43xx_phy_read(bcm, 0x0812);
+ backup[3] = bcm43xx_phy_read(bcm, 0x0814);
+ backup[4] = bcm43xx_phy_read(bcm, 0x0815);
+ backup[5] = bcm43xx_phy_read(bcm, 0x005A);
+ backup[6] = bcm43xx_phy_read(bcm, 0x0059);
+ backup[7] = bcm43xx_phy_read(bcm, 0x0058);
+ backup[8] = bcm43xx_phy_read(bcm, 0x000A);
+ backup[9] = bcm43xx_phy_read(bcm, 0x0003);
+ backup[10] = bcm43xx_radio_read16(bcm, 0x007A);
+ backup[11] = bcm43xx_radio_read16(bcm, 0x0043);
+
+ bcm43xx_phy_write(bcm, 0x0429,
+ bcm43xx_phy_read(bcm, 0x0429) & 0x7FFF);
+ bcm43xx_phy_write(bcm, 0x0001,
+ (bcm43xx_phy_read(bcm, 0x0001) & 0x3FFF) | 0x4000);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
+ bcm43xx_phy_write(bcm, 0x0812,
+ (bcm43xx_phy_read(bcm, 0x0812) & 0xFFF3) | 0x0004);
+ bcm43xx_phy_write(bcm, 0x0802,
+ bcm43xx_phy_read(bcm, 0x0802) & ~(0x1 | 0x2));
+ if (phy->rev >= 6) {
+ backup[12] = bcm43xx_phy_read(bcm, 0x002E);
+ backup[13] = bcm43xx_phy_read(bcm, 0x002F);
+ backup[14] = bcm43xx_phy_read(bcm, 0x080F);
+ backup[15] = bcm43xx_phy_read(bcm, 0x0810);
+ backup[16] = bcm43xx_phy_read(bcm, 0x0801);
+ backup[17] = bcm43xx_phy_read(bcm, 0x0060);
+ backup[18] = bcm43xx_phy_read(bcm, 0x0014);
+ backup[19] = bcm43xx_phy_read(bcm, 0x0478);
+
+ bcm43xx_phy_write(bcm, 0x002E, 0);
+ bcm43xx_phy_write(bcm, 0x002F, 0);
+ bcm43xx_phy_write(bcm, 0x080F, 0);
+ bcm43xx_phy_write(bcm, 0x0810, 0);
+ bcm43xx_phy_write(bcm, 0x0478,
+ bcm43xx_phy_read(bcm, 0x0478) | 0x0100);
+ bcm43xx_phy_write(bcm, 0x0801,
+ bcm43xx_phy_read(bcm, 0x0801) | 0x0040);
+ bcm43xx_phy_write(bcm, 0x0060,
+ bcm43xx_phy_read(bcm, 0x0060) | 0x0040);
+ bcm43xx_phy_write(bcm, 0x0014,
+ bcm43xx_phy_read(bcm, 0x0014) | 0x0200);
+ }
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x0070);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);
+ udelay(30);
+
+ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
+ if (v47F >= 0x20)
+ v47F -= 0x40;
+ if (v47F == 31) {
+ for (i = 7; i >= 4; i--) {
+ bcm43xx_radio_write16(bcm, 0x007B, i);
+ udelay(20);
+ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
+ if (v47F >= 0x20)
+ v47F -= 0x40;
+ if (v47F < 31 && saved == 0xFFFF)
+ saved = i;
+ }
+ if (saved == 0xFFFF)
+ saved = 4;
+ } else {
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);
+ bcm43xx_phy_write(bcm, 0x0814,
+ bcm43xx_phy_read(bcm, 0x0814) | 0x0001);
+ bcm43xx_phy_write(bcm, 0x0815,
+ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFE);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x000C);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) | 0x000C);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) | 0x0030);
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) | 0x0030);
+ bcm43xx_phy_write(bcm, 0x005A, 0x0480);
+ bcm43xx_phy_write(bcm, 0x0059, 0x0810);
+ bcm43xx_phy_write(bcm, 0x0058, 0x000D);
+ if (phy->rev == 0) {
+ bcm43xx_phy_write(bcm, 0x0003, 0x0122);
+ } else {
+ bcm43xx_phy_write(bcm, 0x000A,
+ bcm43xx_phy_read(bcm, 0x000A)
+ | 0x2000);
+ }
+ bcm43xx_phy_write(bcm, 0x0814,
+ bcm43xx_phy_read(bcm, 0x0814) | 0x0004);
+ bcm43xx_phy_write(bcm, 0x0815,
+ bcm43xx_phy_read(bcm, 0x0815) & 0xFFFB);
+ bcm43xx_phy_write(bcm, 0x0003,
+ (bcm43xx_phy_read(bcm, 0x0003) & 0xFF9F)
+ | 0x0040);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x000F);
+ bcm43xx_set_all_gains(bcm, 3, 0, 1);
+ bcm43xx_radio_write16(bcm, 0x0043,
+ (bcm43xx_radio_read16(bcm, 0x0043)
+ & 0x00F0) | 0x000F);
+ udelay(30);
+ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
+ if (v47F >= 0x20)
+ v47F -= 0x40;
+ if (v47F == -32) {
+ for (i = 0; i < 4; i++) {
+ bcm43xx_radio_write16(bcm, 0x007B, i);
+ udelay(20);
+ v47F = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
+ if (v47F >= 0x20)
+ v47F -= 0x40;
+ if (v47F > -31 && saved == 0xFFFF)
+ saved = i;
+ }
+ if (saved == 0xFFFF)
+ saved = 3;
+ } else
+ saved = 0;
+ }
+ bcm43xx_radio_write16(bcm, 0x007B, saved);
+
+ if (phy->rev >= 6) {
+ bcm43xx_phy_write(bcm, 0x002E, backup[12]);
+ bcm43xx_phy_write(bcm, 0x002F, backup[13]);
+ bcm43xx_phy_write(bcm, 0x080F, backup[14]);
+ bcm43xx_phy_write(bcm, 0x0810, backup[15]);
+ }
+ bcm43xx_phy_write(bcm, 0x0814, backup[3]);
+ bcm43xx_phy_write(bcm, 0x0815, backup[4]);
+ bcm43xx_phy_write(bcm, 0x005A, backup[5]);
+ bcm43xx_phy_write(bcm, 0x0059, backup[6]);
+ bcm43xx_phy_write(bcm, 0x0058, backup[7]);
+ bcm43xx_phy_write(bcm, 0x000A, backup[8]);
+ bcm43xx_phy_write(bcm, 0x0003, backup[9]);
+ bcm43xx_radio_write16(bcm, 0x0043, backup[11]);
+ bcm43xx_radio_write16(bcm, 0x007A, backup[10]);
+ bcm43xx_phy_write(bcm, 0x0802,
+ bcm43xx_phy_read(bcm, 0x0802) | 0x1 | 0x2);
+ bcm43xx_phy_write(bcm, 0x0429,
+ bcm43xx_phy_read(bcm, 0x0429) | 0x8000);
+ bcm43xx_set_original_gains(bcm);
+ if (phy->rev >= 6) {
+ bcm43xx_phy_write(bcm, 0x0801, backup[16]);
+ bcm43xx_phy_write(bcm, 0x0060, backup[17]);
+ bcm43xx_phy_write(bcm, 0x0014, backup[18]);
+ bcm43xx_phy_write(bcm, 0x0478, backup[19]);
+ }
+ bcm43xx_phy_write(bcm, 0x0001, backup[0]);
+ bcm43xx_phy_write(bcm, 0x0812, backup[2]);
+ bcm43xx_phy_write(bcm, 0x0811, backup[1]);
+}
+
+void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 backup[18] = { 0 };
+ u16 tmp;
+ s16 nrssi0, nrssi1;
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_B:
+ backup[0] = bcm43xx_radio_read16(bcm, 0x007A);
+ backup[1] = bcm43xx_radio_read16(bcm, 0x0052);
+ backup[2] = bcm43xx_radio_read16(bcm, 0x0043);
+ backup[3] = bcm43xx_phy_read(bcm, 0x0030);
+ backup[4] = bcm43xx_phy_read(bcm, 0x0026);
+ backup[5] = bcm43xx_phy_read(bcm, 0x0015);
+ backup[6] = bcm43xx_phy_read(bcm, 0x002A);
+ backup[7] = bcm43xx_phy_read(bcm, 0x0020);
+ backup[8] = bcm43xx_phy_read(bcm, 0x005A);
+ backup[9] = bcm43xx_phy_read(bcm, 0x0059);
+ backup[10] = bcm43xx_phy_read(bcm, 0x0058);
+ backup[11] = bcm43xx_read16(bcm, 0x03E2);
+ backup[12] = bcm43xx_read16(bcm, 0x03E6);
+ backup[13] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
+
+ tmp = bcm43xx_radio_read16(bcm, 0x007A);
+ tmp &= (phy->rev >= 5) ? 0x007F : 0x000F;
+ bcm43xx_radio_write16(bcm, 0x007A, tmp);
+ bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
+ bcm43xx_write16(bcm, 0x03EC, 0x7F7F);
+ bcm43xx_phy_write(bcm, 0x0026, 0x0000);
+ bcm43xx_phy_write(bcm, 0x0015,
+ bcm43xx_phy_read(bcm, 0x0015) | 0x0020);
+ bcm43xx_phy_write(bcm, 0x002A, 0x08A3);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);
+
+ nrssi0 = (s16)bcm43xx_phy_read(bcm, 0x0027);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);
+ if (phy->rev >= 2) {
+ bcm43xx_write16(bcm, 0x03E6, 0x0040);
+ } else if (phy->rev == 0) {
+ bcm43xx_write16(bcm, 0x03E6, 0x0122);
+ } else {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) & 0x2000);
+ }
+ bcm43xx_phy_write(bcm, 0x0020, 0x3F3F);
+ bcm43xx_phy_write(bcm, 0x0015, 0xF330);
+ bcm43xx_radio_write16(bcm, 0x005A, 0x0060);
+ bcm43xx_radio_write16(bcm, 0x0043,
+ bcm43xx_radio_read16(bcm, 0x0043) & 0x00F0);
+ bcm43xx_phy_write(bcm, 0x005A, 0x0480);
+ bcm43xx_phy_write(bcm, 0x0059, 0x0810);
+ bcm43xx_phy_write(bcm, 0x0058, 0x000D);
+ udelay(20);
+
+ nrssi1 = (s16)bcm43xx_phy_read(bcm, 0x0027);
+ bcm43xx_phy_write(bcm, 0x0030, backup[3]);
+ bcm43xx_radio_write16(bcm, 0x007A, backup[0]);
+ bcm43xx_write16(bcm, 0x03E2, backup[11]);
+ bcm43xx_phy_write(bcm, 0x0026, backup[4]);
+ bcm43xx_phy_write(bcm, 0x0015, backup[5]);
+ bcm43xx_phy_write(bcm, 0x002A, backup[6]);
+ bcm43xx_synth_pu_workaround(bcm, radio->channel);
+ if (phy->rev != 0)
+ bcm43xx_write16(bcm, 0x03F4, backup[13]);
+
+ bcm43xx_phy_write(bcm, 0x0020, backup[7]);
+ bcm43xx_phy_write(bcm, 0x005A, backup[8]);
+ bcm43xx_phy_write(bcm, 0x0059, backup[9]);
+ bcm43xx_phy_write(bcm, 0x0058, backup[10]);
+ bcm43xx_radio_write16(bcm, 0x0052, backup[1]);
+ bcm43xx_radio_write16(bcm, 0x0043, backup[2]);
+
+ if (nrssi0 == nrssi1)
+ radio->nrssislope = 0x00010000;
+ else
+ radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
+
+ if (nrssi0 <= -4) {
+ radio->nrssi[0] = nrssi0;
+ radio->nrssi[1] = nrssi1;
+ }
+ break;
+ case BCM43xx_PHYTYPE_G:
+ if (radio->revision >= 9)
+ return;
+ if (radio->revision == 8)
+ bcm43xx_calc_nrssi_offset(bcm);
+
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF);
+ bcm43xx_phy_write(bcm, 0x0802,
+ bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC);
+ backup[7] = bcm43xx_read16(bcm, 0x03E2);
+ bcm43xx_write16(bcm, 0x03E2,
+ bcm43xx_read16(bcm, 0x03E2) | 0x8000);
+ backup[0] = bcm43xx_radio_read16(bcm, 0x007A);
+ backup[1] = bcm43xx_radio_read16(bcm, 0x0052);
+ backup[2] = bcm43xx_radio_read16(bcm, 0x0043);
+ backup[3] = bcm43xx_phy_read(bcm, 0x0015);
+ backup[4] = bcm43xx_phy_read(bcm, 0x005A);
+ backup[5] = bcm43xx_phy_read(bcm, 0x0059);
+ backup[6] = bcm43xx_phy_read(bcm, 0x0058);
+ backup[8] = bcm43xx_read16(bcm, 0x03E6);
+ backup[9] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
+ if (phy->rev >= 3) {
+ backup[10] = bcm43xx_phy_read(bcm, 0x002E);
+ backup[11] = bcm43xx_phy_read(bcm, 0x002F);
+ backup[12] = bcm43xx_phy_read(bcm, 0x080F);
+ backup[13] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_LO_CONTROL);
+ backup[14] = bcm43xx_phy_read(bcm, 0x0801);
+ backup[15] = bcm43xx_phy_read(bcm, 0x0060);
+ backup[16] = bcm43xx_phy_read(bcm, 0x0014);
+ backup[17] = bcm43xx_phy_read(bcm, 0x0478);
+ bcm43xx_phy_write(bcm, 0x002E, 0);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, 0);
+ switch (phy->rev) {
+ case 4: case 6: case 7:
+ bcm43xx_phy_write(bcm, 0x0478,
+ bcm43xx_phy_read(bcm, 0x0478)
+ | 0x0100);
+ bcm43xx_phy_write(bcm, 0x0801,
+ bcm43xx_phy_read(bcm, 0x0801)
+ | 0x0040);
+ break;
+ case 3: case 5:
+ bcm43xx_phy_write(bcm, 0x0801,
+ bcm43xx_phy_read(bcm, 0x0801)
+ & 0xFFBF);
+ break;
+ }
+ bcm43xx_phy_write(bcm, 0x0060,
+ bcm43xx_phy_read(bcm, 0x0060)
+ | 0x0040);
+ bcm43xx_phy_write(bcm, 0x0014,
+ bcm43xx_phy_read(bcm, 0x0014)
+ | 0x0200);
+ }
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x0070);
+ bcm43xx_set_all_gains(bcm, 0, 8, 0);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) & 0x00F7);
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x0811,
+ (bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0030);
+ bcm43xx_phy_write(bcm, 0x0812,
+ (bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0010);
+ }
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x0080);
+ udelay(20);
+
+ nrssi0 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
+ if (nrssi0 >= 0x0020)
+ nrssi0 -= 0x0040;
+
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) & 0x007F);
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x0003,
+ (bcm43xx_phy_read(bcm, 0x0003)
+ & 0xFF9F) | 0x0040);
+ }
+
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
+ | 0x2000);
+ bcm43xx_radio_write16(bcm, 0x007A,
+ bcm43xx_radio_read16(bcm, 0x007A) | 0x000F);
+ bcm43xx_phy_write(bcm, 0x0015, 0xF330);
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x0812,
+ (bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF) | 0x0020);
+ bcm43xx_phy_write(bcm, 0x0811,
+ (bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF) | 0x0020);
+ }
+
+ bcm43xx_set_all_gains(bcm, 3, 0, 1);
+ if (radio->revision == 8) {
+ bcm43xx_radio_write16(bcm, 0x0043, 0x001F);
+ } else {
+ tmp = bcm43xx_radio_read16(bcm, 0x0052) & 0xFF0F;
+ bcm43xx_radio_write16(bcm, 0x0052, tmp | 0x0060);
+ tmp = bcm43xx_radio_read16(bcm, 0x0043) & 0xFFF0;
+ bcm43xx_radio_write16(bcm, 0x0043, tmp | 0x0009);
+ }
+ bcm43xx_phy_write(bcm, 0x005A, 0x0480);
+ bcm43xx_phy_write(bcm, 0x0059, 0x0810);
+ bcm43xx_phy_write(bcm, 0x0058, 0x000D);
+ udelay(20);
+ nrssi1 = (s16)((bcm43xx_phy_read(bcm, 0x047F) >> 8) & 0x003F);
+ if (nrssi1 >= 0x0020)
+ nrssi1 -= 0x0040;
+ if (nrssi0 == nrssi1)
+ radio->nrssislope = 0x00010000;
+ else
+ radio->nrssislope = 0x00400000 / (nrssi0 - nrssi1);
+ if (nrssi0 >= -4) {
+ radio->nrssi[0] = nrssi1;
+ radio->nrssi[1] = nrssi0;
+ }
+ if (phy->rev >= 3) {
+ bcm43xx_phy_write(bcm, 0x002E, backup[10]);
+ bcm43xx_phy_write(bcm, 0x002F, backup[11]);
+ bcm43xx_phy_write(bcm, 0x080F, backup[12]);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_LO_CONTROL, backup[13]);
+ }
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x0812,
+ bcm43xx_phy_read(bcm, 0x0812) & 0xFFCF);
+ bcm43xx_phy_write(bcm, 0x0811,
+ bcm43xx_phy_read(bcm, 0x0811) & 0xFFCF);
+ }
+
+ bcm43xx_radio_write16(bcm, 0x007A, backup[0]);
+ bcm43xx_radio_write16(bcm, 0x0052, backup[1]);
+ bcm43xx_radio_write16(bcm, 0x0043, backup[2]);
+ bcm43xx_write16(bcm, 0x03E2, backup[7]);
+ bcm43xx_write16(bcm, 0x03E6, backup[8]);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[9]);
+ bcm43xx_phy_write(bcm, 0x0015, backup[3]);
+ bcm43xx_phy_write(bcm, 0x005A, backup[4]);
+ bcm43xx_phy_write(bcm, 0x0059, backup[5]);
+ bcm43xx_phy_write(bcm, 0x0058, backup[6]);
+ bcm43xx_synth_pu_workaround(bcm, radio->channel);
+ bcm43xx_phy_write(bcm, 0x0802,
+ bcm43xx_phy_read(bcm, 0x0802) | (0x0001 | 0x0002));
+ bcm43xx_set_original_gains(bcm);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x8000);
+ if (phy->rev >= 3) {
+ bcm43xx_phy_write(bcm, 0x0801, backup[14]);
+ bcm43xx_phy_write(bcm, 0x0060, backup[15]);
+ bcm43xx_phy_write(bcm, 0x0014, backup[16]);
+ bcm43xx_phy_write(bcm, 0x0478, backup[17]);
+ }
+ bcm43xx_nrssi_mem_update(bcm);
+ bcm43xx_calc_nrssi_threshold(bcm);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ s32 threshold;
+ s32 a, b;
+ s16 tmp16;
+ u16 tmp_u16;
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_B: {
+ if (radio->version != 0x2050)
+ return;
+ if (!(bcm->sprom.boardflags & BCM43xx_BFL_RSSI))
+ return;
+
+ if (radio->revision >= 6) {
+ threshold = (radio->nrssi[1] - radio->nrssi[0]) * 32;
+ threshold += 20 * (radio->nrssi[0] + 1);
+ threshold /= 40;
+ } else
+ threshold = radio->nrssi[1] - 5;
+
+ threshold = limit_value(threshold, 0, 0x3E);
+ bcm43xx_phy_read(bcm, 0x0020); /* dummy read */
+ bcm43xx_phy_write(bcm, 0x0020, (((u16)threshold) << 8) | 0x001C);
+
+ if (radio->revision >= 6) {
+ bcm43xx_phy_write(bcm, 0x0087, 0x0E0D);
+ bcm43xx_phy_write(bcm, 0x0086, 0x0C0B);
+ bcm43xx_phy_write(bcm, 0x0085, 0x0A09);
+ bcm43xx_phy_write(bcm, 0x0084, 0x0808);
+ bcm43xx_phy_write(bcm, 0x0083, 0x0808);
+ bcm43xx_phy_write(bcm, 0x0082, 0x0604);
+ bcm43xx_phy_write(bcm, 0x0081, 0x0302);
+ bcm43xx_phy_write(bcm, 0x0080, 0x0100);
+ }
+ break;
+ }
+ case BCM43xx_PHYTYPE_G:
+ if (!phy->connected ||
+ !(bcm->sprom.boardflags & BCM43xx_BFL_RSSI)) {
+ tmp16 = bcm43xx_nrssi_hw_read(bcm, 0x20);
+ if (tmp16 >= 0x20)
+ tmp16 -= 0x40;
+ if (tmp16 < 3) {
+ bcm43xx_phy_write(bcm, 0x048A,
+ (bcm43xx_phy_read(bcm, 0x048A)
+ & 0xF000) | 0x09EB);
+ } else {
+ bcm43xx_phy_write(bcm, 0x048A,
+ (bcm43xx_phy_read(bcm, 0x048A)
+ & 0xF000) | 0x0AED);
+ }
+ } else {
+ if (radio->interfmode == BCM43xx_RADIO_INTERFMODE_NONWLAN) {
+ a = 0xE;
+ b = 0xA;
+ } else if (!radio->aci_wlan_automatic && radio->aci_enable) {
+ a = 0x13;
+ b = 0x12;
+ } else {
+ a = 0xE;
+ b = 0x11;
+ }
+
+ a = a * (radio->nrssi[1] - radio->nrssi[0]);
+ a += (radio->nrssi[0] << 6);
+ if (a < 32)
+ a += 31;
+ else
+ a += 32;
+ a = a >> 6;
+ a = limit_value(a, -31, 31);
+
+ b = b * (radio->nrssi[1] - radio->nrssi[0]);
+ b += (radio->nrssi[0] << 6);
+ if (b < 32)
+ b += 31;
+ else
+ b += 32;
+ b = b >> 6;
+ b = limit_value(b, -31, 31);
+
+ tmp_u16 = bcm43xx_phy_read(bcm, 0x048A) & 0xF000;
+ tmp_u16 |= ((u32)b & 0x0000003F);
+ tmp_u16 |= (((u32)a & 0x0000003F) << 6);
+ bcm43xx_phy_write(bcm, 0x048A, tmp_u16);
+ }
+ break;
+ default:
+ assert(0);
+ }
+}
+
+/* Stack implementation to save/restore values from the
+ * interference mitigation code.
+ * It is save to restore values in random order.
+ */
+static void _stack_save(u32 *_stackptr, size_t *stackidx,
+ u8 id, u16 offset, u16 value)
+{
+ u32 *stackptr = &(_stackptr[*stackidx]);
+
+ assert((offset & 0xF000) == 0x0000);
+ assert((id & 0xF0) == 0x00);
+ *stackptr = offset;
+ *stackptr |= ((u32)id) << 12;
+ *stackptr |= ((u32)value) << 16;
+ (*stackidx)++;
+ assert(*stackidx < BCM43xx_INTERFSTACK_SIZE);
+}
+
+static u16 _stack_restore(u32 *stackptr,
+ u8 id, u16 offset)
+{
+ size_t i;
+
+ assert((offset & 0xF000) == 0x0000);
+ assert((id & 0xF0) == 0x00);
+ for (i = 0; i < BCM43xx_INTERFSTACK_SIZE; i++, stackptr++) {
+ if ((*stackptr & 0x00000FFF) != offset)
+ continue;
+ if (((*stackptr & 0x0000F000) >> 12) != id)
+ continue;
+ return ((*stackptr & 0xFFFF0000) >> 16);
+ }
+ assert(0);
+
+ return 0;
+}
+
+#define phy_stacksave(offset) \
+ do { \
+ _stack_save(stack, &stackidx, 0x1, (offset), \
+ bcm43xx_phy_read(bcm, (offset))); \
+ } while (0)
+#define phy_stackrestore(offset) \
+ do { \
+ bcm43xx_phy_write(bcm, (offset), \
+ _stack_restore(stack, 0x1, \
+ (offset))); \
+ } while (0)
+#define radio_stacksave(offset) \
+ do { \
+ _stack_save(stack, &stackidx, 0x2, (offset), \
+ bcm43xx_radio_read16(bcm, (offset))); \
+ } while (0)
+#define radio_stackrestore(offset) \
+ do { \
+ bcm43xx_radio_write16(bcm, (offset), \
+ _stack_restore(stack, 0x2, \
+ (offset))); \
+ } while (0)
+#define ilt_stacksave(offset) \
+ do { \
+ _stack_save(stack, &stackidx, 0x3, (offset), \
+ bcm43xx_ilt_read(bcm, (offset))); \
+ } while (0)
+#define ilt_stackrestore(offset) \
+ do { \
+ bcm43xx_ilt_write(bcm, (offset), \
+ _stack_restore(stack, 0x3, \
+ (offset))); \
+ } while (0)
+
+static void
+bcm43xx_radio_interference_mitigation_enable(struct bcm43xx_private *bcm,
+ int mode)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 tmp, flipped;
+ u32 tmp32;
+ size_t stackidx = 0;
+ u32 *stack = radio->interfstack;
+
+ switch (mode) {
+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:
+ if (phy->rev != 1) {
+ bcm43xx_phy_write(bcm, 0x042B,
+ bcm43xx_phy_read(bcm, 0x042B) | 0x0800);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & ~0x4000);
+ break;
+ }
+ radio_stacksave(0x0078);
+ tmp = (bcm43xx_radio_read16(bcm, 0x0078) & 0x001E);
+ flipped = flip_4bit(tmp);
+ if (flipped < 10 && flipped >= 8)
+ flipped = 7;
+ else if (flipped >= 10)
+ flipped -= 3;
+ flipped = flip_4bit(flipped);
+ flipped = (flipped << 1) | 0x0020;
+ bcm43xx_radio_write16(bcm, 0x0078, flipped);
+
+ bcm43xx_calc_nrssi_threshold(bcm);
+
+ phy_stacksave(0x0406);
+ bcm43xx_phy_write(bcm, 0x0406, 0x7E28);
+
+ bcm43xx_phy_write(bcm, 0x042B,
+ bcm43xx_phy_read(bcm, 0x042B) | 0x0800);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD) | 0x1000);
+
+ phy_stacksave(0x04A0);
+ bcm43xx_phy_write(bcm, 0x04A0,
+ (bcm43xx_phy_read(bcm, 0x04A0) & 0xC0C0) | 0x0008);
+ phy_stacksave(0x04A1);
+ bcm43xx_phy_write(bcm, 0x04A1,
+ (bcm43xx_phy_read(bcm, 0x04A1) & 0xC0C0) | 0x0605);
+ phy_stacksave(0x04A2);
+ bcm43xx_phy_write(bcm, 0x04A2,
+ (bcm43xx_phy_read(bcm, 0x04A2) & 0xC0C0) | 0x0204);
+ phy_stacksave(0x04A8);
+ bcm43xx_phy_write(bcm, 0x04A8,
+ (bcm43xx_phy_read(bcm, 0x04A8) & 0xC0C0) | 0x0803);
+ phy_stacksave(0x04AB);
+ bcm43xx_phy_write(bcm, 0x04AB,
+ (bcm43xx_phy_read(bcm, 0x04AB) & 0xC0C0) | 0x0605);
+
+ phy_stacksave(0x04A7);
+ bcm43xx_phy_write(bcm, 0x04A7, 0x0002);
+ phy_stacksave(0x04A3);
+ bcm43xx_phy_write(bcm, 0x04A3, 0x287A);
+ phy_stacksave(0x04A9);
+ bcm43xx_phy_write(bcm, 0x04A9, 0x2027);
+ phy_stacksave(0x0493);
+ bcm43xx_phy_write(bcm, 0x0493, 0x32F5);
+ phy_stacksave(0x04AA);
+ bcm43xx_phy_write(bcm, 0x04AA, 0x2027);
+ phy_stacksave(0x04AC);
+ bcm43xx_phy_write(bcm, 0x04AC, 0x32F5);
+ break;
+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
+ if (bcm43xx_phy_read(bcm, 0x0033) & 0x0800)
+ break;
+
+ radio->aci_enable = 1;
+
+ phy_stacksave(BCM43xx_PHY_RADIO_BITFIELD);
+ phy_stacksave(BCM43xx_PHY_G_CRS);
+ if (phy->rev < 2) {
+ phy_stacksave(0x0406);
+ } else {
+ phy_stacksave(0x04C0);
+ phy_stacksave(0x04C1);
+ }
+ phy_stacksave(0x0033);
+ phy_stacksave(0x04A7);
+ phy_stacksave(0x04A3);
+ phy_stacksave(0x04A9);
+ phy_stacksave(0x04AA);
+ phy_stacksave(0x04AC);
+ phy_stacksave(0x0493);
+ phy_stacksave(0x04A1);
+ phy_stacksave(0x04A0);
+ phy_stacksave(0x04A2);
+ phy_stacksave(0x048A);
+ phy_stacksave(0x04A8);
+ phy_stacksave(0x04AB);
+ if (phy->rev == 2) {
+ phy_stacksave(0x04AD);
+ phy_stacksave(0x04AE);
+ } else if (phy->rev >= 3) {
+ phy_stacksave(0x04AD);
+ phy_stacksave(0x0415);
+ phy_stacksave(0x0416);
+ phy_stacksave(0x0417);
+ ilt_stacksave(0x1A00 + 0x2);
+ ilt_stacksave(0x1A00 + 0x3);
+ }
+ phy_stacksave(0x042B);
+ phy_stacksave(0x048C);
+
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD)
+ & ~0x1000);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS)
+ & 0xFFFC) | 0x0002);
+
+ bcm43xx_phy_write(bcm, 0x0033, 0x0800);
+ bcm43xx_phy_write(bcm, 0x04A3, 0x2027);
+ bcm43xx_phy_write(bcm, 0x04A9, 0x1CA8);
+ bcm43xx_phy_write(bcm, 0x0493, 0x287A);
+ bcm43xx_phy_write(bcm, 0x04AA, 0x1CA8);
+ bcm43xx_phy_write(bcm, 0x04AC, 0x287A);
+
+ bcm43xx_phy_write(bcm, 0x04A0,
+ (bcm43xx_phy_read(bcm, 0x04A0)
+ & 0xFFC0) | 0x001A);
+ bcm43xx_phy_write(bcm, 0x04A7, 0x000D);
+
+ if (phy->rev < 2) {
+ bcm43xx_phy_write(bcm, 0x0406, 0xFF0D);
+ } else if (phy->rev == 2) {
+ bcm43xx_phy_write(bcm, 0x04C0, 0xFFFF);
+ bcm43xx_phy_write(bcm, 0x04C1, 0x00A9);
+ } else {
+ bcm43xx_phy_write(bcm, 0x04C0, 0x00C1);
+ bcm43xx_phy_write(bcm, 0x04C1, 0x0059);
+ }
+
+ bcm43xx_phy_write(bcm, 0x04A1,
+ (bcm43xx_phy_read(bcm, 0x04A1)
+ & 0xC0FF) | 0x1800);
+ bcm43xx_phy_write(bcm, 0x04A1,
+ (bcm43xx_phy_read(bcm, 0x04A1)
+ & 0xFFC0) | 0x0015);
+ bcm43xx_phy_write(bcm, 0x04A8,
+ (bcm43xx_phy_read(bcm, 0x04A8)
+ & 0xCFFF) | 0x1000);
+ bcm43xx_phy_write(bcm, 0x04A8,
+ (bcm43xx_phy_read(bcm, 0x04A8)
+ & 0xF0FF) | 0x0A00);
+ bcm43xx_phy_write(bcm, 0x04AB,
+ (bcm43xx_phy_read(bcm, 0x04AB)
+ & 0xCFFF) | 0x1000);
+ bcm43xx_phy_write(bcm, 0x04AB,
+ (bcm43xx_phy_read(bcm, 0x04AB)
+ & 0xF0FF) | 0x0800);
+ bcm43xx_phy_write(bcm, 0x04AB,
+ (bcm43xx_phy_read(bcm, 0x04AB)
+ & 0xFFCF) | 0x0010);
+ bcm43xx_phy_write(bcm, 0x04AB,
+ (bcm43xx_phy_read(bcm, 0x04AB)
+ & 0xFFF0) | 0x0005);
+ bcm43xx_phy_write(bcm, 0x04A8,
+ (bcm43xx_phy_read(bcm, 0x04A8)
+ & 0xFFCF) | 0x0010);
+ bcm43xx_phy_write(bcm, 0x04A8,
+ (bcm43xx_phy_read(bcm, 0x04A8)
+ & 0xFFF0) | 0x0006);
+ bcm43xx_phy_write(bcm, 0x04A2,
+ (bcm43xx_phy_read(bcm, 0x04A2)
+ & 0xF0FF) | 0x0800);
+ bcm43xx_phy_write(bcm, 0x04A0,
+ (bcm43xx_phy_read(bcm, 0x04A0)
+ & 0xF0FF) | 0x0500);
+ bcm43xx_phy_write(bcm, 0x04A2,
+ (bcm43xx_phy_read(bcm, 0x04A2)
+ & 0xFFF0) | 0x000B);
+
+ if (phy->rev >= 3) {
+ bcm43xx_phy_write(bcm, 0x048A,
+ bcm43xx_phy_read(bcm, 0x048A)
+ & ~0x8000);
+ bcm43xx_phy_write(bcm, 0x0415,
+ (bcm43xx_phy_read(bcm, 0x0415)
+ & 0x8000) | 0x36D8);
+ bcm43xx_phy_write(bcm, 0x0416,
+ (bcm43xx_phy_read(bcm, 0x0416)
+ & 0x8000) | 0x36D8);
+ bcm43xx_phy_write(bcm, 0x0417,
+ (bcm43xx_phy_read(bcm, 0x0417)
+ & 0xFE00) | 0x016D);
+ } else {
+ bcm43xx_phy_write(bcm, 0x048A,
+ bcm43xx_phy_read(bcm, 0x048A)
+ | 0x1000);
+ bcm43xx_phy_write(bcm, 0x048A,
+ (bcm43xx_phy_read(bcm, 0x048A)
+ & 0x9FFF) | 0x2000);
+ tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET);
+ if (!(tmp32 & 0x800)) {
+ tmp32 |= 0x800;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET,
+ tmp32);
+ }
+ }
+ if (phy->rev >= 2) {
+ bcm43xx_phy_write(bcm, 0x042B,
+ bcm43xx_phy_read(bcm, 0x042B)
+ | 0x0800);
+ }
+ bcm43xx_phy_write(bcm, 0x048C,
+ (bcm43xx_phy_read(bcm, 0x048C)
+ & 0xF0FF) | 0x0200);
+ if (phy->rev == 2) {
+ bcm43xx_phy_write(bcm, 0x04AE,
+ (bcm43xx_phy_read(bcm, 0x04AE)
+ & 0xFF00) | 0x007F);
+ bcm43xx_phy_write(bcm, 0x04AD,
+ (bcm43xx_phy_read(bcm, 0x04AD)
+ & 0x00FF) | 0x1300);
+ } else if (phy->rev >= 6) {
+ bcm43xx_ilt_write(bcm, 0x1A00 + 0x3, 0x007F);
+ bcm43xx_ilt_write(bcm, 0x1A00 + 0x2, 0x007F);
+ bcm43xx_phy_write(bcm, 0x04AD,
+ bcm43xx_phy_read(bcm, 0x04AD)
+ & 0x00FF);
+ }
+ bcm43xx_calc_nrssi_slope(bcm);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+static void
+bcm43xx_radio_interference_mitigation_disable(struct bcm43xx_private *bcm,
+ int mode)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u32 tmp32;
+ u32 *stack = radio->interfstack;
+
+ switch (mode) {
+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:
+ if (phy->rev != 1) {
+ bcm43xx_phy_write(bcm, 0x042B,
+ bcm43xx_phy_read(bcm, 0x042B) & ~0x0800);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000);
+ break;
+ }
+ phy_stackrestore(0x0078);
+ bcm43xx_calc_nrssi_threshold(bcm);
+ phy_stackrestore(0x0406);
+ bcm43xx_phy_write(bcm, 0x042B,
+ bcm43xx_phy_read(bcm, 0x042B) & ~0x0800);
+ if (!bcm->bad_frames_preempt) {
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_RADIO_BITFIELD,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_RADIO_BITFIELD)
+ & ~(1 << 11));
+ }
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) | 0x4000);
+ phy_stackrestore(0x04A0);
+ phy_stackrestore(0x04A1);
+ phy_stackrestore(0x04A2);
+ phy_stackrestore(0x04A8);
+ phy_stackrestore(0x04AB);
+ phy_stackrestore(0x04A7);
+ phy_stackrestore(0x04A3);
+ phy_stackrestore(0x04A9);
+ phy_stackrestore(0x0493);
+ phy_stackrestore(0x04AA);
+ phy_stackrestore(0x04AC);
+ break;
+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
+ if (!(bcm43xx_phy_read(bcm, 0x0033) & 0x0800))
+ break;
+
+ radio->aci_enable = 0;
+
+ phy_stackrestore(BCM43xx_PHY_RADIO_BITFIELD);
+ phy_stackrestore(BCM43xx_PHY_G_CRS);
+ phy_stackrestore(0x0033);
+ phy_stackrestore(0x04A3);
+ phy_stackrestore(0x04A9);
+ phy_stackrestore(0x0493);
+ phy_stackrestore(0x04AA);
+ phy_stackrestore(0x04AC);
+ phy_stackrestore(0x04A0);
+ phy_stackrestore(0x04A7);
+ if (phy->rev >= 2) {
+ phy_stackrestore(0x04C0);
+ phy_stackrestore(0x04C1);
+ } else
+ phy_stackrestore(0x0406);
+ phy_stackrestore(0x04A1);
+ phy_stackrestore(0x04AB);
+ phy_stackrestore(0x04A8);
+ if (phy->rev == 2) {
+ phy_stackrestore(0x04AD);
+ phy_stackrestore(0x04AE);
+ } else if (phy->rev >= 3) {
+ phy_stackrestore(0x04AD);
+ phy_stackrestore(0x0415);
+ phy_stackrestore(0x0416);
+ phy_stackrestore(0x0417);
+ ilt_stackrestore(0x1A00 + 0x2);
+ ilt_stackrestore(0x1A00 + 0x3);
+ }
+ phy_stackrestore(0x04A2);
+ phy_stackrestore(0x04A8);
+ phy_stackrestore(0x042B);
+ phy_stackrestore(0x048C);
+ tmp32 = bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET);
+ if (tmp32 & 0x800) {
+ tmp32 &= ~0x800;
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET,
+ tmp32);
+ }
+ bcm43xx_calc_nrssi_slope(bcm);
+ break;
+ default:
+ assert(0);
+ }
+}
+
+#undef phy_stacksave
+#undef phy_stackrestore
+#undef radio_stacksave
+#undef radio_stackrestore
+#undef ilt_stacksave
+#undef ilt_stackrestore
+
+int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm,
+ int mode)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ int currentmode;
+
+ if ((phy->type != BCM43xx_PHYTYPE_G) ||
+ (phy->rev == 0) ||
+ (!phy->connected))
+ return -ENODEV;
+
+ radio->aci_wlan_automatic = 0;
+ switch (mode) {
+ case BCM43xx_RADIO_INTERFMODE_AUTOWLAN:
+ radio->aci_wlan_automatic = 1;
+ if (radio->aci_enable)
+ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
+ else
+ mode = BCM43xx_RADIO_INTERFMODE_NONE;
+ break;
+ case BCM43xx_RADIO_INTERFMODE_NONE:
+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:
+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ currentmode = radio->interfmode;
+ if (currentmode == mode)
+ return 0;
+ if (currentmode != BCM43xx_RADIO_INTERFMODE_NONE)
+ bcm43xx_radio_interference_mitigation_disable(bcm, currentmode);
+
+ if (mode == BCM43xx_RADIO_INTERFMODE_NONE) {
+ radio->aci_enable = 0;
+ radio->aci_hw_rssi = 0;
+ } else
+ bcm43xx_radio_interference_mitigation_enable(bcm, mode);
+ radio->interfmode = mode;
+
+ return 0;
+}
+
+u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm)
+{
+ u16 reg, index, ret;
+
+ reg = bcm43xx_radio_read16(bcm, 0x0060);
+ index = (reg & 0x001E) >> 1;
+ ret = rcc_table[index] << 1;
+ ret |= (reg & 0x0001);
+ ret |= 0x0020;
+
+ return ret;
+}
+
+u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 backup[19] = { 0 };
+ u16 ret;
+ u16 i, j;
+ u32 tmp1 = 0, tmp2 = 0;
+
+ backup[0] = bcm43xx_radio_read16(bcm, 0x0043);
+ backup[14] = bcm43xx_radio_read16(bcm, 0x0051);
+ backup[15] = bcm43xx_radio_read16(bcm, 0x0052);
+ backup[1] = bcm43xx_phy_read(bcm, 0x0015);
+ backup[16] = bcm43xx_phy_read(bcm, 0x005A);
+ backup[17] = bcm43xx_phy_read(bcm, 0x0059);
+ backup[18] = bcm43xx_phy_read(bcm, 0x0058);
+ if (phy->type == BCM43xx_PHYTYPE_B) {
+ backup[2] = bcm43xx_phy_read(bcm, 0x0030);
+ backup[3] = bcm43xx_read16(bcm, 0x03EC);
+ bcm43xx_phy_write(bcm, 0x0030, 0x00FF);
+ bcm43xx_write16(bcm, 0x03EC, 0x3F3F);
+ } else {
+ if (phy->connected) {
+ backup[4] = bcm43xx_phy_read(bcm, 0x0811);
+ backup[5] = bcm43xx_phy_read(bcm, 0x0812);
+ backup[6] = bcm43xx_phy_read(bcm, 0x0814);
+ backup[7] = bcm43xx_phy_read(bcm, 0x0815);
+ backup[8] = bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS);
+ backup[9] = bcm43xx_phy_read(bcm, 0x0802);
+ bcm43xx_phy_write(bcm, 0x0814,
+ (bcm43xx_phy_read(bcm, 0x0814) | 0x0003));
+ bcm43xx_phy_write(bcm, 0x0815,
+ (bcm43xx_phy_read(bcm, 0x0815) & 0xFFFC));
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS,
+ (bcm43xx_phy_read(bcm, BCM43xx_PHY_G_CRS) & 0x7FFF));
+ bcm43xx_phy_write(bcm, 0x0802,
+ (bcm43xx_phy_read(bcm, 0x0802) & 0xFFFC));
+ bcm43xx_phy_write(bcm, 0x0811, 0x01B3);
+ bcm43xx_phy_write(bcm, 0x0812, 0x0FB2);
+ }
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
+ (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) | 0x8000));
+ }
+ backup[10] = bcm43xx_phy_read(bcm, 0x0035);
+ bcm43xx_phy_write(bcm, 0x0035,
+ (bcm43xx_phy_read(bcm, 0x0035) & 0xFF7F));
+ backup[11] = bcm43xx_read16(bcm, 0x03E6);
+ backup[12] = bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT);
+
+ // Initialization
+ if (phy->version == 0) {
+ bcm43xx_write16(bcm, 0x03E6, 0x0122);
+ } else {
+ if (phy->version >= 2)
+ bcm43xx_write16(bcm, 0x03E6, 0x0040);
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
+ (bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT) | 0x2000));
+ }
+
+ ret = bcm43xx_radio_calibrationvalue(bcm);
+
+ if (phy->type == BCM43xx_PHYTYPE_B)
+ bcm43xx_radio_write16(bcm, 0x0078, 0x0003);
+
+ bcm43xx_phy_write(bcm, 0x0015, 0xBFAF);
+ bcm43xx_phy_write(bcm, 0x002B, 0x1403);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x00B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xBFA0);
+ bcm43xx_radio_write16(bcm, 0x0051,
+ (bcm43xx_radio_read16(bcm, 0x0051) | 0x0004));
+ bcm43xx_radio_write16(bcm, 0x0052, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x0043,
+ bcm43xx_radio_read16(bcm, 0x0043) | 0x0009);
+ bcm43xx_phy_write(bcm, 0x0058, 0x0000);
+
+ for (i = 0; i < 16; i++) {
+ bcm43xx_phy_write(bcm, 0x005A, 0x0480);
+ bcm43xx_phy_write(bcm, 0x0059, 0xC810);
+ bcm43xx_phy_write(bcm, 0x0058, 0x000D);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
+ udelay(10);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xEFB0);
+ udelay(10);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xFFF0);
+ udelay(10);
+ tmp1 += bcm43xx_phy_read(bcm, 0x002D);
+ bcm43xx_phy_write(bcm, 0x0058, 0x0000);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
+ }
+
+ tmp1++;
+ tmp1 >>= 9;
+ udelay(10);
+ bcm43xx_phy_write(bcm, 0x0058, 0x0000);
+
+ for (i = 0; i < 16; i++) {
+ bcm43xx_radio_write16(bcm, 0x0078, (flip_4bit(i) << 1) | 0x0020);
+ backup[13] = bcm43xx_radio_read16(bcm, 0x0078);
+ udelay(10);
+ for (j = 0; j < 16; j++) {
+ bcm43xx_phy_write(bcm, 0x005A, 0x0D80);
+ bcm43xx_phy_write(bcm, 0x0059, 0xC810);
+ bcm43xx_phy_write(bcm, 0x0058, 0x000D);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
+ udelay(10);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xEFB0);
+ udelay(10);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B3); /* 0x30B3 is not a typo */
+ bcm43xx_phy_write(bcm, 0x0015, 0xFFF0);
+ udelay(10);
+ tmp2 += bcm43xx_phy_read(bcm, 0x002D);
+ bcm43xx_phy_write(bcm, 0x0058, 0x0000);
+ if (phy->connected)
+ bcm43xx_phy_write(bcm, 0x0812, 0x30B2);
+ bcm43xx_phy_write(bcm, 0x0015, 0xAFB0);
+ }
+ tmp2++;
+ tmp2 >>= 8;
+ if (tmp1 < tmp2)
+ break;
+ }
+
+ /* Restore the registers */
+ bcm43xx_phy_write(bcm, 0x0015, backup[1]);
+ bcm43xx_radio_write16(bcm, 0x0051, backup[14]);
+ bcm43xx_radio_write16(bcm, 0x0052, backup[15]);
+ bcm43xx_radio_write16(bcm, 0x0043, backup[0]);
+ bcm43xx_phy_write(bcm, 0x005A, backup[16]);
+ bcm43xx_phy_write(bcm, 0x0059, backup[17]);
+ bcm43xx_phy_write(bcm, 0x0058, backup[18]);
+ bcm43xx_write16(bcm, 0x03E6, backup[11]);
+ if (phy->version != 0)
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT, backup[12]);
+ bcm43xx_phy_write(bcm, 0x0035, backup[10]);
+ bcm43xx_radio_selectchannel(bcm, radio->channel, 1);
+ if (phy->type == BCM43xx_PHYTYPE_B) {
+ bcm43xx_phy_write(bcm, 0x0030, backup[2]);
+ bcm43xx_write16(bcm, 0x03EC, backup[3]);
+ } else {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_PHY_RADIO,
+ (bcm43xx_read16(bcm, BCM43xx_MMIO_PHY_RADIO) & 0x7FFF));
+ if (phy->connected) {
+ bcm43xx_phy_write(bcm, 0x0811, backup[4]);
+ bcm43xx_phy_write(bcm, 0x0812, backup[5]);
+ bcm43xx_phy_write(bcm, 0x0814, backup[6]);
+ bcm43xx_phy_write(bcm, 0x0815, backup[7]);
+ bcm43xx_phy_write(bcm, BCM43xx_PHY_G_CRS, backup[8]);
+ bcm43xx_phy_write(bcm, 0x0802, backup[9]);
+ }
+ }
+ if (i >= 15)
+ ret = backup[13];
+
+ return ret;
+}
+
+void bcm43xx_radio_init2060(struct bcm43xx_private *bcm)
+{
+ int err;
+
+ bcm43xx_radio_write16(bcm, 0x0004, 0x00C0);
+ bcm43xx_radio_write16(bcm, 0x0005, 0x0008);
+ bcm43xx_radio_write16(bcm, 0x0009, 0x0040);
+ bcm43xx_radio_write16(bcm, 0x0005, 0x00AA);
+ bcm43xx_radio_write16(bcm, 0x0032, 0x008F);
+ bcm43xx_radio_write16(bcm, 0x0006, 0x008F);
+ bcm43xx_radio_write16(bcm, 0x0034, 0x008F);
+ bcm43xx_radio_write16(bcm, 0x002C, 0x0007);
+ bcm43xx_radio_write16(bcm, 0x0082, 0x0080);
+ bcm43xx_radio_write16(bcm, 0x0080, 0x0000);
+ bcm43xx_radio_write16(bcm, 0x003F, 0x00DA);
+ bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008);
+ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0010);
+ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020);
+ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020);
+ udelay(400);
+
+ bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0020) | 0x0010);
+ udelay(400);
+
+ bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008) | 0x0008);
+ bcm43xx_radio_write16(bcm, 0x0085, bcm43xx_radio_read16(bcm, 0x0085) & ~0x0010);
+ bcm43xx_radio_write16(bcm, 0x0005, bcm43xx_radio_read16(bcm, 0x0005) & ~0x0008);
+ bcm43xx_radio_write16(bcm, 0x0081, bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040);
+ bcm43xx_radio_write16(bcm, 0x0081, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0040) | 0x0040);
+ bcm43xx_radio_write16(bcm, 0x0005, (bcm43xx_radio_read16(bcm, 0x0081) & ~0x0008) | 0x0008);
+ bcm43xx_phy_write(bcm, 0x0063, 0xDDC6);
+ bcm43xx_phy_write(bcm, 0x0069, 0x07BE);
+ bcm43xx_phy_write(bcm, 0x006A, 0x0000);
+
+ err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_A, 0);
+ assert(err == 0);
+ udelay(1000);
+}
+
+static inline
+u16 freq_r3A_value(u16 frequency)
+{
+ u16 value;
+
+ if (frequency < 5091)
+ value = 0x0040;
+ else if (frequency < 5321)
+ value = 0x0000;
+ else if (frequency < 5806)
+ value = 0x0080;
+ else
+ value = 0x0040;
+
+ return value;
+}
+
+void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm)
+{
+ static const u8 data_high[5] = { 0x00, 0x40, 0x80, 0x90, 0xD0 };
+ static const u8 data_low[5] = { 0x00, 0x01, 0x05, 0x06, 0x0A };
+ u16 tmp = bcm43xx_radio_read16(bcm, 0x001E);
+ int i, j;
+
+ for (i = 0; i < 5; i++) {
+ for (j = 0; j < 5; j++) {
+ if (tmp == (data_high[i] << 4 | data_low[j])) {
+ bcm43xx_phy_write(bcm, 0x0069, (i - j) << 8 | 0x00C0);
+ return;
+ }
+ }
+ }
+}
+
+int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm,
+ u8 channel,
+ int synthetic_pu_workaround)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 r8, tmp;
+ u16 freq;
+
+ if ((radio->manufact == 0x17F) &&
+ (radio->version == 0x2060) &&
+ (radio->revision == 1)) {
+ if (channel > 200)
+ return -EINVAL;
+ freq = channel2freq_a(channel);
+
+ r8 = bcm43xx_radio_read16(bcm, 0x0008);
+ bcm43xx_write16(bcm, 0x03F0, freq);
+ bcm43xx_radio_write16(bcm, 0x0008, r8);
+
+ TODO();//TODO: write max channel TX power? to Radio 0x2D
+ tmp = bcm43xx_radio_read16(bcm, 0x002E);
+ tmp &= 0x0080;
+ TODO();//TODO: OR tmp with the Power out estimation for this channel?
+ bcm43xx_radio_write16(bcm, 0x002E, tmp);
+
+ if (freq >= 4920 && freq <= 5500) {
+ /*
+ * r8 = (((freq * 15 * 0xE1FC780F) >> 32) / 29) & 0x0F;
+ * = (freq * 0.025862069
+ */
+ r8 = 3 * freq / 116; /* is equal to r8 = freq * 0.025862 */
+ }
+ bcm43xx_radio_write16(bcm, 0x0007, (r8 << 4) | r8);
+ bcm43xx_radio_write16(bcm, 0x0020, (r8 << 4) | r8);
+ bcm43xx_radio_write16(bcm, 0x0021, (r8 << 4) | r8);
+ bcm43xx_radio_write16(bcm, 0x0022,
+ (bcm43xx_radio_read16(bcm, 0x0022)
+ & 0x000F) | (r8 << 4));
+ bcm43xx_radio_write16(bcm, 0x002A, (r8 << 4));
+ bcm43xx_radio_write16(bcm, 0x002B, (r8 << 4));
+ bcm43xx_radio_write16(bcm, 0x0008,
+ (bcm43xx_radio_read16(bcm, 0x0008)
+ & 0x00F0) | (r8 << 4));
+ bcm43xx_radio_write16(bcm, 0x0029,
+ (bcm43xx_radio_read16(bcm, 0x0029)
+ & 0xFF0F) | 0x00B0);
+ bcm43xx_radio_write16(bcm, 0x0035, 0x00AA);
+ bcm43xx_radio_write16(bcm, 0x0036, 0x0085);
+ bcm43xx_radio_write16(bcm, 0x003A,
+ (bcm43xx_radio_read16(bcm, 0x003A)
+ & 0xFF20) | freq_r3A_value(freq));
+ bcm43xx_radio_write16(bcm, 0x003D,
+ bcm43xx_radio_read16(bcm, 0x003D) & 0x00FF);
+ bcm43xx_radio_write16(bcm, 0x0081,
+ (bcm43xx_radio_read16(bcm, 0x0081)
+ & 0xFF7F) | 0x0080);
+ bcm43xx_radio_write16(bcm, 0x0035,
+ bcm43xx_radio_read16(bcm, 0x0035) & 0xFFEF);
+ bcm43xx_radio_write16(bcm, 0x0035,
+ (bcm43xx_radio_read16(bcm, 0x0035)
+ & 0xFFEF) | 0x0010);
+ bcm43xx_radio_set_tx_iq(bcm);
+ TODO(); //TODO: TSSI2dbm workaround
+ bcm43xx_phy_xmitpower(bcm);//FIXME correct?
+ } else {
+ if ((channel < 1) || (channel > 14))
+ return -EINVAL;
+
+ if (synthetic_pu_workaround)
+ bcm43xx_synth_pu_workaround(bcm, channel);
+
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL,
+ channel2freq_bg(channel));
+
+ if (channel == 14) {
+ if (bcm->sprom.locale == BCM43xx_LOCALE_JAPAN) {
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET,
+ bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET)
+ & ~(1 << 7));
+ } else {
+ bcm43xx_shm_write32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET,
+ bcm43xx_shm_read32(bcm, BCM43xx_SHM_SHARED,
+ BCM43xx_UCODEFLAGS_OFFSET)
+ | (1 << 7));
+ }
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
+ | (1 << 11));
+ } else {
+ bcm43xx_write16(bcm, BCM43xx_MMIO_CHANNEL_EXT,
+ bcm43xx_read16(bcm, BCM43xx_MMIO_CHANNEL_EXT)
+ & 0xF7BF);
+ }
+ }
+
+ radio->channel = channel;
+ //XXX: Using the longer of 2 timeouts (8000 vs 2000 usecs). Specs states
+ // that 2000 usecs might suffice.
+ udelay(8000);
+
+ return 0;
+}
+
+void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val)
+{
+ u16 tmp;
+
+ val <<= 8;
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0022) & 0xFCFF;
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0022, tmp | val);
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x03A8) & 0xFCFF;
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x03A8, tmp | val);
+ tmp = bcm43xx_shm_read16(bcm, BCM43xx_SHM_SHARED, 0x0054) & 0xFCFF;
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0054, tmp | val);
+}
+
+/* http://bcm-specs.sipsolutions.net/TX_Gain_Base_Band */
+static u16 bcm43xx_get_txgain_base_band(u16 txpower)
+{
+ u16 ret;
+
+ assert(txpower <= 63);
+
+ if (txpower >= 54)
+ ret = 2;
+ else if (txpower >= 49)
+ ret = 4;
+ else if (txpower >= 44)
+ ret = 5;
+ else
+ ret = 6;
+
+ return ret;
+}
+
+/* http://bcm-specs.sipsolutions.net/TX_Gain_Radio_Frequency_Power_Amplifier */
+static u16 bcm43xx_get_txgain_freq_power_amp(u16 txpower)
+{
+ u16 ret;
+
+ assert(txpower <= 63);
+
+ if (txpower >= 32)
+ ret = 0;
+ else if (txpower >= 25)
+ ret = 1;
+ else if (txpower >= 20)
+ ret = 2;
+ else if (txpower >= 12)
+ ret = 3;
+ else
+ ret = 4;
+
+ return ret;
+}
+
+/* http://bcm-specs.sipsolutions.net/TX_Gain_Digital_Analog_Converter */
+static u16 bcm43xx_get_txgain_dac(u16 txpower)
+{
+ u16 ret;
+
+ assert(txpower <= 63);
+
+ if (txpower >= 54)
+ ret = txpower - 53;
+ else if (txpower >= 49)
+ ret = txpower - 42;
+ else if (txpower >= 44)
+ ret = txpower - 37;
+ else if (txpower >= 32)
+ ret = txpower - 32;
+ else if (txpower >= 25)
+ ret = txpower - 20;
+ else if (txpower >= 20)
+ ret = txpower - 13;
+ else if (txpower >= 12)
+ ret = txpower - 8;
+ else
+ ret = txpower;
+
+ return ret;
+}
+
+void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 pamp, base, dac, ilt;
+
+ txpower = limit_value(txpower, 0, 63);
+
+ pamp = bcm43xx_get_txgain_freq_power_amp(txpower);
+ pamp <<= 5;
+ pamp &= 0x00E0;
+ bcm43xx_phy_write(bcm, 0x0019, pamp);
+
+ base = bcm43xx_get_txgain_base_band(txpower);
+ base &= 0x000F;
+ bcm43xx_phy_write(bcm, 0x0017, base | 0x0020);
+
+ ilt = bcm43xx_ilt_read(bcm, 0x3001);
+ ilt &= 0x0007;
+
+ dac = bcm43xx_get_txgain_dac(txpower);
+ dac <<= 3;
+ dac |= ilt;
+
+ bcm43xx_ilt_write(bcm, 0x3001, dac);
+
+ radio->txpwr_offset = txpower;
+
+ TODO();
+ //TODO: FuncPlaceholder (Adjust BB loft cancel)
+}
+
+void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm,
+ u16 baseband_attenuation, u16 radio_attenuation,
+ u16 txpower)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ if (baseband_attenuation == 0xFFFF)
+ baseband_attenuation = radio->baseband_atten;
+ if (radio_attenuation == 0xFFFF)
+ radio_attenuation = radio->radio_atten;
+ if (txpower == 0xFFFF)
+ txpower = radio->txctl1;
+ radio->baseband_atten = baseband_attenuation;
+ radio->radio_atten = radio_attenuation;
+ radio->txctl1 = txpower;
+
+ assert(/*baseband_attenuation >= 0 &&*/ baseband_attenuation <= 11);
+ if (radio->revision < 6)
+ assert(/*radio_attenuation >= 0 &&*/ radio_attenuation <= 9);
+ else
+ assert(/* radio_attenuation >= 0 &&*/ radio_attenuation <= 31);
+ assert(/*txpower >= 0 &&*/ txpower <= 7);
+
+ bcm43xx_phy_set_baseband_attenuation(bcm, baseband_attenuation);
+ bcm43xx_radio_write16(bcm, 0x0043, radio_attenuation);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0064, radio_attenuation);
+ if (radio->version == 0x2050) {
+ bcm43xx_radio_write16(bcm, 0x0052,
+ (bcm43xx_radio_read16(bcm, 0x0052) & ~0x0070)
+ | ((txpower << 4) & 0x0070));
+ }
+ //FIXME: The spec is very weird and unclear here.
+ if (phy->type == BCM43xx_PHYTYPE_G)
+ bcm43xx_phy_lo_adjust(bcm, 0);
+}
+
+u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ if (radio->version == 0x2050 && radio->revision < 6)
+ return 0;
+ return 2;
+}
+
+u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ u16 att = 0xFFFF;
+
+ if (phy->type == BCM43xx_PHYTYPE_A)
+ return 0x60;
+
+ switch (radio->version) {
+ case 0x2053:
+ switch (radio->revision) {
+ case 1:
+ att = 6;
+ break;
+ }
+ break;
+ case 0x2050:
+ switch (radio->revision) {
+ case 0:
+ att = 5;
+ break;
+ case 1:
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
+ bcm->board_type == 0x421 &&
+ bcm->board_revision >= 30)
+ att = 3;
+ else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
+ bcm->board_type == 0x416)
+ att = 3;
+ else
+ att = 1;
+ } else {
+ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
+ bcm->board_type == 0x421 &&
+ bcm->board_revision >= 30)
+ att = 7;
+ else
+ att = 6;
+ }
+ break;
+ case 2:
+ if (phy->type == BCM43xx_PHYTYPE_G) {
+ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
+ bcm->board_type == 0x421 &&
+ bcm->board_revision >= 30)
+ att = 3;
+ else if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
+ bcm->board_type == 0x416)
+ att = 5;
+ else if (bcm->chip_id == 0x4320)
+ att = 4;
+ else
+ att = 3;
+ } else
+ att = 6;
+ break;
+ case 3:
+ att = 5;
+ break;
+ case 4:
+ case 5:
+ att = 1;
+ break;
+ case 6:
+ case 7:
+ att = 5;
+ break;
+ case 8:
+ att = 0x1A;
+ break;
+ case 9:
+ default:
+ att = 5;
+ }
+ }
+ if (bcm->board_vendor == PCI_VENDOR_ID_BROADCOM &&
+ bcm->board_type == 0x421) {
+ if (bcm->board_revision < 0x43)
+ att = 2;
+ else if (bcm->board_revision < 0x51)
+ att = 3;
+ }
+ if (att == 0xFFFF)
+ att = 5;
+
+ return att;
+}
+
+u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ if (radio->version != 0x2050)
+ return 0;
+ if (radio->revision == 1)
+ return 3;
+ if (radio->revision < 6)
+ return 2;
+ if (radio->revision == 8)
+ return 1;
+ return 0;
+}
+
+void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ int err;
+
+ if (radio->enabled)
+ return;
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ bcm43xx_radio_write16(bcm, 0x0004, 0x00C0);
+ bcm43xx_radio_write16(bcm, 0x0005, 0x0008);
+ bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) & 0xFFF7);
+ bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) & 0xFFF7);
+ bcm43xx_radio_init2060(bcm);
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ bcm43xx_phy_write(bcm, 0x0015, 0x8000);
+ bcm43xx_phy_write(bcm, 0x0015, 0xCC00);
+ bcm43xx_phy_write(bcm, 0x0015, (phy->connected ? 0x00C0 : 0x0000));
+ err = bcm43xx_radio_selectchannel(bcm, BCM43xx_RADIO_DEFAULT_CHANNEL_BG, 1);
+ assert(err == 0);
+ break;
+ default:
+ assert(0);
+ }
+ radio->enabled = 1;
+ dprintk(KERN_INFO PFX "Radio turned on\n");
+}
+
+void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+
+ if (phy->type == BCM43xx_PHYTYPE_A) {
+ bcm43xx_radio_write16(bcm, 0x0004, 0x00FF);
+ bcm43xx_radio_write16(bcm, 0x0005, 0x00FB);
+ bcm43xx_phy_write(bcm, 0x0010, bcm43xx_phy_read(bcm, 0x0010) | 0x0008);
+ bcm43xx_phy_write(bcm, 0x0011, bcm43xx_phy_read(bcm, 0x0011) | 0x0008);
+ }
+ if (phy->type == BCM43xx_PHYTYPE_G && bcm->current_core->rev >= 5) {
+ bcm43xx_phy_write(bcm, 0x0811, bcm43xx_phy_read(bcm, 0x0811) | 0x008C);
+ bcm43xx_phy_write(bcm, 0x0812, bcm43xx_phy_read(bcm, 0x0812) & 0xFF73);
+ } else
+ bcm43xx_phy_write(bcm, 0x0015, 0xAA00);
+ radio->enabled = 0;
+ dprintk(KERN_INFO PFX "Radio turned off\n");
+}
+
+void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0068, 0x7F7F);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x006a, 0x7F7F);
+ break;
+ case BCM43xx_PHYTYPE_B:
+ case BCM43xx_PHYTYPE_G:
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0058, 0x7F7F);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x005a, 0x7F7F);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0070, 0x7F7F);
+ bcm43xx_shm_write16(bcm, BCM43xx_SHM_SHARED, 0x0072, 0x7F7F);
+ break;
+ }
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_radio.h b/drivers/net/wireless/bcm43xx/bcm43xx_radio.h
new file mode 100644
index 000000000000..9ed18039fa3e
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_radio.h
@@ -0,0 +1,99 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef BCM43xx_RADIO_H_
+#define BCM43xx_RADIO_H_
+
+#include "bcm43xx.h"
+
+
+#define BCM43xx_RADIO_DEFAULT_CHANNEL_A 36
+#define BCM43xx_RADIO_DEFAULT_CHANNEL_BG 6
+
+/* Force antenna 0. */
+#define BCM43xx_RADIO_TXANTENNA_0 0
+/* Force antenna 1. */
+#define BCM43xx_RADIO_TXANTENNA_1 1
+/* Use the RX antenna, that was selected for the most recently
+ * received good PLCP header.
+ */
+#define BCM43xx_RADIO_TXANTENNA_LASTPLCP 3
+#define BCM43xx_RADIO_TXANTENNA_DEFAULT BCM43xx_RADIO_TXANTENNA_LASTPLCP
+
+#define BCM43xx_RADIO_INTERFMODE_NONE 0
+#define BCM43xx_RADIO_INTERFMODE_NONWLAN 1
+#define BCM43xx_RADIO_INTERFMODE_MANUALWLAN 2
+#define BCM43xx_RADIO_INTERFMODE_AUTOWLAN 3
+
+
+void bcm43xx_radio_lock(struct bcm43xx_private *bcm);
+void bcm43xx_radio_unlock(struct bcm43xx_private *bcm);
+
+u16 bcm43xx_radio_read16(struct bcm43xx_private *bcm, u16 offset);
+void bcm43xx_radio_write16(struct bcm43xx_private *bcm, u16 offset, u16 val);
+
+u16 bcm43xx_radio_init2050(struct bcm43xx_private *bcm);
+void bcm43xx_radio_init2060(struct bcm43xx_private *bcm);
+
+void bcm43xx_radio_turn_on(struct bcm43xx_private *bcm);
+void bcm43xx_radio_turn_off(struct bcm43xx_private *bcm);
+
+int bcm43xx_radio_selectchannel(struct bcm43xx_private *bcm, u8 channel,
+ int synthetic_pu_workaround);
+
+void bcm43xx_radio_set_txpower_a(struct bcm43xx_private *bcm, u16 txpower);
+void bcm43xx_radio_set_txpower_bg(struct bcm43xx_private *bcm,
+ u16 baseband_attenuation, u16 attenuation,
+ u16 txpower);
+
+u16 bcm43xx_default_baseband_attenuation(struct bcm43xx_private *bcm);
+u16 bcm43xx_default_radio_attenuation(struct bcm43xx_private *bcm);
+u16 bcm43xx_default_txctl1(struct bcm43xx_private *bcm);
+
+void bcm43xx_radio_set_txantenna(struct bcm43xx_private *bcm, u32 val);
+
+void bcm43xx_radio_clear_tssi(struct bcm43xx_private *bcm);
+
+u8 bcm43xx_radio_aci_detect(struct bcm43xx_private *bcm, u8 channel);
+u8 bcm43xx_radio_aci_scan(struct bcm43xx_private *bcm);
+
+int bcm43xx_radio_set_interference_mitigation(struct bcm43xx_private *bcm, int mode);
+
+void bcm43xx_calc_nrssi_slope(struct bcm43xx_private *bcm);
+void bcm43xx_calc_nrssi_threshold(struct bcm43xx_private *bcm);
+s16 bcm43xx_nrssi_hw_read(struct bcm43xx_private *bcm, u16 offset);
+void bcm43xx_nrssi_hw_write(struct bcm43xx_private *bcm, u16 offset, s16 val);
+void bcm43xx_nrssi_hw_update(struct bcm43xx_private *bcm, u16 val);
+void bcm43xx_nrssi_mem_update(struct bcm43xx_private *bcm);
+
+void bcm43xx_radio_set_tx_iq(struct bcm43xx_private *bcm);
+u16 bcm43xx_radio_calibrationvalue(struct bcm43xx_private *bcm);
+
+#endif /* BCM43xx_RADIO_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
new file mode 100644
index 000000000000..c44d890b949b
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.c
@@ -0,0 +1,322 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ SYSFS support routines
+
+ Copyright (c) 2006 Michael Buesch <mbuesch@freenet.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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#include "bcm43xx_sysfs.h"
+#include "bcm43xx.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_radio.h"
+
+#include <linux/capability.h>
+
+
+#define GENERIC_FILESIZE 64
+
+
+static int get_integer(const char *buf, size_t count)
+{
+ char tmp[10 + 1] = { 0 };
+ int ret = -EINVAL;
+
+ if (count == 0)
+ goto out;
+ count = min(count, (size_t)10);
+ memcpy(tmp, buf, count);
+ ret = simple_strtol(tmp, NULL, 10);
+out:
+ return ret;
+}
+
+static int get_boolean(const char *buf, size_t count)
+{
+ if (count != 0) {
+ if (buf[0] == '1')
+ return 1;
+ if (buf[0] == '0')
+ return 0;
+ if (count >= 4 && memcmp(buf, "true", 4) == 0)
+ return 1;
+ if (count >= 5 && memcmp(buf, "false", 5) == 0)
+ return 0;
+ if (count >= 3 && memcmp(buf, "yes", 3) == 0)
+ return 1;
+ if (count >= 2 && memcmp(buf, "no", 2) == 0)
+ return 0;
+ if (count >= 2 && memcmp(buf, "on", 2) == 0)
+ return 1;
+ if (count >= 3 && memcmp(buf, "off", 3) == 0)
+ return 0;
+ }
+ return -EINVAL;
+}
+
+static ssize_t bcm43xx_attr_sprom_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);
+ u16 *sprom;
+ unsigned long flags;
+ int i, err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ assert(BCM43xx_SPROM_SIZE * sizeof(u16) <= PAGE_SIZE);
+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
+ GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+ bcm43xx_lock_mmio(bcm, flags);
+ assert(bcm->initialized);
+ err = bcm43xx_sprom_read(bcm, sprom);
+ if (!err) {
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
+ buf[i * 2] = sprom[i] & 0x00FF;
+ buf[i * 2 + 1] = (sprom[i] & 0xFF00) >> 8;
+ }
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
+ kfree(sprom);
+
+ return err ? err : BCM43xx_SPROM_SIZE * sizeof(u16);
+}
+
+static ssize_t bcm43xx_attr_sprom_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_sprom);
+ u16 *sprom;
+ unsigned long flags;
+ int i, err;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ if (count != BCM43xx_SPROM_SIZE * sizeof(u16))
+ return -EINVAL;
+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
+ GFP_KERNEL);
+ if (!sprom)
+ return -ENOMEM;
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
+ sprom[i] = buf[i * 2] & 0xFF;
+ sprom[i] |= ((u16)(buf[i * 2 + 1] & 0xFF)) << 8;
+ }
+ bcm43xx_lock_mmio(bcm, flags);
+ assert(bcm->initialized);
+ err = bcm43xx_sprom_write(bcm, sprom);
+ bcm43xx_unlock_mmio(bcm, flags);
+ kfree(sprom);
+
+ return err ? err : count;
+
+}
+
+static ssize_t bcm43xx_attr_interfmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);
+ unsigned long flags;
+ int err;
+ ssize_t count = 0;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ bcm43xx_lock(bcm, flags);
+ assert(bcm->initialized);
+
+ switch (bcm43xx_current_radio(bcm)->interfmode) {
+ case BCM43xx_RADIO_INTERFMODE_NONE:
+ count = snprintf(buf, PAGE_SIZE, "0 (No Interference Mitigation)\n");
+ break;
+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:
+ count = snprintf(buf, PAGE_SIZE, "1 (Non-WLAN Interference Mitigation)\n");
+ break;
+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
+ count = snprintf(buf, PAGE_SIZE, "2 (WLAN Interference Mitigation)\n");
+ break;
+ default:
+ assert(0);
+ }
+ err = 0;
+
+ bcm43xx_unlock(bcm, flags);
+
+ return err ? err : count;
+
+}
+
+static ssize_t bcm43xx_attr_interfmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_interfmode);
+ unsigned long flags;
+ int err;
+ int mode;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ mode = get_integer(buf, count);
+ switch (mode) {
+ case 0:
+ mode = BCM43xx_RADIO_INTERFMODE_NONE;
+ break;
+ case 1:
+ mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
+ break;
+ case 2:
+ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
+ break;
+ case 3:
+ mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ bcm43xx_lock_mmio(bcm, flags);
+ assert(bcm->initialized);
+
+ err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
+ if (err) {
+ printk(KERN_ERR PFX "Interference Mitigation not "
+ "supported by device\n");
+ }
+
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return err ? err : count;
+}
+
+static ssize_t bcm43xx_attr_preamble_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);
+ unsigned long flags;
+ int err;
+ ssize_t count;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ bcm43xx_lock(bcm, flags);
+ assert(bcm->initialized);
+
+ if (bcm->short_preamble)
+ count = snprintf(buf, PAGE_SIZE, "1 (Short Preamble enabled)\n");
+ else
+ count = snprintf(buf, PAGE_SIZE, "0 (Short Preamble disabled)\n");
+
+ err = 0;
+ bcm43xx_unlock(bcm, flags);
+
+ return err ? err : count;
+}
+
+static ssize_t bcm43xx_attr_preamble_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bcm43xx_private *bcm = devattr_to_bcm(attr, attr_preamble);
+ unsigned long flags;
+ int err;
+ int value;
+
+ if (!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ value = get_boolean(buf, count);
+ if (value < 0)
+ return value;
+ bcm43xx_lock(bcm, flags);
+ assert(bcm->initialized);
+
+ bcm->short_preamble = !!value;
+
+ err = 0;
+ bcm43xx_unlock(bcm, flags);
+
+ return err ? err : count;
+}
+
+int bcm43xx_sysfs_register(struct bcm43xx_private *bcm)
+{
+ struct device *dev = &bcm->pci_dev->dev;
+ struct bcm43xx_sysfs *sysfs = &bcm->sysfs;
+ int err;
+
+ assert(bcm->initialized);
+
+ sysfs->attr_sprom.attr.name = "sprom";
+ sysfs->attr_sprom.attr.owner = THIS_MODULE;
+ sysfs->attr_sprom.attr.mode = 0600;
+ sysfs->attr_sprom.show = bcm43xx_attr_sprom_show;
+ sysfs->attr_sprom.store = bcm43xx_attr_sprom_store;
+ err = device_create_file(dev, &sysfs->attr_sprom);
+ if (err)
+ goto out;
+
+ sysfs->attr_interfmode.attr.name = "interference";
+ sysfs->attr_interfmode.attr.owner = THIS_MODULE;
+ sysfs->attr_interfmode.attr.mode = 0600;
+ sysfs->attr_interfmode.show = bcm43xx_attr_interfmode_show;
+ sysfs->attr_interfmode.store = bcm43xx_attr_interfmode_store;
+ err = device_create_file(dev, &sysfs->attr_interfmode);
+ if (err)
+ goto err_remove_sprom;
+
+ sysfs->attr_preamble.attr.name = "shortpreamble";
+ sysfs->attr_preamble.attr.owner = THIS_MODULE;
+ sysfs->attr_preamble.attr.mode = 0600;
+ sysfs->attr_preamble.show = bcm43xx_attr_preamble_show;
+ sysfs->attr_preamble.store = bcm43xx_attr_preamble_store;
+ err = device_create_file(dev, &sysfs->attr_preamble);
+ if (err)
+ goto err_remove_interfmode;
+
+out:
+ return err;
+err_remove_interfmode:
+ device_remove_file(dev, &sysfs->attr_interfmode);
+err_remove_sprom:
+ device_remove_file(dev, &sysfs->attr_sprom);
+ goto out;
+}
+
+void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm)
+{
+ struct device *dev = &bcm->pci_dev->dev;
+ struct bcm43xx_sysfs *sysfs = &bcm->sysfs;
+
+ device_remove_file(dev, &sysfs->attr_preamble);
+ device_remove_file(dev, &sysfs->attr_interfmode);
+ device_remove_file(dev, &sysfs->attr_sprom);
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h
new file mode 100644
index 000000000000..57f14514e3e0
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_sysfs.h
@@ -0,0 +1,25 @@
+#ifndef BCM43xx_SYSFS_H_
+#define BCM43xx_SYSFS_H_
+
+#include <linux/device.h>
+
+
+struct bcm43xx_sysfs {
+ struct device_attribute attr_sprom;
+ struct device_attribute attr_interfmode;
+ struct device_attribute attr_preamble;
+};
+
+#define devattr_to_bcm(attr, attr_name) ({ \
+ struct bcm43xx_sysfs *__s; struct bcm43xx_private *__p; \
+ __s = container_of((attr), struct bcm43xx_sysfs, attr_name); \
+ __p = container_of(__s, struct bcm43xx_private, sysfs); \
+ __p; \
+ })
+
+struct bcm43xx_private;
+
+int bcm43xx_sysfs_register(struct bcm43xx_private *bcm);
+void bcm43xx_sysfs_unregister(struct bcm43xx_private *bcm);
+
+#endif /* BCM43xx_SYSFS_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
new file mode 100644
index 000000000000..3daee828ef4b
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
@@ -0,0 +1,1002 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; 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/wireless.h>
+#include <net/iw_handler.h>
+#include <net/ieee80211softmac.h>
+#include <net/ieee80211softmac_wx.h>
+#include <linux/capability.h>
+#include <linux/sched.h> /* for capable() */
+#include <linux/delay.h>
+
+#include "bcm43xx.h"
+#include "bcm43xx_wx.h"
+#include "bcm43xx_main.h"
+#include "bcm43xx_radio.h"
+#include "bcm43xx_phy.h"
+
+
+/* The WIRELESS_EXT version, which is implemented by this driver. */
+#define BCM43xx_WX_VERSION 18
+
+#define MAX_WX_STRING 80
+
+
+static int bcm43xx_wx_get_name(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int i;
+ struct bcm43xx_phyinfo *phy;
+ char suffix[7] = { 0 };
+ int have_a = 0, have_b = 0, have_g = 0;
+
+ bcm43xx_lock(bcm, flags);
+ for (i = 0; i < bcm->nr_80211_available; i++) {
+ phy = &(bcm->core_80211_ext[i].phy);
+ switch (phy->type) {
+ case BCM43xx_PHYTYPE_A:
+ have_a = 1;
+ break;
+ case BCM43xx_PHYTYPE_G:
+ have_g = 1;
+ case BCM43xx_PHYTYPE_B:
+ have_b = 1;
+ break;
+ default:
+ assert(0);
+ }
+ }
+ bcm43xx_unlock(bcm, flags);
+
+ i = 0;
+ if (have_a) {
+ suffix[i++] = 'a';
+ suffix[i++] = '/';
+ }
+ if (have_b) {
+ suffix[i++] = 'b';
+ suffix[i++] = '/';
+ }
+ if (have_g) {
+ suffix[i++] = 'g';
+ suffix[i++] = '/';
+ }
+ if (i != 0)
+ suffix[i - 1] = '\0';
+
+ snprintf(data->name, IFNAMSIZ, "IEEE 802.11%s", suffix);
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_channelfreq(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ u8 channel;
+ int freq;
+ int err = -EINVAL;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if ((data->freq.m >= 0) && (data->freq.m <= 1000)) {
+ channel = data->freq.m;
+ freq = bcm43xx_channel_to_freq(bcm, channel);
+ } else {
+ channel = bcm43xx_freq_to_channel(bcm, data->freq.m);
+ freq = data->freq.m;
+ }
+ if (!bcm43xx_is_valid_channel(bcm, channel))
+ goto out_unlock;
+ if (bcm->initialized) {
+ //ieee80211softmac_disassoc(softmac, $REASON);
+ bcm43xx_mac_suspend(bcm);
+ err = bcm43xx_radio_selectchannel(bcm, channel, 0);
+ bcm43xx_mac_enable(bcm);
+ } else {
+ bcm43xx_current_radio(bcm)->initial_channel = channel;
+ err = 0;
+ }
+out_unlock:
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_channelfreq(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct bcm43xx_radioinfo *radio;
+ unsigned long flags;
+ int err = -ENODEV;
+ u16 channel;
+
+ bcm43xx_lock(bcm, flags);
+ radio = bcm43xx_current_radio(bcm);
+ channel = radio->channel;
+ if (channel == 0xFF) {
+ assert(!bcm->initialized);
+ channel = radio->initial_channel;
+ if (channel == 0xFF)
+ goto out_unlock;
+ }
+ assert(channel > 0 && channel <= 1000);
+ data->freq.e = 1;
+ data->freq.m = bcm43xx_channel_to_freq(bcm, channel) * 100000;
+ data->freq.flags = 1;
+
+ err = 0;
+out_unlock:
+ bcm43xx_unlock(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_set_mode(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int mode;
+
+ mode = data->mode;
+ if (mode == IW_MODE_AUTO)
+ mode = BCM43xx_INITIAL_IWMODE;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (bcm->ieee->iw_mode != mode)
+ bcm43xx_set_iwmode(bcm, mode);
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_get_mode(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ bcm43xx_lock(bcm, flags);
+ data->mode = bcm->ieee->iw_mode;
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_get_rangeparams(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct iw_range *range = (struct iw_range *)extra;
+ const struct ieee80211_geo *geo;
+ unsigned long flags;
+ int i, j;
+ struct bcm43xx_phyinfo *phy;
+
+ data->data.length = sizeof(*range);
+ memset(range, 0, sizeof(*range));
+
+ //TODO: What about 802.11b?
+ /* 54Mb/s == ~27Mb/s payload throughput (802.11g) */
+ range->throughput = 27 * 1000 * 1000;
+
+ range->max_qual.qual = 100;
+ /* TODO: Real max RSSI */
+ range->max_qual.level = 3;
+ range->max_qual.noise = 100;
+ range->max_qual.updated = 7;
+
+ range->avg_qual.qual = 70;
+ range->avg_qual.level = 2;
+ range->avg_qual.noise = 40;
+ range->avg_qual.updated = 7;
+
+ range->min_rts = BCM43xx_MIN_RTS_THRESHOLD;
+ range->max_rts = BCM43xx_MAX_RTS_THRESHOLD;
+ range->min_frag = MIN_FRAG_THRESHOLD;
+ range->max_frag = MAX_FRAG_THRESHOLD;
+
+ range->encoding_size[0] = 5;
+ range->encoding_size[1] = 13;
+ range->num_encoding_sizes = 2;
+ range->max_encoding_tokens = WEP_KEYS;
+
+ range->we_version_compiled = WIRELESS_EXT;
+ range->we_version_source = BCM43xx_WX_VERSION;
+
+ range->enc_capa = IW_ENC_CAPA_WPA |
+ IW_ENC_CAPA_WPA2 |
+ IW_ENC_CAPA_CIPHER_TKIP |
+ IW_ENC_CAPA_CIPHER_CCMP;
+
+ bcm43xx_lock(bcm, flags);
+ phy = bcm43xx_current_phy(bcm);
+
+ range->num_bitrates = 0;
+ i = 0;
+ if (phy->type == BCM43xx_PHYTYPE_A ||
+ phy->type == BCM43xx_PHYTYPE_G) {
+ range->num_bitrates = 8;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_6MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_9MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_12MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_18MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_24MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_36MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_48MB;
+ range->bitrate[i++] = IEEE80211_OFDM_RATE_54MB;
+ }
+ if (phy->type == BCM43xx_PHYTYPE_B ||
+ phy->type == BCM43xx_PHYTYPE_G) {
+ range->num_bitrates += 4;
+ range->bitrate[i++] = IEEE80211_CCK_RATE_1MB;
+ range->bitrate[i++] = IEEE80211_CCK_RATE_2MB;
+ range->bitrate[i++] = IEEE80211_CCK_RATE_5MB;
+ range->bitrate[i++] = IEEE80211_CCK_RATE_11MB;
+ }
+
+ geo = ieee80211_get_geo(bcm->ieee);
+ range->num_channels = geo->a_channels + geo->bg_channels;
+ j = 0;
+ for (i = 0; i < geo->a_channels; i++) {
+ if (j == IW_MAX_FREQUENCIES)
+ break;
+ range->freq[j].i = j + 1;
+ range->freq[j].m = geo->a[i].freq;//FIXME?
+ range->freq[j].e = 1;
+ j++;
+ }
+ for (i = 0; i < geo->bg_channels; i++) {
+ if (j == IW_MAX_FREQUENCIES)
+ break;
+ range->freq[j].i = j + 1;
+ range->freq[j].m = geo->bg[i].freq;//FIXME?
+ range->freq[j].e = 1;
+ j++;
+ }
+ range->num_frequency = j;
+
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_nick(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ size_t len;
+
+ bcm43xx_lock(bcm, flags);
+ len = min((size_t)data->data.length, (size_t)IW_ESSID_MAX_SIZE);
+ memcpy(bcm->nick, extra, len);
+ bcm->nick[len] = '\0';
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_get_nick(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ size_t len;
+
+ bcm43xx_lock(bcm, flags);
+ len = strlen(bcm->nick) + 1;
+ memcpy(extra, bcm->nick, len);
+ data->data.length = (__u16)len;
+ data->data.flags = 1;
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_rts(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int err = -EINVAL;
+
+ bcm43xx_lock(bcm, flags);
+ if (data->rts.disabled) {
+ bcm->rts_threshold = BCM43xx_MAX_RTS_THRESHOLD;
+ err = 0;
+ } else {
+ if (data->rts.value >= BCM43xx_MIN_RTS_THRESHOLD &&
+ data->rts.value <= BCM43xx_MAX_RTS_THRESHOLD) {
+ bcm->rts_threshold = data->rts.value;
+ err = 0;
+ }
+ }
+ bcm43xx_unlock(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_rts(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ bcm43xx_lock(bcm, flags);
+ data->rts.value = bcm->rts_threshold;
+ data->rts.fixed = 0;
+ data->rts.disabled = (bcm->rts_threshold == BCM43xx_MAX_RTS_THRESHOLD);
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_frag(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int err = -EINVAL;
+
+ bcm43xx_lock(bcm, flags);
+ if (data->frag.disabled) {
+ bcm->ieee->fts = MAX_FRAG_THRESHOLD;
+ err = 0;
+ } else {
+ if (data->frag.value >= MIN_FRAG_THRESHOLD &&
+ data->frag.value <= MAX_FRAG_THRESHOLD) {
+ bcm->ieee->fts = data->frag.value & ~0x1;
+ err = 0;
+ }
+ }
+ bcm43xx_unlock(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_frag(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+
+ bcm43xx_lock(bcm, flags);
+ data->frag.value = bcm->ieee->fts;
+ data->frag.fixed = 0;
+ data->frag.disabled = (bcm->ieee->fts == MAX_FRAG_THRESHOLD);
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_xmitpower(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct bcm43xx_radioinfo *radio;
+ struct bcm43xx_phyinfo *phy;
+ unsigned long flags;
+ int err = -ENODEV;
+ u16 maxpower;
+
+ if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) {
+ printk(PFX KERN_ERR "TX power not in dBm.\n");
+ return -EOPNOTSUPP;
+ }
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (!bcm->initialized)
+ goto out_unlock;
+ radio = bcm43xx_current_radio(bcm);
+ phy = bcm43xx_current_phy(bcm);
+ if (data->txpower.disabled != (!(radio->enabled))) {
+ if (data->txpower.disabled)
+ bcm43xx_radio_turn_off(bcm);
+ else
+ bcm43xx_radio_turn_on(bcm);
+ }
+ if (data->txpower.value > 0) {
+ /* desired and maxpower dBm values are in Q5.2 */
+ if (phy->type == BCM43xx_PHYTYPE_A)
+ maxpower = bcm->sprom.maxpower_aphy;
+ else
+ maxpower = bcm->sprom.maxpower_bgphy;
+ radio->txpower_desired = limit_value(data->txpower.value << 2,
+ 0, maxpower);
+ bcm43xx_phy_xmitpower(bcm);
+ }
+ err = 0;
+
+out_unlock:
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_xmitpower(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct bcm43xx_radioinfo *radio;
+ unsigned long flags;
+ int err = -ENODEV;
+
+ bcm43xx_lock(bcm, flags);
+ if (!bcm->initialized)
+ goto out_unlock;
+ radio = bcm43xx_current_radio(bcm);
+ /* desired dBm value is in Q5.2 */
+ data->txpower.value = radio->txpower_desired >> 2;
+ data->txpower.fixed = 1;
+ data->txpower.flags = IW_TXPOW_DBM;
+ data->txpower.disabled = !(radio->enabled);
+
+ err = 0;
+out_unlock:
+ bcm43xx_unlock(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_set_encoding(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err;
+
+ err = ieee80211_wx_set_encode(bcm->ieee, info, data, extra);
+
+ return err;
+}
+
+static int bcm43xx_wx_set_encodingext(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err;
+
+ err = ieee80211_wx_set_encodeext(bcm->ieee, info, data, extra);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_encoding(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err;
+
+ err = ieee80211_wx_get_encode(bcm->ieee, info, data, extra);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_encodingext(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err;
+
+ err = ieee80211_wx_get_encodeext(bcm->ieee, info, data, extra);
+
+ return err;
+}
+
+static int bcm43xx_wx_set_interfmode(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int mode, err = 0;
+
+ mode = *((int *)extra);
+ switch (mode) {
+ case 0:
+ mode = BCM43xx_RADIO_INTERFMODE_NONE;
+ break;
+ case 1:
+ mode = BCM43xx_RADIO_INTERFMODE_NONWLAN;
+ break;
+ case 2:
+ mode = BCM43xx_RADIO_INTERFMODE_MANUALWLAN;
+ break;
+ case 3:
+ mode = BCM43xx_RADIO_INTERFMODE_AUTOWLAN;
+ break;
+ default:
+ printk(KERN_ERR PFX "set_interfmode allowed parameters are: "
+ "0 => None, 1 => Non-WLAN, 2 => WLAN, "
+ "3 => Auto-WLAN\n");
+ return -EINVAL;
+ }
+
+ bcm43xx_lock_mmio(bcm, flags);
+ if (bcm->initialized) {
+ err = bcm43xx_radio_set_interference_mitigation(bcm, mode);
+ if (err) {
+ printk(KERN_ERR PFX "Interference Mitigation not "
+ "supported by device\n");
+ }
+ } else {
+ if (mode == BCM43xx_RADIO_INTERFMODE_AUTOWLAN) {
+ printk(KERN_ERR PFX "Interference Mitigation mode Auto-WLAN "
+ "not supported while the interface is down.\n");
+ err = -ENODEV;
+ } else
+ bcm43xx_current_radio(bcm)->interfmode = mode;
+ }
+ bcm43xx_unlock_mmio(bcm, flags);
+
+ return err;
+}
+
+static int bcm43xx_wx_get_interfmode(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int mode;
+
+ bcm43xx_lock(bcm, flags);
+ mode = bcm43xx_current_radio(bcm)->interfmode;
+ bcm43xx_unlock(bcm, flags);
+
+ switch (mode) {
+ case BCM43xx_RADIO_INTERFMODE_NONE:
+ strncpy(extra, "0 (No Interference Mitigation)", MAX_WX_STRING);
+ break;
+ case BCM43xx_RADIO_INTERFMODE_NONWLAN:
+ strncpy(extra, "1 (Non-WLAN Interference Mitigation)", MAX_WX_STRING);
+ break;
+ case BCM43xx_RADIO_INTERFMODE_MANUALWLAN:
+ strncpy(extra, "2 (WLAN Interference Mitigation)", MAX_WX_STRING);
+ break;
+ default:
+ assert(0);
+ }
+ data->data.length = strlen(extra) + 1;
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_shortpreamble(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int on;
+
+ on = *((int *)extra);
+ bcm43xx_lock(bcm, flags);
+ bcm->short_preamble = !!on;
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_get_shortpreamble(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int on;
+
+ bcm43xx_lock(bcm, flags);
+ on = bcm->short_preamble;
+ bcm43xx_unlock(bcm, flags);
+
+ if (on)
+ strncpy(extra, "1 (Short Preamble enabled)", MAX_WX_STRING);
+ else
+ strncpy(extra, "0 (Short Preamble disabled)", MAX_WX_STRING);
+ data->data.length = strlen(extra) + 1;
+
+ return 0;
+}
+
+static int bcm43xx_wx_set_swencryption(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int on;
+
+ on = *((int *)extra);
+
+ bcm43xx_lock(bcm, flags);
+ bcm->ieee->host_encrypt = !!on;
+ bcm->ieee->host_decrypt = !!on;
+ bcm->ieee->host_build_iv = !on;
+ bcm43xx_unlock(bcm, flags);
+
+ return 0;
+}
+
+static int bcm43xx_wx_get_swencryption(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ unsigned long flags;
+ int on;
+
+ bcm43xx_lock(bcm, flags);
+ on = bcm->ieee->host_encrypt;
+ bcm43xx_unlock(bcm, flags);
+
+ if (on)
+ strncpy(extra, "1 (SW encryption enabled) ", MAX_WX_STRING);
+ else
+ strncpy(extra, "0 (SW encryption disabled) ", MAX_WX_STRING);
+ data->data.length = strlen(extra + 1);
+
+ return 0;
+}
+
+/* Enough buffer to hold a hexdump of the sprom data. */
+#define SPROM_BUFFERSIZE 512
+
+static int sprom2hex(const u16 *sprom, char *dump)
+{
+ int i, pos = 0;
+
+ for (i = 0; i < BCM43xx_SPROM_SIZE; i++) {
+ pos += snprintf(dump + pos, SPROM_BUFFERSIZE - pos - 1,
+ "%04X", swab16(sprom[i]) & 0xFFFF);
+ }
+
+ return pos + 1;
+}
+
+static int hex2sprom(u16 *sprom, const char *dump, unsigned int len)
+{
+ char tmp[5] = { 0 };
+ int cnt = 0;
+ unsigned long parsed;
+
+ if (len < BCM43xx_SPROM_SIZE * sizeof(u16) * 2)
+ return -EINVAL;
+ while (cnt < BCM43xx_SPROM_SIZE) {
+ memcpy(tmp, dump, 4);
+ dump += 4;
+ parsed = simple_strtoul(tmp, NULL, 16);
+ sprom[cnt++] = swab16((u16)parsed);
+ }
+
+ return 0;
+}
+
+static int bcm43xx_wx_sprom_read(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err = -EPERM;
+ u16 *sprom;
+ unsigned long flags;
+
+ if (!capable(CAP_SYS_RAWIO))
+ goto out;
+
+ err = -ENOMEM;
+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
+ GFP_KERNEL);
+ if (!sprom)
+ goto out;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ err = -ENODEV;
+ if (bcm->initialized)
+ err = bcm43xx_sprom_read(bcm, sprom);
+ bcm43xx_unlock_mmio(bcm, flags);
+ if (!err)
+ data->data.length = sprom2hex(sprom, extra);
+ kfree(sprom);
+out:
+ return err;
+}
+
+static int bcm43xx_wx_sprom_write(struct net_device *net_dev,
+ struct iw_request_info *info,
+ union iwreq_data *data,
+ char *extra)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ int err = -EPERM;
+ u16 *sprom;
+ unsigned long flags;
+ char *input;
+ unsigned int len;
+
+ if (!capable(CAP_SYS_RAWIO))
+ goto out;
+
+ err = -ENOMEM;
+ sprom = kmalloc(BCM43xx_SPROM_SIZE * sizeof(*sprom),
+ GFP_KERNEL);
+ if (!sprom)
+ goto out;
+
+ len = data->data.length;
+ extra[len - 1] = '\0';
+ input = strchr(extra, ':');
+ if (input) {
+ input++;
+ len -= input - extra;
+ } else
+ input = extra;
+ err = hex2sprom(sprom, input, len);
+ if (err)
+ goto out_kfree;
+
+ bcm43xx_lock_mmio(bcm, flags);
+ err = -ENODEV;
+ if (bcm->initialized)
+ err = bcm43xx_sprom_write(bcm, sprom);
+ bcm43xx_unlock_mmio(bcm, flags);
+out_kfree:
+ kfree(sprom);
+out:
+ return err;
+}
+
+/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */
+
+static struct iw_statistics *bcm43xx_get_wireless_stats(struct net_device *net_dev)
+{
+ struct bcm43xx_private *bcm = bcm43xx_priv(net_dev);
+ struct ieee80211softmac_device *mac = ieee80211_priv(net_dev);
+ struct iw_statistics *wstats;
+
+ wstats = &bcm->stats.wstats;
+ if (!mac->associated) {
+ wstats->miss.beacon = 0;
+// bcm->ieee->ieee_stats.tx_retry_limit_exceeded = 0; // FIXME: should this be cleared here?
+ wstats->discard.retries = 0;
+// bcm->ieee->ieee_stats.tx_discards_wrong_sa = 0; // FIXME: same question
+ wstats->discard.nwid = 0;
+// bcm->ieee->ieee_stats.rx_discards_undecryptable = 0; // FIXME: ditto
+ wstats->discard.code = 0;
+// bcm->ieee->ieee_stats.rx_fragments = 0; // FIXME: same here
+ wstats->discard.fragment = 0;
+ wstats->discard.misc = 0;
+ wstats->qual.qual = 0;
+ wstats->qual.level = 0;
+ wstats->qual.noise = 0;
+ wstats->qual.updated = 7;
+ wstats->qual.updated |= IW_QUAL_NOISE_INVALID |
+ IW_QUAL_QUAL_INVALID | IW_QUAL_LEVEL_INVALID;
+ return wstats;
+ }
+ /* fill in the real statistics when iface associated */
+ wstats->qual.qual = 100; // TODO: get the real signal quality
+ wstats->qual.level = 3 - bcm->stats.link_quality;
+ wstats->qual.noise = bcm->stats.noise;
+ wstats->qual.updated = IW_QUAL_QUAL_UPDATED | IW_QUAL_LEVEL_UPDATED |
+ IW_QUAL_NOISE_UPDATED;
+ wstats->discard.code = bcm->ieee->ieee_stats.rx_discards_undecryptable;
+ wstats->discard.retries = bcm->ieee->ieee_stats.tx_retry_limit_exceeded;
+ wstats->discard.nwid = bcm->ieee->ieee_stats.tx_discards_wrong_sa;
+ wstats->discard.fragment = bcm->ieee->ieee_stats.rx_fragments;
+ wstats->discard.misc = 0; // FIXME
+ wstats->miss.beacon = 0; // FIXME
+ return wstats;
+}
+
+
+#ifdef WX
+# undef WX
+#endif
+#define WX(ioctl) [(ioctl) - SIOCSIWCOMMIT]
+static const iw_handler bcm43xx_wx_handlers[] = {
+ /* Wireless Identification */
+ WX(SIOCGIWNAME) = bcm43xx_wx_get_name,
+ /* Basic operations */
+ WX(SIOCSIWFREQ) = bcm43xx_wx_set_channelfreq,
+ WX(SIOCGIWFREQ) = bcm43xx_wx_get_channelfreq,
+ WX(SIOCSIWMODE) = bcm43xx_wx_set_mode,
+ WX(SIOCGIWMODE) = bcm43xx_wx_get_mode,
+ /* Informative stuff */
+ WX(SIOCGIWRANGE) = bcm43xx_wx_get_rangeparams,
+ /* Access Point manipulation */
+ WX(SIOCSIWAP) = ieee80211softmac_wx_set_wap,
+ WX(SIOCGIWAP) = ieee80211softmac_wx_get_wap,
+ WX(SIOCSIWSCAN) = ieee80211softmac_wx_trigger_scan,
+ WX(SIOCGIWSCAN) = ieee80211softmac_wx_get_scan_results,
+ /* 802.11 specific support */
+ WX(SIOCSIWESSID) = ieee80211softmac_wx_set_essid,
+ WX(SIOCGIWESSID) = ieee80211softmac_wx_get_essid,
+ WX(SIOCSIWNICKN) = bcm43xx_wx_set_nick,
+ WX(SIOCGIWNICKN) = bcm43xx_wx_get_nick,
+ /* Other parameters */
+ WX(SIOCSIWRATE) = ieee80211softmac_wx_set_rate,
+ WX(SIOCGIWRATE) = ieee80211softmac_wx_get_rate,
+ WX(SIOCSIWRTS) = bcm43xx_wx_set_rts,
+ WX(SIOCGIWRTS) = bcm43xx_wx_get_rts,
+ WX(SIOCSIWFRAG) = bcm43xx_wx_set_frag,
+ WX(SIOCGIWFRAG) = bcm43xx_wx_get_frag,
+ WX(SIOCSIWTXPOW) = bcm43xx_wx_set_xmitpower,
+ WX(SIOCGIWTXPOW) = bcm43xx_wx_get_xmitpower,
+//TODO WX(SIOCSIWRETRY) = bcm43xx_wx_set_retry,
+//TODO WX(SIOCGIWRETRY) = bcm43xx_wx_get_retry,
+ /* Encoding */
+ WX(SIOCSIWENCODE) = bcm43xx_wx_set_encoding,
+ WX(SIOCGIWENCODE) = bcm43xx_wx_get_encoding,
+ WX(SIOCSIWENCODEEXT) = bcm43xx_wx_set_encodingext,
+ WX(SIOCGIWENCODEEXT) = bcm43xx_wx_get_encodingext,
+ /* Power saving */
+//TODO WX(SIOCSIWPOWER) = bcm43xx_wx_set_power,
+//TODO WX(SIOCGIWPOWER) = bcm43xx_wx_get_power,
+ WX(SIOCSIWGENIE) = ieee80211softmac_wx_set_genie,
+ WX(SIOCGIWGENIE) = ieee80211softmac_wx_get_genie,
+ WX(SIOCSIWAUTH) = ieee80211_wx_set_auth,
+ WX(SIOCGIWAUTH) = ieee80211_wx_get_auth,
+};
+#undef WX
+
+static const iw_handler bcm43xx_priv_wx_handlers[] = {
+ /* Set Interference Mitigation Mode. */
+ bcm43xx_wx_set_interfmode,
+ /* Get Interference Mitigation Mode. */
+ bcm43xx_wx_get_interfmode,
+ /* Enable/Disable Short Preamble mode. */
+ bcm43xx_wx_set_shortpreamble,
+ /* Get Short Preamble mode. */
+ bcm43xx_wx_get_shortpreamble,
+ /* Enable/Disable Software Encryption mode */
+ bcm43xx_wx_set_swencryption,
+ /* Get Software Encryption mode */
+ bcm43xx_wx_get_swencryption,
+ /* Write SRPROM data. */
+ bcm43xx_wx_sprom_write,
+ /* Read SPROM data. */
+ bcm43xx_wx_sprom_read,
+};
+
+#define PRIV_WX_SET_INTERFMODE (SIOCIWFIRSTPRIV + 0)
+#define PRIV_WX_GET_INTERFMODE (SIOCIWFIRSTPRIV + 1)
+#define PRIV_WX_SET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 2)
+#define PRIV_WX_GET_SHORTPREAMBLE (SIOCIWFIRSTPRIV + 3)
+#define PRIV_WX_SET_SWENCRYPTION (SIOCIWFIRSTPRIV + 4)
+#define PRIV_WX_GET_SWENCRYPTION (SIOCIWFIRSTPRIV + 5)
+#define PRIV_WX_SPROM_WRITE (SIOCIWFIRSTPRIV + 6)
+#define PRIV_WX_SPROM_READ (SIOCIWFIRSTPRIV + 7)
+
+#define PRIV_WX_DUMMY(ioctl) \
+ { \
+ .cmd = (ioctl), \
+ .name = "__unused" \
+ }
+
+static const struct iw_priv_args bcm43xx_priv_wx_args[] = {
+ {
+ .cmd = PRIV_WX_SET_INTERFMODE,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_interfmode",
+ },
+ {
+ .cmd = PRIV_WX_GET_INTERFMODE,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_interfmode",
+ },
+ {
+ .cmd = PRIV_WX_SET_SHORTPREAMBLE,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_shortpreambl",
+ },
+ {
+ .cmd = PRIV_WX_GET_SHORTPREAMBLE,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_shortpreambl",
+ },
+ {
+ .cmd = PRIV_WX_SET_SWENCRYPTION,
+ .set_args = IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1,
+ .name = "set_swencryption",
+ },
+ {
+ .cmd = PRIV_WX_GET_SWENCRYPTION,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | MAX_WX_STRING,
+ .name = "get_swencryption",
+ },
+ {
+ .cmd = PRIV_WX_SPROM_WRITE,
+ .set_args = IW_PRIV_TYPE_CHAR | SPROM_BUFFERSIZE,
+ .name = "write_sprom",
+ },
+ {
+ .cmd = PRIV_WX_SPROM_READ,
+ .get_args = IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | SPROM_BUFFERSIZE,
+ .name = "read_sprom",
+ },
+};
+
+const struct iw_handler_def bcm43xx_wx_handlers_def = {
+ .standard = bcm43xx_wx_handlers,
+ .num_standard = ARRAY_SIZE(bcm43xx_wx_handlers),
+ .num_private = ARRAY_SIZE(bcm43xx_priv_wx_handlers),
+ .num_private_args = ARRAY_SIZE(bcm43xx_priv_wx_args),
+ .private = bcm43xx_priv_wx_handlers,
+ .private_args = bcm43xx_priv_wx_args,
+ .get_wireless_stats = bcm43xx_get_wireless_stats,
+};
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.h b/drivers/net/wireless/bcm43xx/bcm43xx_wx.h
new file mode 100644
index 000000000000..1f29ff3aa4c3
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.h
@@ -0,0 +1,36 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.ch>
+
+ Some parts of the code in this file are derived from the ipw2200
+ driver Copyright(c) 2003 - 2004 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Steet, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+*/
+
+#ifndef BCM43xx_WX_H_
+#define BCM43xx_WX_H_
+
+extern const struct iw_handler_def bcm43xx_wx_handlers_def;
+
+#endif /* BCM43xx_WX_H_ */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
new file mode 100644
index 000000000000..d8ece28c079f
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
@@ -0,0 +1,582 @@
+/*
+
+ Broadcom BCM43xx wireless driver
+
+ Transmission (TX/RX) related functions.
+
+ Copyright (c) 2005 Martin Langer <martin-langer@gmx.de>,
+ Stefano Brivio <st3@riseup.net>
+ Michael Buesch <mbuesch@freenet.de>
+ Danny van Dyk <kugelfang@gentoo.org>
+ Andreas Jaggi <andreas.jaggi@waterwave.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
+ 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 "bcm43xx_xmit.h"
+
+#include <linux/etherdevice.h>
+
+
+/* Extract the bitrate out of a CCK PLCP header. */
+static u8 bcm43xx_plcp_get_bitrate_cck(struct bcm43xx_plcp_hdr4 *plcp)
+{
+ switch (plcp->raw[0]) {
+ case 0x0A:
+ return IEEE80211_CCK_RATE_1MB;
+ case 0x14:
+ return IEEE80211_CCK_RATE_2MB;
+ case 0x37:
+ return IEEE80211_CCK_RATE_5MB;
+ case 0x6E:
+ return IEEE80211_CCK_RATE_11MB;
+ }
+ assert(0);
+ return 0;
+}
+
+/* Extract the bitrate out of an OFDM PLCP header. */
+static u8 bcm43xx_plcp_get_bitrate_ofdm(struct bcm43xx_plcp_hdr4 *plcp)
+{
+ switch (plcp->raw[0] & 0xF) {
+ case 0xB:
+ return IEEE80211_OFDM_RATE_6MB;
+ case 0xF:
+ return IEEE80211_OFDM_RATE_9MB;
+ case 0xA:
+ return IEEE80211_OFDM_RATE_12MB;
+ case 0xE:
+ return IEEE80211_OFDM_RATE_18MB;
+ case 0x9:
+ return IEEE80211_OFDM_RATE_24MB;
+ case 0xD:
+ return IEEE80211_OFDM_RATE_36MB;
+ case 0x8:
+ return IEEE80211_OFDM_RATE_48MB;
+ case 0xC:
+ return IEEE80211_OFDM_RATE_54MB;
+ }
+ assert(0);
+ return 0;
+}
+
+u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate)
+{
+ switch (bitrate) {
+ case IEEE80211_CCK_RATE_1MB:
+ return 0x0A;
+ case IEEE80211_CCK_RATE_2MB:
+ return 0x14;
+ case IEEE80211_CCK_RATE_5MB:
+ return 0x37;
+ case IEEE80211_CCK_RATE_11MB:
+ return 0x6E;
+ }
+ assert(0);
+ return 0;
+}
+
+u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate)
+{
+ switch (bitrate) {
+ case IEEE80211_OFDM_RATE_6MB:
+ return 0xB;
+ case IEEE80211_OFDM_RATE_9MB:
+ return 0xF;
+ case IEEE80211_OFDM_RATE_12MB:
+ return 0xA;
+ case IEEE80211_OFDM_RATE_18MB:
+ return 0xE;
+ case IEEE80211_OFDM_RATE_24MB:
+ return 0x9;
+ case IEEE80211_OFDM_RATE_36MB:
+ return 0xD;
+ case IEEE80211_OFDM_RATE_48MB:
+ return 0x8;
+ case IEEE80211_OFDM_RATE_54MB:
+ return 0xC;
+ }
+ assert(0);
+ return 0;
+}
+
+static void bcm43xx_generate_plcp_hdr(struct bcm43xx_plcp_hdr4 *plcp,
+ const u16 octets, const u8 bitrate,
+ const int ofdm_modulation)
+{
+ __le32 *data = &(plcp->data);
+ __u8 *raw = plcp->raw;
+
+ if (ofdm_modulation) {
+ *data = bcm43xx_plcp_get_ratecode_ofdm(bitrate);
+ assert(!(octets & 0xF000));
+ *data |= (octets << 5);
+ *data = cpu_to_le32(*data);
+ } else {
+ u32 plen;
+
+ plen = octets * 16 / bitrate;
+ if ((octets * 16 % bitrate) > 0) {
+ plen++;
+ if ((bitrate == IEEE80211_CCK_RATE_11MB)
+ && ((octets * 8 % 11) < 4)) {
+ raw[1] = 0x84;
+ } else
+ raw[1] = 0x04;
+ } else
+ raw[1] = 0x04;
+ *data |= cpu_to_le32(plen << 16);
+ raw[0] = bcm43xx_plcp_get_ratecode_cck(bitrate);
+ }
+}
+
+static u8 bcm43xx_calc_fallback_rate(u8 bitrate)
+{
+ switch (bitrate) {
+ case IEEE80211_CCK_RATE_1MB:
+ return IEEE80211_CCK_RATE_1MB;
+ case IEEE80211_CCK_RATE_2MB:
+ return IEEE80211_CCK_RATE_1MB;
+ case IEEE80211_CCK_RATE_5MB:
+ return IEEE80211_CCK_RATE_2MB;
+ case IEEE80211_CCK_RATE_11MB:
+ return IEEE80211_CCK_RATE_5MB;
+ case IEEE80211_OFDM_RATE_6MB:
+ return IEEE80211_CCK_RATE_5MB;
+ case IEEE80211_OFDM_RATE_9MB:
+ return IEEE80211_OFDM_RATE_6MB;
+ case IEEE80211_OFDM_RATE_12MB:
+ return IEEE80211_OFDM_RATE_9MB;
+ case IEEE80211_OFDM_RATE_18MB:
+ return IEEE80211_OFDM_RATE_12MB;
+ case IEEE80211_OFDM_RATE_24MB:
+ return IEEE80211_OFDM_RATE_18MB;
+ case IEEE80211_OFDM_RATE_36MB:
+ return IEEE80211_OFDM_RATE_24MB;
+ case IEEE80211_OFDM_RATE_48MB:
+ return IEEE80211_OFDM_RATE_36MB;
+ case IEEE80211_OFDM_RATE_54MB:
+ return IEEE80211_OFDM_RATE_48MB;
+ }
+ assert(0);
+ return 0;
+}
+
+static
+__le16 bcm43xx_calc_duration_id(const struct ieee80211_hdr *wireless_header,
+ u8 bitrate)
+{
+ const u16 frame_ctl = le16_to_cpu(wireless_header->frame_ctl);
+ __le16 duration_id = wireless_header->duration_id;
+
+ switch (WLAN_FC_GET_TYPE(frame_ctl)) {
+ case IEEE80211_FTYPE_DATA:
+ case IEEE80211_FTYPE_MGMT:
+ //TODO: Steal the code from ieee80211, once it is completed there.
+ break;
+ case IEEE80211_FTYPE_CTL:
+ /* Use the original duration/id. */
+ break;
+ default:
+ assert(0);
+ }
+
+ return duration_id;
+}
+
+static inline
+u16 ceiling_div(u16 dividend, u16 divisor)
+{
+ return ((dividend + divisor - 1) / divisor);
+}
+
+static void bcm43xx_generate_rts(const struct bcm43xx_phyinfo *phy,
+ struct bcm43xx_txhdr *txhdr,
+ u16 *flags,
+ u8 bitrate,
+ const struct ieee80211_hdr_4addr *wlhdr)
+{
+ u16 fctl;
+ u16 dur;
+ u8 fallback_bitrate;
+ int ofdm_modulation;
+ int fallback_ofdm_modulation;
+// u8 *sa, *da;
+ u16 flen;
+
+//FIXME sa = ieee80211_get_SA((struct ieee80211_hdr *)wlhdr);
+//FIXME da = ieee80211_get_DA((struct ieee80211_hdr *)wlhdr);
+ fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);
+ ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));
+ fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));
+
+ flen = sizeof(u16) + sizeof(u16) + ETH_ALEN + ETH_ALEN + IEEE80211_FCS_LEN,
+ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_plcp),
+ flen, bitrate,
+ !ieee80211_is_cck_rate(bitrate));
+ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->rts_cts_fallback_plcp),
+ flen, fallback_bitrate,
+ !ieee80211_is_cck_rate(fallback_bitrate));
+ fctl = IEEE80211_FTYPE_CTL;
+ fctl |= IEEE80211_STYPE_RTS;
+ dur = le16_to_cpu(wlhdr->duration_id);
+/*FIXME: should we test for dur==0 here and let it unmodified in this case?
+ * The following assert checks for this case...
+ */
+assert(dur);
+/*FIXME: The duration calculation is not really correct.
+ * I am not 100% sure which bitrate to use. We use the RTS rate here,
+ * but this is likely to be wrong.
+ */
+ if (phy->type == BCM43xx_PHYTYPE_A) {
+ /* Three times SIFS */
+ dur += 16 * 3;
+ /* Add ACK duration. */
+ dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,
+ bitrate * 4);
+ /* Add CTS duration. */
+ dur += ceiling_div((16 + 8 * (14 /*bytes*/) + 6) * 10,
+ bitrate * 4);
+ } else {
+ /* Three times SIFS */
+ dur += 10 * 3;
+ /* Add ACK duration. */
+ dur += ceiling_div(8 * (14 /*bytes*/) * 10,
+ bitrate);
+ /* Add CTS duration. */
+ dur += ceiling_div(8 * (14 /*bytes*/) * 10,
+ bitrate);
+ }
+
+ txhdr->rts_cts_frame_control = cpu_to_le16(fctl);
+ txhdr->rts_cts_dur = cpu_to_le16(dur);
+//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(wlhdr->addr1), BCM43xx_MACARG(wlhdr->addr2), BCM43xx_MACARG(wlhdr->addr3));
+//printk(BCM43xx_MACFMT " " BCM43xx_MACFMT "\n", BCM43xx_MACARG(sa), BCM43xx_MACARG(da));
+ memcpy(txhdr->rts_cts_mac1, wlhdr->addr1, ETH_ALEN);//FIXME!
+// memcpy(txhdr->rts_cts_mac2, sa, ETH_ALEN);
+
+ *flags |= BCM43xx_TXHDRFLAG_RTSCTS;
+ *flags |= BCM43xx_TXHDRFLAG_RTS;
+ if (ofdm_modulation)
+ *flags |= BCM43xx_TXHDRFLAG_RTSCTS_OFDM;
+ if (fallback_ofdm_modulation)
+ *flags |= BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM;
+}
+
+void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm,
+ struct bcm43xx_txhdr *txhdr,
+ const unsigned char *fragment_data,
+ const unsigned int fragment_len,
+ const int is_first_fragment,
+ const u16 cookie)
+{
+ const struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ const struct ieee80211_hdr_4addr *wireless_header = (const struct ieee80211_hdr_4addr *)fragment_data;
+ const struct ieee80211_security *secinfo = &bcm->ieee->sec;
+ u8 bitrate;
+ u8 fallback_bitrate;
+ int ofdm_modulation;
+ int fallback_ofdm_modulation;
+ u16 plcp_fragment_len = fragment_len;
+ u16 flags = 0;
+ u16 control = 0;
+ u16 wsec_rate = 0;
+ u16 encrypt_frame;
+
+ /* Now construct the TX header. */
+ memset(txhdr, 0, sizeof(*txhdr));
+
+ bitrate = bcm->softmac->txrates.default_rate;
+ ofdm_modulation = !(ieee80211_is_cck_rate(bitrate));
+ fallback_bitrate = bcm43xx_calc_fallback_rate(bitrate);
+ fallback_ofdm_modulation = !(ieee80211_is_cck_rate(fallback_bitrate));
+
+ /* Set Frame Control from 80211 header. */
+ txhdr->frame_control = wireless_header->frame_ctl;
+ /* Copy address1 from 80211 header. */
+ memcpy(txhdr->mac1, wireless_header->addr1, 6);
+ /* Set the fallback duration ID. */
+ txhdr->fallback_dur_id = bcm43xx_calc_duration_id((const struct ieee80211_hdr *)wireless_header,
+ fallback_bitrate);
+ /* Set the cookie (used as driver internal ID for the frame) */
+ txhdr->cookie = cpu_to_le16(cookie);
+
+ /* Hardware appends FCS. */
+ plcp_fragment_len += IEEE80211_FCS_LEN;
+
+ /* Hardware encryption. */
+ encrypt_frame = le16_to_cpup(&wireless_header->frame_ctl) & IEEE80211_FCTL_PROTECTED;
+ if (encrypt_frame && !bcm->ieee->host_encrypt) {
+ const struct ieee80211_hdr_3addr *hdr = (struct ieee80211_hdr_3addr *)wireless_header;
+ memcpy(txhdr->wep_iv, hdr->payload, 4);
+ /* Hardware appends ICV. */
+ plcp_fragment_len += 4;
+
+ wsec_rate |= (bcm->key[secinfo->active_key].algorithm << BCM43xx_TXHDR_WSEC_ALGO_SHIFT)
+ & BCM43xx_TXHDR_WSEC_ALGO_MASK;
+ wsec_rate |= (secinfo->active_key << BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT)
+ & BCM43xx_TXHDR_WSEC_KEYINDEX_MASK;
+ }
+
+ /* Generate the PLCP header and the fallback PLCP header. */
+ bcm43xx_generate_plcp_hdr((struct bcm43xx_plcp_hdr4 *)(&txhdr->plcp),
+ plcp_fragment_len,
+ bitrate, ofdm_modulation);
+ bcm43xx_generate_plcp_hdr(&txhdr->fallback_plcp, plcp_fragment_len,
+ fallback_bitrate, fallback_ofdm_modulation);
+
+ /* Set the CONTROL field */
+ if (ofdm_modulation)
+ control |= BCM43xx_TXHDRCTL_OFDM;
+ if (bcm->short_preamble) //FIXME: could be the other way around, please test
+ control |= BCM43xx_TXHDRCTL_SHORT_PREAMBLE;
+ control |= (phy->antenna_diversity << BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT)
+ & BCM43xx_TXHDRCTL_ANTENNADIV_MASK;
+
+ /* Set the FLAGS field */
+ if (!is_multicast_ether_addr(wireless_header->addr1) &&
+ !is_broadcast_ether_addr(wireless_header->addr1))
+ flags |= BCM43xx_TXHDRFLAG_EXPECTACK;
+ if (1 /* FIXME: PS poll?? */)
+ flags |= 0x10; // FIXME: unknown meaning.
+ if (fallback_ofdm_modulation)
+ flags |= BCM43xx_TXHDRFLAG_FALLBACKOFDM;
+ if (is_first_fragment)
+ flags |= BCM43xx_TXHDRFLAG_FIRSTFRAGMENT;
+
+ /* Set WSEC/RATE field */
+ wsec_rate |= (txhdr->plcp.raw[0] << BCM43xx_TXHDR_RATE_SHIFT)
+ & BCM43xx_TXHDR_RATE_MASK;
+
+ /* Generate the RTS/CTS packet, if required. */
+ /* FIXME: We should first try with CTS-to-self,
+ * if we are on 80211g. If we get too many
+ * failures (hidden nodes), we should switch back to RTS/CTS.
+ */
+ if (0/*FIXME txctl->use_rts_cts*/) {
+ bcm43xx_generate_rts(phy, txhdr, &flags,
+ 0/*FIXME txctl->rts_cts_rate*/,
+ wireless_header);
+ }
+
+ txhdr->flags = cpu_to_le16(flags);
+ txhdr->control = cpu_to_le16(control);
+ txhdr->wsec_rate = cpu_to_le16(wsec_rate);
+}
+
+static s8 bcm43xx_rssi_postprocess(struct bcm43xx_private *bcm,
+ u8 in_rssi, int ofdm,
+ int adjust_2053, int adjust_2050)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ s32 tmp;
+
+ switch (radio->version) {
+ case 0x2050:
+ if (ofdm) {
+ tmp = in_rssi;
+ if (tmp > 127)
+ tmp -= 256;
+ tmp *= 73;
+ tmp /= 64;
+ if (adjust_2050)
+ tmp += 25;
+ else
+ tmp -= 3;
+ } else {
+ if (bcm->sprom.boardflags & BCM43xx_BFL_RSSI) {
+ if (in_rssi > 63)
+ in_rssi = 63;
+ tmp = radio->nrssi_lt[in_rssi];
+ tmp = 31 - tmp;
+ tmp *= -131;
+ tmp /= 128;
+ tmp -= 57;
+ } else {
+ tmp = in_rssi;
+ tmp = 31 - tmp;
+ tmp *= -149;
+ tmp /= 128;
+ tmp -= 68;
+ }
+ if (phy->type == BCM43xx_PHYTYPE_G &&
+ adjust_2050)
+ tmp += 25;
+ }
+ break;
+ case 0x2060:
+ if (in_rssi > 127)
+ tmp = in_rssi - 256;
+ else
+ tmp = in_rssi;
+ break;
+ default:
+ tmp = in_rssi;
+ tmp -= 11;
+ tmp *= 103;
+ tmp /= 64;
+ if (adjust_2053)
+ tmp -= 109;
+ else
+ tmp -= 83;
+ }
+
+ return (s8)tmp;
+}
+
+//TODO
+#if 0
+static s8 bcm43xx_rssinoise_postprocess(struct bcm43xx_private *bcm,
+ u8 in_rssi)
+{
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ s8 ret;
+
+ if (phy->type == BCM43xx_PHYTYPE_A) {
+ //TODO: Incomplete specs.
+ ret = 0;
+ } else
+ ret = bcm43xx_rssi_postprocess(bcm, in_rssi, 0, 1, 1);
+
+ return ret;
+}
+#endif
+
+int bcm43xx_rx(struct bcm43xx_private *bcm,
+ struct sk_buff *skb,
+ struct bcm43xx_rxhdr *rxhdr)
+{
+ struct bcm43xx_radioinfo *radio = bcm43xx_current_radio(bcm);
+ struct bcm43xx_phyinfo *phy = bcm43xx_current_phy(bcm);
+ struct bcm43xx_plcp_hdr4 *plcp;
+ struct ieee80211_rx_stats stats;
+ struct ieee80211_hdr_4addr *wlhdr;
+ u16 frame_ctl;
+ int is_packet_for_us = 0;
+ int err = -EINVAL;
+ const u16 rxflags1 = le16_to_cpu(rxhdr->flags1);
+ const u16 rxflags2 = le16_to_cpu(rxhdr->flags2);
+ const u16 rxflags3 = le16_to_cpu(rxhdr->flags3);
+ const int is_ofdm = !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_OFDM);
+
+ if (rxflags2 & BCM43xx_RXHDR_FLAGS2_TYPE2FRAME) {
+ plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data + 2);
+ /* Skip two unknown bytes and the PLCP header. */
+ skb_pull(skb, 2 + sizeof(struct bcm43xx_plcp_hdr6));
+ } else {
+ plcp = (struct bcm43xx_plcp_hdr4 *)(skb->data);
+ /* Skip the PLCP header. */
+ skb_pull(skb, sizeof(struct bcm43xx_plcp_hdr6));
+ }
+ /* The SKB contains the PAYLOAD (wireless header + data)
+ * at this point. The FCS at the end is stripped.
+ */
+
+ memset(&stats, 0, sizeof(stats));
+ stats.mac_time = le16_to_cpu(rxhdr->mactime);
+ stats.rssi = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm,
+ !!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ),
+ !!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ));
+ stats.signal = rxhdr->signal_quality; //FIXME
+//TODO stats.noise =
+ if (is_ofdm)
+ stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp);
+ else
+ stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp);
+//printk("RX ofdm %d, rate == %u\n", is_ofdm, stats.rate);
+ stats.received_channel = radio->channel;
+//TODO stats.control =
+ stats.mask = IEEE80211_STATMASK_SIGNAL |
+//TODO IEEE80211_STATMASK_NOISE |
+ IEEE80211_STATMASK_RATE |
+ IEEE80211_STATMASK_RSSI;
+ if (phy->type == BCM43xx_PHYTYPE_A)
+ stats.freq = IEEE80211_52GHZ_BAND;
+ else
+ stats.freq = IEEE80211_24GHZ_BAND;
+ stats.len = skb->len;
+
+ bcm->stats.last_rx = jiffies;
+ if (bcm->ieee->iw_mode == IW_MODE_MONITOR) {
+ err = ieee80211_rx(bcm->ieee, skb, &stats);
+ return (err == 0) ? -EINVAL : 0;
+ }
+
+ wlhdr = (struct ieee80211_hdr_4addr *)(skb->data);
+
+ switch (bcm->ieee->iw_mode) {
+ case IW_MODE_ADHOC:
+ if (memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 ||
+ memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 ||
+ is_broadcast_ether_addr(wlhdr->addr1) ||
+ is_multicast_ether_addr(wlhdr->addr1) ||
+ bcm->net_dev->flags & IFF_PROMISC)
+ is_packet_for_us = 1;
+ break;
+ case IW_MODE_INFRA:
+ default:
+ /* When receiving multicast or broadcast packets, filter out
+ the packets we send ourself; we shouldn't see those */
+ if (memcmp(wlhdr->addr3, bcm->ieee->bssid, ETH_ALEN) == 0 ||
+ memcmp(wlhdr->addr1, bcm->net_dev->dev_addr, ETH_ALEN) == 0 ||
+ (memcmp(wlhdr->addr3, bcm->net_dev->dev_addr, ETH_ALEN) &&
+ (is_broadcast_ether_addr(wlhdr->addr1) ||
+ is_multicast_ether_addr(wlhdr->addr1) ||
+ bcm->net_dev->flags & IFF_PROMISC)))
+ is_packet_for_us = 1;
+ break;
+ }
+
+ frame_ctl = le16_to_cpu(wlhdr->frame_ctl);
+ if ((frame_ctl & IEEE80211_FCTL_PROTECTED) && !bcm->ieee->host_decrypt) {
+ frame_ctl &= ~IEEE80211_FCTL_PROTECTED;
+ wlhdr->frame_ctl = cpu_to_le16(frame_ctl);
+ /* trim IV and ICV */
+ /* FIXME: this must be done only for WEP encrypted packets */
+ if (skb->len < 32) {
+ dprintkl(KERN_ERR PFX "RX packet dropped (PROTECTED flag "
+ "set and length < 32)\n");
+ return -EINVAL;
+ } else {
+ memmove(skb->data + 4, skb->data, 24);
+ skb_pull(skb, 4);
+ skb_trim(skb, skb->len - 4);
+ stats.len -= 8;
+ }
+ wlhdr = (struct ieee80211_hdr_4addr *)(skb->data);
+ }
+
+ switch (WLAN_FC_GET_TYPE(frame_ctl)) {
+ case IEEE80211_FTYPE_MGMT:
+ ieee80211_rx_mgt(bcm->ieee, wlhdr, &stats);
+ break;
+ case IEEE80211_FTYPE_DATA:
+ if (is_packet_for_us) {
+ err = ieee80211_rx(bcm->ieee, skb, &stats);
+ err = (err == 0) ? -EINVAL : 0;
+ }
+ break;
+ case IEEE80211_FTYPE_CTL:
+ break;
+ default:
+ assert(0);
+ return -EINVAL;
+ }
+
+ return err;
+}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h
new file mode 100644
index 000000000000..2aed19e35c77
--- /dev/null
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.h
@@ -0,0 +1,156 @@
+#ifndef BCM43xx_XMIT_H_
+#define BCM43xx_XMIT_H_
+
+#include "bcm43xx_main.h"
+
+
+#define _bcm43xx_declare_plcp_hdr(size) \
+ struct bcm43xx_plcp_hdr##size { \
+ union { \
+ __le32 data; \
+ __u8 raw[size]; \
+ } __attribute__((__packed__)); \
+ } __attribute__((__packed__))
+
+/* struct bcm43xx_plcp_hdr4 */
+_bcm43xx_declare_plcp_hdr(4);
+/* struct bcm43xx_plcp_hdr6 */
+_bcm43xx_declare_plcp_hdr(6);
+
+#undef _bcm43xx_declare_plcp_hdr
+
+/* Device specific TX header. To be prepended to TX frames. */
+struct bcm43xx_txhdr {
+ union {
+ struct {
+ __le16 flags;
+ __le16 wsec_rate;
+ __le16 frame_control;
+ u16 unknown_zeroed_0;
+ __le16 control;
+ u8 wep_iv[10];
+ u8 unknown_wsec_tkip_data[3]; //FIXME
+ PAD_BYTES(3);
+ u8 mac1[6];
+ u16 unknown_zeroed_1;
+ struct bcm43xx_plcp_hdr4 rts_cts_fallback_plcp;
+ __le16 rts_cts_dur_fallback;
+ struct bcm43xx_plcp_hdr4 fallback_plcp;
+ __le16 fallback_dur_id;
+ PAD_BYTES(2);
+ __le16 cookie;
+ __le16 unknown_scb_stuff; //FIXME
+ struct bcm43xx_plcp_hdr6 rts_cts_plcp;
+ __le16 rts_cts_frame_control;
+ __le16 rts_cts_dur;
+ u8 rts_cts_mac1[6];
+ u8 rts_cts_mac2[6];
+ PAD_BYTES(2);
+ struct bcm43xx_plcp_hdr6 plcp;
+ } __attribute__((__packed__));
+ u8 raw[82];
+ } __attribute__((__packed__));
+} __attribute__((__packed__));
+
+/* Values/Masks for the device TX header */
+#define BCM43xx_TXHDRFLAG_EXPECTACK 0x0001
+#define BCM43xx_TXHDRFLAG_RTSCTS 0x0002
+#define BCM43xx_TXHDRFLAG_RTS 0x0004
+#define BCM43xx_TXHDRFLAG_FIRSTFRAGMENT 0x0008
+#define BCM43xx_TXHDRFLAG_DESTPSMODE 0x0020
+#define BCM43xx_TXHDRFLAG_RTSCTS_OFDM 0x0080
+#define BCM43xx_TXHDRFLAG_FALLBACKOFDM 0x0100
+#define BCM43xx_TXHDRFLAG_RTSCTSFALLBACK_OFDM 0x0200
+#define BCM43xx_TXHDRFLAG_CTS 0x0400
+#define BCM43xx_TXHDRFLAG_FRAMEBURST 0x0800
+
+#define BCM43xx_TXHDRCTL_OFDM 0x0001
+#define BCM43xx_TXHDRCTL_SHORT_PREAMBLE 0x0010
+#define BCM43xx_TXHDRCTL_ANTENNADIV_MASK 0x0030
+#define BCM43xx_TXHDRCTL_ANTENNADIV_SHIFT 8
+
+#define BCM43xx_TXHDR_RATE_MASK 0x0F00
+#define BCM43xx_TXHDR_RATE_SHIFT 8
+#define BCM43xx_TXHDR_RTSRATE_MASK 0xF000
+#define BCM43xx_TXHDR_RTSRATE_SHIFT 12
+#define BCM43xx_TXHDR_WSEC_KEYINDEX_MASK 0x00F0
+#define BCM43xx_TXHDR_WSEC_KEYINDEX_SHIFT 4
+#define BCM43xx_TXHDR_WSEC_ALGO_MASK 0x0003
+#define BCM43xx_TXHDR_WSEC_ALGO_SHIFT 0
+
+void bcm43xx_generate_txhdr(struct bcm43xx_private *bcm,
+ struct bcm43xx_txhdr *txhdr,
+ const unsigned char *fragment_data,
+ const unsigned int fragment_len,
+ const int is_first_fragment,
+ const u16 cookie);
+
+/* RX header as received from the hardware. */
+struct bcm43xx_rxhdr {
+ /* Frame Length. Must be generated explicitely in PIO mode. */
+ __le16 frame_length;
+ PAD_BYTES(2);
+ /* Flags field 1 */
+ __le16 flags1;
+ u8 rssi;
+ u8 signal_quality;
+ PAD_BYTES(2);
+ /* Flags field 3 */
+ __le16 flags3;
+ /* Flags field 2 */
+ __le16 flags2;
+ /* Lower 16bits of the TSF at the time the frame started. */
+ __le16 mactime;
+ PAD_BYTES(14);
+} __attribute__((__packed__));
+
+#define BCM43xx_RXHDR_FLAGS1_OFDM (1 << 0)
+/*#define BCM43xx_RXHDR_FLAGS1_SIGNAL??? (1 << 3) FIXME */
+#define BCM43xx_RXHDR_FLAGS1_SHORTPREAMBLE (1 << 7)
+#define BCM43xx_RXHDR_FLAGS1_2053RSSIADJ (1 << 14)
+
+#define BCM43xx_RXHDR_FLAGS2_INVALIDFRAME (1 << 0)
+#define BCM43xx_RXHDR_FLAGS2_TYPE2FRAME (1 << 2)
+/*FIXME: WEP related flags */
+
+#define BCM43xx_RXHDR_FLAGS3_2050RSSIADJ (1 << 10)
+
+/* Transmit Status as received from the hardware. */
+struct bcm43xx_hwxmitstatus {
+ PAD_BYTES(4);
+ __le16 cookie;
+ u8 flags;
+ u8 cnt1:4,
+ cnt2:4;
+ PAD_BYTES(2);
+ __le16 seq;
+ __le16 unknown; //FIXME
+} __attribute__((__packed__));
+
+/* Transmit Status in CPU byteorder. */
+struct bcm43xx_xmitstatus {
+ u16 cookie;
+ u8 flags;
+ u8 cnt1:4,
+ cnt2:4;
+ u16 seq;
+ u16 unknown; //FIXME
+};
+
+#define BCM43xx_TXSTAT_FLAG_ACK 0x01
+//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x02
+//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x04
+//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x08
+//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x10
+#define BCM43xx_TXSTAT_FLAG_IGNORE 0x20
+//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x40
+//TODO #define BCM43xx_TXSTAT_FLAG_??? 0x80
+
+u8 bcm43xx_plcp_get_ratecode_cck(const u8 bitrate);
+u8 bcm43xx_plcp_get_ratecode_ofdm(const u8 bitrate);
+
+int bcm43xx_rx(struct bcm43xx_private *bcm,
+ struct sk_buff *skb,
+ struct bcm43xx_rxhdr *rxhdr);
+
+#endif /* BCM43xx_XMIT_H_ */
diff --git a/drivers/net/wireless/hostap/hostap_80211.h b/drivers/net/wireless/hostap/hostap_80211.h
index 1fc72fe511e9..cc1ee7f4f5f8 100644
--- a/drivers/net/wireless/hostap/hostap_80211.h
+++ b/drivers/net/wireless/hostap/hostap_80211.h
@@ -92,8 +92,6 @@ void hostap_dump_rx_80211(const char *name, struct sk_buff *skb,
void hostap_dump_tx_80211(const char *name, struct sk_buff *skb);
int hostap_data_start_xmit(struct sk_buff *skb, struct net_device *dev);
int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev);
-struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
- struct ieee80211_crypt_data *crypt);
int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev);
#endif /* HOSTAP_80211_H */
diff --git a/drivers/net/wireless/hostap/hostap_80211_tx.c b/drivers/net/wireless/hostap/hostap_80211_tx.c
index 4a85e63906f1..06a5214145e3 100644
--- a/drivers/net/wireless/hostap/hostap_80211_tx.c
+++ b/drivers/net/wireless/hostap/hostap_80211_tx.c
@@ -299,8 +299,8 @@ int hostap_mgmt_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Called only from software IRQ */
-struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
- struct ieee80211_crypt_data *crypt)
+static struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
+ struct ieee80211_crypt_data *crypt)
{
struct hostap_interface *iface;
local_info_t *local;
@@ -317,7 +317,7 @@ struct sk_buff * hostap_tx_encrypt(struct sk_buff *skb,
}
if (local->tkip_countermeasures &&
- crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
+ strcmp(crypt->ops->name, "TKIP") == 0) {
hdr = (struct ieee80211_hdr_4addr *) skb->data;
if (net_ratelimit()) {
printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
@@ -469,7 +469,7 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
}
if (local->ieee_802_1x && meta->ethertype == ETH_P_PAE && tx.crypt &&
- !(fc & IEEE80211_FCTL_VERS)) {
+ !(fc & IEEE80211_FCTL_PROTECTED)) {
no_encrypt = 1;
PDEBUG(DEBUG_EXTRA2, "%s: TX: IEEE 802.1X - passing "
"unencrypted EAPOL frame\n", dev->name);
@@ -535,5 +535,4 @@ int hostap_master_start_xmit(struct sk_buff *skb, struct net_device *dev)
EXPORT_SYMBOL(hostap_dump_tx_80211);
-EXPORT_SYMBOL(hostap_tx_encrypt);
EXPORT_SYMBOL(hostap_master_start_xmit);
diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c
index 93f8a8fa8890..a5d826237b26 100644
--- a/drivers/parisc/ccio-dma.c
+++ b/drivers/parisc/ccio-dma.c
@@ -1560,7 +1560,7 @@ static int ccio_probe(struct parisc_device *dev)
*ioc_p = ioc;
ioc->hw_path = dev->hw_path;
- ioc->ioc_regs = ioremap(dev->hpa.start, 4096);
+ ioc->ioc_regs = ioremap_nocache(dev->hpa.start, 4096);
ccio_ioc_init(ioc);
ccio_init_resources(ioc);
hppa_dma_ops = &ccio_ops;
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index 3d1a7f98c676..6e8ed0c81a6c 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -5,6 +5,7 @@
** (c) Copyright 1999 SuSE GmbH
** (c) Copyright 1999,2000 Hewlett-Packard Company
** (c) Copyright 2000 Grant Grundler
+** (c) Copyright 2006 Helge Deller
**
** 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
@@ -785,7 +786,7 @@ dino_bridge_init(struct dino_device *dino_dev, const char *name)
if((io_addr & (1 << i)) == 0)
continue;
- start = (unsigned long)(signed int)(0xf0000000 | (i << 23));
+ start = F_EXTEND(0xf0000000UL) | (i << 23);
end = start + 8 * 1024 * 1024 - 1;
DBG("DINO RANGE %d is at 0x%lx-0x%lx\n", count,
@@ -996,7 +997,7 @@ static int __init dino_probe(struct parisc_device *dev)
}
dino_dev->hba.dev = dev;
- dino_dev->hba.base_addr = ioremap(hpa, 4096);
+ dino_dev->hba.base_addr = ioremap_nocache(hpa, 4096);
dino_dev->hba.lmmio_space_offset = 0; /* CPU addrs == bus addrs */
spin_lock_init(&dino_dev->dinosaur_pen);
dino_dev->hba.iommu = ccio_get_iommu(dev);
diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c
index 3d94d86c1c9f..9d3bd15bf53b 100644
--- a/drivers/parisc/eisa.c
+++ b/drivers/parisc/eisa.c
@@ -366,7 +366,7 @@ static int __devinit eisa_probe(struct parisc_device *dev)
eisa_dev.eeprom_addr = MIRAGE_EEPROM_BASE_ADDR;
}
}
- eisa_eeprom_addr = ioremap(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH);
+ eisa_eeprom_addr = ioremap_nocache(eisa_dev.eeprom_addr, HPEE_MAX_LENGTH);
result = eisa_enumerator(eisa_dev.eeprom_addr, &eisa_dev.hba.io_space,
&eisa_dev.hba.lmmio_space);
init_eisa_pic();
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
index 8d7a36392eb8..7a458d5bc751 100644
--- a/drivers/parisc/iosapic.c
+++ b/drivers/parisc/iosapic.c
@@ -879,7 +879,7 @@ void *iosapic_register(unsigned long hpa)
return NULL;
}
- isi->addr = ioremap(hpa, 4096);
+ isi->addr = ioremap_nocache(hpa, 4096);
isi->isi_hpa = hpa;
isi->isi_version = iosapic_rd_version(isi);
isi->isi_num_vectors = IOSAPIC_IRDT_MAX_ENTRY(isi->isi_version) + 1;
diff --git a/drivers/parisc/lba_pci.c b/drivers/parisc/lba_pci.c
index e8a2a4a852f5..3fe4a77fa16a 100644
--- a/drivers/parisc/lba_pci.c
+++ b/drivers/parisc/lba_pci.c
@@ -1213,7 +1213,7 @@ lba_pat_resources(struct parisc_device *pa_dev, struct lba_device *lba_dev)
** Postable I/O port space is per PCI host adapter.
** base of 64MB PIOP region
*/
- lba_dev->iop_base = ioremap(p->start, 64 * 1024 * 1024);
+ lba_dev->iop_base = ioremap_nocache(p->start, 64 * 1024 * 1024);
sprintf(lba_dev->hba.io_name, "PCI%02lx Ports",
lba_dev->hba.bus_num.start);
@@ -1525,7 +1525,7 @@ lba_driver_probe(struct parisc_device *dev)
u32 func_class;
void *tmp_obj;
char *version;
- void __iomem *addr = ioremap(dev->hpa.start, 4096);
+ void __iomem *addr = ioremap_nocache(dev->hpa.start, 4096);
/* Read HW Rev First */
func_class = READ_REG32(addr + LBA_FCLASS);
@@ -1619,7 +1619,7 @@ lba_driver_probe(struct parisc_device *dev)
} else {
if (!astro_iop_base) {
/* Sprockets PDC uses NPIOP region */
- astro_iop_base = ioremap(LBA_PORT_BASE, 64 * 1024);
+ astro_iop_base = ioremap_nocache(LBA_PORT_BASE, 64 * 1024);
pci_port = &lba_astro_port_ops;
}
@@ -1700,7 +1700,7 @@ void __init lba_init(void)
*/
void lba_set_iregs(struct parisc_device *lba, u32 ibase, u32 imask)
{
- void __iomem * base_addr = ioremap(lba->hpa.start, 4096);
+ void __iomem * base_addr = ioremap_nocache(lba->hpa.start, 4096);
imask <<= 2; /* adjust for hints - 2 more bits */
diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c
index a28e17898fbd..4e53be9c03ab 100644
--- a/drivers/parisc/pdc_stable.c
+++ b/drivers/parisc/pdc_stable.c
@@ -4,9 +4,8 @@
* Copyright (C) 2005-2006 Thibaut VARENE <varenet@parisc-linux.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.
+ * 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
diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c
index 0821747e44cf..42b32ff2fca6 100644
--- a/drivers/parisc/sba_iommu.c
+++ b/drivers/parisc/sba_iommu.c
@@ -1642,9 +1642,9 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num)
**
**************************************************************************/
-static void __iomem *ioc_remap(struct sba_device *sba_dev, int offset)
+static void __iomem *ioc_remap(struct sba_device *sba_dev, unsigned int offset)
{
- return ioremap(sba_dev->dev->hpa.start + offset, SBA_FUNC_SIZE);
+ return ioremap_nocache(sba_dev->dev->hpa.start + offset, SBA_FUNC_SIZE);
}
static void sba_hw_init(struct sba_device *sba_dev)
@@ -2040,7 +2040,7 @@ sba_driver_callback(struct parisc_device *dev)
u32 func_class;
int i;
char *version;
- void __iomem *sba_addr = ioremap(dev->hpa.start, SBA_FUNC_SIZE);
+ void __iomem *sba_addr = ioremap_nocache(dev->hpa.start, SBA_FUNC_SIZE);
struct proc_dir_entry *info_entry, *bitmap_entry, *root;
sba_dump_ranges(sba_addr);
diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c
index ad6d3b28a3a6..719b863bc20e 100644
--- a/drivers/parisc/superio.c
+++ b/drivers/parisc/superio.c
@@ -12,6 +12,7 @@
* (C) Copyright 2001 John Marvin <jsm fc hp com>
* (C) Copyright 2003 Grant Grundler <grundler parisc-linux org>
* (C) Copyright 2005 Kyle McMartin <kyle@parisc-linux.org>
+ * (C) Copyright 2006 Helge Deller <deller@gmx.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -388,43 +389,34 @@ int superio_fixup_irq(struct pci_dev *pcidev)
return local_irq;
}
-static struct uart_port serial[] = {
- {
- .iotype = UPIO_PORT,
- .line = 0,
- .type = PORT_16550A,
- .uartclk = 115200*16,
- .fifosize = 16,
- },
- {
- .iotype = UPIO_PORT,
- .line = 1,
- .type = PORT_16550A,
- .uartclk = 115200*16,
- .fifosize = 16,
- }
-};
-
static void __devinit superio_serial_init(void)
{
#ifdef CONFIG_SERIAL_8250
int retval;
-
- serial[0].iobase = sio_dev.sp1_base;
- serial[0].irq = SP1_IRQ;
- spin_lock_init(&serial[0].lock);
-
- retval = early_serial_setup(&serial[0]);
+ struct uart_port serial_port;
+
+ memset(&serial_port, 0, sizeof(serial_port));
+ serial_port.iotype = UPIO_PORT;
+ serial_port.type = PORT_16550A;
+ serial_port.uartclk = 115200*16;
+ serial_port.fifosize = 16;
+ spin_lock_init(&serial_port.lock);
+
+ /* serial port #1 */
+ serial_port.iobase = sio_dev.sp1_base;
+ serial_port.irq = SP1_IRQ;
+ serial_port.line = 0;
+ retval = early_serial_setup(&serial_port);
if (retval < 0) {
printk(KERN_WARNING PFX "Register Serial #0 failed.\n");
return;
}
- serial[1].iobase = sio_dev.sp2_base;
- serial[1].irq = SP2_IRQ;
- spin_lock_init(&serial[1].lock);
- retval = early_serial_setup(&serial[1]);
-
+ /* serial port #2 */
+ serial_port.iobase = sio_dev.sp2_base;
+ serial_port.irq = SP2_IRQ;
+ serial_port.line = 1;
+ retval = early_serial_setup(&serial_port);
if (retval < 0)
printk(KERN_WARNING PFX "Register Serial #1 failed.\n");
#endif /* CONFIG_SERIAL_8250 */
diff --git a/drivers/pcmcia/vrc4171_card.c b/drivers/pcmcia/vrc4171_card.c
index 0574efd7828a..459e6e1946fd 100644
--- a/drivers/pcmcia/vrc4171_card.c
+++ b/drivers/pcmcia/vrc4171_card.c
@@ -634,7 +634,7 @@ static void vrc4171_remove_sockets(void)
static int __devinit vrc4171_card_setup(char *options)
{
if (options == NULL || *options == '\0')
- return 0;
+ return 1;
if (strncmp(options, "irq:", 4) == 0) {
int irq;
@@ -644,7 +644,7 @@ static int __devinit vrc4171_card_setup(char *options)
vrc4171_irq = irq;
if (*options != ',')
- return 0;
+ return 1;
options++;
}
@@ -663,10 +663,10 @@ static int __devinit vrc4171_card_setup(char *options)
}
if (*options != ',')
- return 0;
+ return 1;
options++;
} else
- return 0;
+ return 1;
}
@@ -688,7 +688,7 @@ static int __devinit vrc4171_card_setup(char *options)
}
if (*options != ',')
- return 0;
+ return 1;
options++;
if (strncmp(options, "memnoprobe", 10) == 0)
@@ -700,7 +700,7 @@ static int __devinit vrc4171_card_setup(char *options)
}
}
- return 0;
+ return 1;
}
__setup("vrc4171_card=", vrc4171_card_setup);
diff --git a/drivers/pcmcia/vrc4173_cardu.c b/drivers/pcmcia/vrc4173_cardu.c
index 57f38dba0a48..6004196f7cc1 100644
--- a/drivers/pcmcia/vrc4173_cardu.c
+++ b/drivers/pcmcia/vrc4173_cardu.c
@@ -516,7 +516,7 @@ static int __devinit vrc4173_cardu_probe(struct pci_dev *dev,
static int __devinit vrc4173_cardu_setup(char *options)
{
if (options == NULL || *options == '\0')
- return 0;
+ return 1;
if (strncmp(options, "cardu1:", 7) == 0) {
options += 7;
@@ -527,9 +527,9 @@ static int __devinit vrc4173_cardu_setup(char *options)
}
if (*options != ',')
- return 0;
+ return 1;
} else
- return 0;
+ return 1;
}
if (strncmp(options, "cardu2:", 7) == 0) {
@@ -538,7 +538,7 @@ static int __devinit vrc4173_cardu_setup(char *options)
cardu_sockets[CARDU2].noprobe = 1;
}
- return 0;
+ return 1;
}
__setup("vrc4173_cardu=", vrc4173_cardu_setup);
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index ffba65656a83..1bd82c4e52a0 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -293,6 +293,10 @@ static const struct pci_device_id ahci_pci_tbl[] = {
board_ahci }, /* JMicron JMB360 */
{ 0x197b, 0x2363, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
board_ahci }, /* JMicron JMB363 */
+ { PCI_VENDOR_ID_ATI, 0x4380, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ATI SB600 non-raid */
+ { PCI_VENDOR_ID_ATI, 0x4381, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+ board_ahci }, /* ATI SB600 raid */
{ } /* terminate list */
};
diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
index 2d5be84d8bd4..24e71b555172 100644
--- a/drivers/scsi/ata_piix.c
+++ b/drivers/scsi/ata_piix.c
@@ -301,7 +301,7 @@ static struct piix_map_db ich6_map_db = {
.mask = 0x3,
.map = {
/* PM PS SM SS MAP */
- { P0, P1, P2, P3 }, /* 00b */
+ { P0, P2, P1, P3 }, /* 00b */
{ IDE, IDE, P1, P3 }, /* 01b */
{ P0, P2, IDE, IDE }, /* 10b */
{ RV, RV, RV, RV },
@@ -312,7 +312,7 @@ static struct piix_map_db ich6m_map_db = {
.mask = 0x3,
.map = {
/* PM PS SM SS MAP */
- { P0, P1, P2, P3 }, /* 00b */
+ { P0, P2, RV, RV }, /* 00b */
{ RV, RV, RV, RV },
{ P0, P2, IDE, IDE }, /* 10b */
{ RV, RV, RV, RV },
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 3a8462e8d063..24eb59e143a9 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -2488,7 +2488,7 @@ static int option_setup(char *str)
}
ints[0] = i - 1;
internal_ibmmca_scsi_setup(cur, ints);
- return 0;
+ return 1;
}
__setup("ibmmcascsi=", option_setup);
diff --git a/drivers/scsi/lasi700.c b/drivers/scsi/lasi700.c
index 459a4daebece..eb7bd310cc82 100644
--- a/drivers/scsi/lasi700.c
+++ b/drivers/scsi/lasi700.c
@@ -112,7 +112,7 @@ lasi700_probe(struct parisc_device *dev)
hostdata->dev = &dev->dev;
dma_set_mask(&dev->dev, DMA_32BIT_MASK);
- hostdata->base = ioremap(base, 0x100);
+ hostdata->base = ioremap_nocache(base, 0x100);
hostdata->differential = 0;
if (dev->id.sversion == LASI_700_SVERSION) {
diff --git a/drivers/scsi/libata-bmdma.c b/drivers/scsi/libata-bmdma.c
index 95d81d86d8b7..835dff0bafdc 100644
--- a/drivers/scsi/libata-bmdma.c
+++ b/drivers/scsi/libata-bmdma.c
@@ -703,6 +703,7 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int
struct ata_probe_ent *probe_ent =
ata_probe_ent_alloc(pci_dev_to_dev(pdev), port[0]);
int p = 0;
+ unsigned long bmdma;
if (!probe_ent)
return NULL;
@@ -716,7 +717,12 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int
probe_ent->port[p].altstatus_addr =
probe_ent->port[p].ctl_addr =
pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS;
- probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4);
+ bmdma = pci_resource_start(pdev, 4);
+ if (bmdma) {
+ if (inb(bmdma + 2) & 0x80)
+ probe_ent->host_set_flags |= ATA_HOST_SIMPLEX;
+ probe_ent->port[p].bmdma_addr = bmdma;
+ }
ata_std_ports(&probe_ent->port[p]);
p++;
}
@@ -726,7 +732,13 @@ ata_pci_init_native_mode(struct pci_dev *pdev, struct ata_port_info **port, int
probe_ent->port[p].altstatus_addr =
probe_ent->port[p].ctl_addr =
pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS;
- probe_ent->port[p].bmdma_addr = pci_resource_start(pdev, 4) + 8;
+ bmdma = pci_resource_start(pdev, 4);
+ if (bmdma) {
+ bmdma += 8;
+ if(inb(bmdma + 2) & 0x80)
+ probe_ent->host_set_flags |= ATA_HOST_SIMPLEX;
+ probe_ent->port[p].bmdma_addr = bmdma;
+ }
ata_std_ports(&probe_ent->port[p]);
p++;
}
@@ -740,6 +752,7 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
struct ata_port_info *port, int port_num)
{
struct ata_probe_ent *probe_ent;
+ unsigned long bmdma;
probe_ent = ata_probe_ent_alloc(pci_dev_to_dev(pdev), port);
if (!probe_ent)
@@ -766,8 +779,13 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
break;
}
- probe_ent->port[0].bmdma_addr =
- pci_resource_start(pdev, 4) + 8 * port_num;
+ bmdma = pci_resource_start(pdev, 4);
+ if (bmdma != 0) {
+ bmdma += 8 * port_num;
+ probe_ent->port[0].bmdma_addr = bmdma;
+ if (inb(bmdma + 2) & 0x80)
+ probe_ent->host_set_flags |= ATA_HOST_SIMPLEX;
+ }
ata_std_ports(&probe_ent->port[0]);
return probe_ent;
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index d279666dcb38..e63c1ff1e102 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -62,7 +62,9 @@
#include "libata.h"
static unsigned int ata_dev_init_params(struct ata_port *ap,
- struct ata_device *dev);
+ struct ata_device *dev,
+ u16 heads,
+ u16 sectors);
static void ata_set_mode(struct ata_port *ap);
static unsigned int ata_dev_set_xfermode(struct ata_port *ap,
struct ata_device *dev);
@@ -276,7 +278,7 @@ static void ata_unpack_xfermask(unsigned int xfer_mask,
}
static const struct ata_xfer_ent {
- unsigned int shift, bits;
+ int shift, bits;
u8 base;
} ata_xfer_tbl[] = {
{ ATA_SHIFT_PIO, ATA_BITS_PIO, XFER_PIO_0 },
@@ -987,9 +989,7 @@ ata_exec_internal(struct ata_port *ap, struct ata_device *dev,
qc->private_data = &wait;
qc->complete_fn = ata_qc_complete_internal;
- qc->err_mask = ata_qc_issue(qc);
- if (qc->err_mask)
- ata_qc_complete(qc);
+ ata_qc_issue(qc);
spin_unlock_irqrestore(&ap->host_set->lock, flags);
@@ -1081,9 +1081,8 @@ unsigned int ata_pio_need_iordy(const struct ata_device *adev)
*
* Read ID data from the specified device. ATA_CMD_ID_ATA is
* performed on ATA devices and ATA_CMD_ID_ATAPI on ATAPI
- * devices. This function also takes care of EDD signature
- * misreporting (to be removed once EDD support is gone) and
- * issues ATA_CMD_INIT_DEV_PARAMS for pre-ATA4 drives.
+ * devices. This function also issues ATA_CMD_INIT_DEV_PARAMS
+ * for pre-ATA4 drives.
*
* LOCKING:
* Kernel thread context (may sleep)
@@ -1095,7 +1094,6 @@ static int ata_dev_read_id(struct ata_port *ap, struct ata_device *dev,
unsigned int *p_class, int post_reset, u16 **p_id)
{
unsigned int class = *p_class;
- unsigned int using_edd;
struct ata_taskfile tf;
unsigned int err_mask = 0;
u16 *id;
@@ -1104,12 +1102,6 @@ static int ata_dev_read_id(struct ata_port *ap, struct ata_device *dev,
DPRINTK("ENTER, host %u, dev %u\n", ap->id, dev->devno);
- if (ap->ops->probe_reset ||
- ap->flags & (ATA_FLAG_SRST | ATA_FLAG_SATA_RESET))
- using_edd = 0;
- else
- using_edd = 1;
-
ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */
id = kmalloc(sizeof(id[0]) * ATA_ID_WORDS, GFP_KERNEL);
@@ -1139,39 +1131,16 @@ static int ata_dev_read_id(struct ata_port *ap, struct ata_device *dev,
err_mask = ata_exec_internal(ap, dev, &tf, DMA_FROM_DEVICE,
id, sizeof(id[0]) * ATA_ID_WORDS);
-
if (err_mask) {
rc = -EIO;
reason = "I/O error";
-
- if (err_mask & ~AC_ERR_DEV)
- goto err_out;
-
- /*
- * arg! EDD works for all test cases, but seems to return
- * the ATA signature for some ATAPI devices. Until the
- * reason for this is found and fixed, we fix up the mess
- * here. If IDENTIFY DEVICE returns command aborted
- * (as ATAPI devices do), then we issue an
- * IDENTIFY PACKET DEVICE.
- *
- * ATA software reset (SRST, the default) does not appear
- * to have this problem.
- */
- if ((using_edd) && (class == ATA_DEV_ATA)) {
- u8 err = tf.feature;
- if (err & ATA_ABORTED) {
- class = ATA_DEV_ATAPI;
- goto retry;
- }
- }
goto err_out;
}
swap_buf_le16(id, ATA_ID_WORDS);
/* sanity check */
- if ((class == ATA_DEV_ATA) != ata_id_is_ata(id)) {
+ if ((class == ATA_DEV_ATA) != (ata_id_is_ata(id) | ata_id_is_cfa(id))) {
rc = -EINVAL;
reason = "device reports illegal type";
goto err_out;
@@ -1187,7 +1156,7 @@ static int ata_dev_read_id(struct ata_port *ap, struct ata_device *dev,
* Some drives were very specific about that exact sequence.
*/
if (ata_id_major_version(id) < 4 || !ata_id_has_lba(id)) {
- err_mask = ata_dev_init_params(ap, dev);
+ err_mask = ata_dev_init_params(ap, dev, id[3], id[6]);
if (err_mask) {
rc = -EIO;
reason = "INIT_DEV_PARAMS failed";
@@ -1440,7 +1409,11 @@ static int ata_bus_probe(struct ata_port *ap)
if (!found)
goto err_out_disable;
- ata_set_mode(ap);
+ if (ap->ops->set_mode)
+ ap->ops->set_mode(ap);
+ else
+ ata_set_mode(ap);
+
if (ap->flags & ATA_FLAG_PORT_DISABLED)
goto err_out_disable;
@@ -1845,7 +1818,7 @@ static void ata_host_set_dma(struct ata_port *ap)
*/
static void ata_set_mode(struct ata_port *ap)
{
- int i, rc;
+ int i, rc, used_dma = 0;
/* step 1: calculate xfer_mask */
for (i = 0; i < ATA_MAX_DEVICES; i++) {
@@ -1863,6 +1836,9 @@ static void ata_set_mode(struct ata_port *ap)
dma_mask = ata_pack_xfermask(0, dev->mwdma_mask, dev->udma_mask);
dev->pio_mode = ata_xfer_mask2mode(pio_mask);
dev->dma_mode = ata_xfer_mask2mode(dma_mask);
+
+ if (dev->dma_mode)
+ used_dma = 1;
}
/* step 2: always set host PIO timings */
@@ -1884,6 +1860,17 @@ static void ata_set_mode(struct ata_port *ap)
goto err_out;
}
+ /*
+ * Record simplex status. If we selected DMA then the other
+ * host channels are not permitted to do so.
+ */
+
+ if (used_dma && (ap->host_set->flags & ATA_HOST_SIMPLEX))
+ ap->host_set->simplex_claimed = 1;
+
+ /*
+ * Chip specific finalisation
+ */
if (ap->ops->post_set_mode)
ap->ops->post_set_mode(ap);
@@ -2005,45 +1992,6 @@ static void ata_bus_post_reset(struct ata_port *ap, unsigned int devmask)
ap->ops->dev_select(ap, 0);
}
-/**
- * ata_bus_edd - Issue EXECUTE DEVICE DIAGNOSTIC command.
- * @ap: Port to reset and probe
- *
- * Use the EXECUTE DEVICE DIAGNOSTIC command to reset and
- * probe the bus. Not often used these days.
- *
- * LOCKING:
- * PCI/etc. bus probe sem.
- * Obtains host_set lock.
- *
- */
-
-static unsigned int ata_bus_edd(struct ata_port *ap)
-{
- struct ata_taskfile tf;
- unsigned long flags;
-
- /* set up execute-device-diag (bus reset) taskfile */
- /* also, take interrupts to a known state (disabled) */
- DPRINTK("execute-device-diag\n");
- ata_tf_init(ap, &tf, 0);
- tf.ctl |= ATA_NIEN;
- tf.command = ATA_CMD_EDD;
- tf.protocol = ATA_PROT_NODATA;
-
- /* do bus reset */
- spin_lock_irqsave(&ap->host_set->lock, flags);
- ata_tf_to_host(ap, &tf);
- spin_unlock_irqrestore(&ap->host_set->lock, flags);
-
- /* spec says at least 2ms. but who knows with those
- * crazy ATAPI devices...
- */
- msleep(150);
-
- return ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
-}
-
static unsigned int ata_bus_softreset(struct ata_port *ap,
unsigned int devmask)
{
@@ -2078,13 +2026,12 @@ static unsigned int ata_bus_softreset(struct ata_port *ap,
*/
msleep(150);
-
/* Before we perform post reset processing we want to see if
- the bus shows 0xFF because the odd clown forgets the D7 pulldown
- resistor */
-
+ * the bus shows 0xFF because the odd clown forgets the D7
+ * pulldown resistor.
+ */
if (ata_check_status(ap) == 0xFF)
- return 1; /* Positive is failure for some reason */
+ return AC_ERR_OTHER;
ata_bus_post_reset(ap, devmask);
@@ -2116,7 +2063,7 @@ void ata_bus_reset(struct ata_port *ap)
struct ata_ioports *ioaddr = &ap->ioaddr;
unsigned int slave_possible = ap->flags & ATA_FLAG_SLAVE_POSS;
u8 err;
- unsigned int dev0, dev1 = 0, rc = 0, devmask = 0;
+ unsigned int dev0, dev1 = 0, devmask = 0;
DPRINTK("ENTER, host %u, port %u\n", ap->id, ap->port_no);
@@ -2139,18 +2086,8 @@ void ata_bus_reset(struct ata_port *ap)
/* issue bus reset */
if (ap->flags & ATA_FLAG_SRST)
- rc = ata_bus_softreset(ap, devmask);
- else if ((ap->flags & ATA_FLAG_SATA_RESET) == 0) {
- /* set up device control */
- if (ap->flags & ATA_FLAG_MMIO)
- writeb(ap->ctl, (void __iomem *) ioaddr->ctl_addr);
- else
- outb(ap->ctl, ioaddr->ctl_addr);
- rc = ata_bus_edd(ap);
- }
-
- if (rc)
- goto err_out;
+ if (ata_bus_softreset(ap, devmask))
+ goto err_out;
/*
* determine by signature whether we have ATA or ATAPI devices
@@ -2223,9 +2160,9 @@ static int sata_phy_resume(struct ata_port *ap)
* so makes reset sequence different from the original
* ->phy_reset implementation and Jeff nervous. :-P
*/
-extern void ata_std_probeinit(struct ata_port *ap)
+void ata_std_probeinit(struct ata_port *ap)
{
- if (ap->flags & ATA_FLAG_SATA && ap->ops->scr_read) {
+ if ((ap->flags & ATA_FLAG_SATA) && ap->ops->scr_read) {
sata_phy_resume(ap);
if (sata_dev_present(ap))
ata_busy_sleep(ap, ATA_TMOUT_BOOT_QUICK, ATA_TMOUT_BOOT);
@@ -2714,18 +2651,23 @@ static int ata_dma_blacklisted(const struct ata_device *dev)
* known limits including host controller limits, device
* blacklist, etc...
*
+ * FIXME: The current implementation limits all transfer modes to
+ * the fastest of the lowested device on the port. This is not
+ * required on most controllers.
+ *
* LOCKING:
* None.
*/
static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev)
{
+ struct ata_host_set *hs = ap->host_set;
unsigned long xfer_mask;
int i;
xfer_mask = ata_pack_xfermask(ap->pio_mask, ap->mwdma_mask,
ap->udma_mask);
- /* use port-wide xfermask for now */
+ /* FIXME: Use port-wide xfermask for now */
for (i = 0; i < ATA_MAX_DEVICES; i++) {
struct ata_device *d = &ap->device[i];
if (!ata_dev_present(d))
@@ -2735,12 +2677,23 @@ static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev)
xfer_mask &= ata_id_xfermask(d->id);
if (ata_dma_blacklisted(d))
xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
+ /* Apply cable rule here. Don't apply it early because when
+ we handle hot plug the cable type can itself change */
+ if (ap->cbl == ATA_CBL_PATA40)
+ xfer_mask &= ~(0xF8 << ATA_SHIFT_UDMA);
}
if (ata_dma_blacklisted(dev))
printk(KERN_WARNING "ata%u: dev %u is on DMA blacklist, "
"disabling DMA\n", ap->id, dev->devno);
+ if (hs->flags & ATA_HOST_SIMPLEX) {
+ if (hs->simplex_claimed)
+ xfer_mask &= ~(ATA_MASK_MWDMA | ATA_MASK_UDMA);
+ }
+ if (ap->ops->mode_filter)
+ xfer_mask = ap->ops->mode_filter(ap, dev, xfer_mask);
+
ata_unpack_xfermask(xfer_mask, &dev->pio_mask, &dev->mwdma_mask,
&dev->udma_mask);
}
@@ -2795,16 +2748,16 @@ static unsigned int ata_dev_set_xfermode(struct ata_port *ap,
*/
static unsigned int ata_dev_init_params(struct ata_port *ap,
- struct ata_device *dev)
+ struct ata_device *dev,
+ u16 heads,
+ u16 sectors)
{
struct ata_taskfile tf;
unsigned int err_mask;
- u16 sectors = dev->id[6];
- u16 heads = dev->id[3];
/* Number of sectors per track 1-255. Number of heads 1-16 */
if (sectors < 1 || sectors > 255 || heads < 1 || heads > 16)
- return 0;
+ return AC_ERR_INVALID;
/* set up init dev params taskfile */
DPRINTK("init dev params \n");
@@ -4042,15 +3995,14 @@ static inline int ata_should_dma_map(struct ata_queued_cmd *qc)
*
* LOCKING:
* spin_lock_irqsave(host_set lock)
- *
- * RETURNS:
- * Zero on success, AC_ERR_* mask on failure
*/
-
-unsigned int ata_qc_issue(struct ata_queued_cmd *qc)
+void ata_qc_issue(struct ata_queued_cmd *qc)
{
struct ata_port *ap = qc->ap;
+ qc->ap->active_tag = qc->tag;
+ qc->flags |= ATA_QCFLAG_ACTIVE;
+
if (ata_should_dma_map(qc)) {
if (qc->flags & ATA_QCFLAG_SG) {
if (ata_sg_setup(qc))
@@ -4065,17 +4017,18 @@ unsigned int ata_qc_issue(struct ata_queued_cmd *qc)
ap->ops->qc_prep(qc);
- qc->ap->active_tag = qc->tag;
- qc->flags |= ATA_QCFLAG_ACTIVE;
-
- return ap->ops->qc_issue(qc);
+ qc->err_mask |= ap->ops->qc_issue(qc);
+ if (unlikely(qc->err_mask))
+ goto err;
+ return;
sg_err:
qc->flags &= ~ATA_QCFLAG_DMAMAP;
- return AC_ERR_SYSTEM;
+ qc->err_mask |= AC_ERR_SYSTEM;
+err:
+ ata_qc_complete(qc);
}
-
/**
* ata_qc_issue_prot - issue taskfile to device in proto-dependent manner
* @qc: command to issue to device
@@ -4536,6 +4489,14 @@ static struct ata_port * ata_host_add(const struct ata_probe_ent *ent,
int rc;
DPRINTK("ENTER\n");
+
+ if (!ent->port_ops->probe_reset &&
+ !(ent->host_flags & (ATA_FLAG_SATA_RESET | ATA_FLAG_SRST))) {
+ printk(KERN_ERR "ata%u: no reset mechanism available\n",
+ port_no);
+ return NULL;
+ }
+
host = scsi_host_alloc(ent->sht, sizeof(struct ata_port));
if (!host)
return NULL;
@@ -4596,6 +4557,7 @@ int ata_device_add(const struct ata_probe_ent *ent)
host_set->mmio_base = ent->mmio_base;
host_set->private_data = ent->private_data;
host_set->ops = ent->port_ops;
+ host_set->flags = ent->host_set_flags;
/* register each port bound to this device */
for (i = 0; i < ent->n_ports; i++) {
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 628191bfd990..53f5b0d9161c 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -1431,9 +1431,7 @@ static void ata_scsi_translate(struct ata_port *ap, struct ata_device *dev,
goto early_finish;
/* select device, send command to hardware */
- qc->err_mask = ata_qc_issue(qc);
- if (qc->err_mask)
- ata_qc_complete(qc);
+ ata_qc_issue(qc);
VPRINTK("EXIT\n");
return;
@@ -2199,9 +2197,7 @@ static void atapi_request_sense(struct ata_queued_cmd *qc)
qc->complete_fn = atapi_sense_complete;
- qc->err_mask = ata_qc_issue(qc);
- if (qc->err_mask)
- ata_qc_complete(qc);
+ ata_qc_issue(qc);
DPRINTK("EXIT\n");
}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index 65f52beea884..1c755b14521a 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -47,7 +47,7 @@ extern struct ata_queued_cmd *ata_qc_new_init(struct ata_port *ap,
extern int ata_rwcmd_protocol(struct ata_queued_cmd *qc);
extern void ata_port_flush_task(struct ata_port *ap);
extern void ata_qc_free(struct ata_queued_cmd *qc);
-extern unsigned int ata_qc_issue(struct ata_queued_cmd *qc);
+extern void ata_qc_issue(struct ata_queued_cmd *qc);
extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
extern void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep);
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index d6d2125f9044..f852421002ef 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1748,7 +1748,7 @@ static int mesh_host_reset(struct scsi_cmnd *cmd)
static void set_mesh_power(struct mesh_state *ms, int state)
{
- if (_machine != _MACH_Pmac)
+ if (!machine_is(powermac))
return;
if (state) {
pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 1);
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
index 275ed9bd898c..fa901fd65085 100644
--- a/drivers/scsi/sata_mv.c
+++ b/drivers/scsi/sata_mv.c
@@ -1010,7 +1010,7 @@ static void mv_fill_sg(struct ata_queued_cmd *qc)
pp->sg_tbl[i].addr = cpu_to_le32(addr & 0xffffffff);
pp->sg_tbl[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
- pp->sg_tbl[i].flags_size = cpu_to_le32(len);
+ pp->sg_tbl[i].flags_size = cpu_to_le32(len & 0xffff);
sg_len -= len;
addr += len;
@@ -1350,7 +1350,6 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
{
void __iomem *mmio = host_set->mmio_base;
void __iomem *hc_mmio = mv_hc_base(mmio, hc);
- struct ata_port *ap;
struct ata_queued_cmd *qc;
u32 hc_irq_cause;
int shift, port, port0, hard_port, handled;
@@ -1373,25 +1372,32 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
for (port = port0; port < port0 + MV_PORTS_PER_HC; port++) {
u8 ata_status = 0;
- ap = host_set->ports[port];
+ struct ata_port *ap = host_set->ports[port];
+ struct mv_port_priv *pp = ap->private_data;
+
hard_port = port & MV_PORT_MASK; /* range 0-3 */
handled = 0; /* ensure ata_status is set if handled++ */
- if ((CRPB_DMA_DONE << hard_port) & hc_irq_cause) {
- /* new CRPB on the queue; just one at a time until NCQ
- */
- ata_status = mv_get_crpb_status(ap);
- handled++;
- } else if ((DEV_IRQ << hard_port) & hc_irq_cause) {
- /* received ATA IRQ; read the status reg to clear INTRQ
- */
- ata_status = readb((void __iomem *)
+ /* Note that DEV_IRQ might happen spuriously during EDMA,
+ * and should be ignored in such cases. We could mask it,
+ * but it's pretty rare and may not be worth the overhead.
+ */
+ if (pp->pp_flags & MV_PP_FLAG_EDMA_EN) {
+ /* EDMA: check for response queue interrupt */
+ if ((CRPB_DMA_DONE << hard_port) & hc_irq_cause) {
+ ata_status = mv_get_crpb_status(ap);
+ handled = 1;
+ }
+ } else {
+ /* PIO: check for device (drive) interrupt */
+ if ((DEV_IRQ << hard_port) & hc_irq_cause) {
+ ata_status = readb((void __iomem *)
ap->ioaddr.status_addr);
- handled++;
+ handled = 1;
+ }
}
- if (ap &&
- (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR)))
+ if (ap->flags & (ATA_FLAG_PORT_DISABLED | ATA_FLAG_NOINTR))
continue;
err_mask = ac_err_mask(ata_status);
@@ -1403,12 +1409,12 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
if ((PORT0_ERR << shift) & relevant) {
mv_err_intr(ap);
err_mask |= AC_ERR_OTHER;
- handled++;
+ handled = 1;
}
- if (handled && ap) {
+ if (handled) {
qc = ata_qc_from_tag(ap, ap->active_tag);
- if (NULL != qc) {
+ if (qc && (qc->flags & ATA_QCFLAG_ACTIVE)) {
VPRINTK("port %u IRQ found for qc, "
"ata_status 0x%x\n", port,ata_status);
/* mark qc status appropriately */
diff --git a/drivers/scsi/zalon.c b/drivers/scsi/zalon.c
index b131432c677d..a6cfbb3b361c 100644
--- a/drivers/scsi/zalon.c
+++ b/drivers/scsi/zalon.c
@@ -88,7 +88,7 @@ zalon_probe(struct parisc_device *dev)
struct gsc_irq gsc_irq;
u32 zalon_vers;
int error = -ENODEV;
- void __iomem *zalon = ioremap(dev->hpa.start, 4096);
+ void __iomem *zalon = ioremap_nocache(dev->hpa.start, 4096);
void __iomem *io_port = zalon + GSC_SCSI_ZALON_OFFSET;
static int unit = 0;
struct Scsi_Host *host;
diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c
index 8b4947933d9b..913c71cc0569 100644
--- a/drivers/serial/8250_gsc.c
+++ b/drivers/serial/8250_gsc.c
@@ -52,13 +52,14 @@ serial_init_chip(struct parisc_device *dev)
address += 0x800;
}
- memset(&port, 0, sizeof(struct uart_port));
- port.mapbase = address;
- port.irq = dev->irq;
- port.iotype = UPIO_MEM;
- port.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- port.uartclk = LASI_BASE_BAUD * 16;
- port.dev = &dev->dev;
+ memset(&port, 0, sizeof(port));
+ port.iotype = UPIO_MEM;
+ port.uartclk = LASI_BASE_BAUD * 16;
+ port.mapbase = address;
+ port.membase = ioremap_nocache(address, 16);
+ port.irq = dev->irq;
+ port.flags = UPF_BOOT_AUTOCONF;
+ port.dev = &dev->dev;
err = serial8250_register_port(&port);
if (err < 0) {
diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
index 4d48b625cd3d..7d823705193c 100644
--- a/drivers/serial/jsm/jsm_tty.c
+++ b/drivers/serial/jsm/jsm_tty.c
@@ -142,12 +142,14 @@ static void jsm_tty_send_xchar(struct uart_port *port, char ch)
{
unsigned long lock_flags;
struct jsm_channel *channel = (struct jsm_channel *)port;
+ struct termios *termios;
spin_lock_irqsave(&port->lock, lock_flags);
- if (ch == port->info->tty->termios->c_cc[VSTART])
+ termios = port->info->tty->termios;
+ if (ch == termios->c_cc[VSTART])
channel->ch_bd->bd_ops->send_start_character(channel);
- if (ch == port->info->tty->termios->c_cc[VSTOP])
+ if (ch == termios->c_cc[VSTOP])
channel->ch_bd->bd_ops->send_stop_character(channel);
spin_unlock_irqrestore(&port->lock, lock_flags);
}
@@ -178,6 +180,7 @@ static int jsm_tty_open(struct uart_port *port)
struct jsm_board *brd;
int rc = 0;
struct jsm_channel *channel = (struct jsm_channel *)port;
+ struct termios *termios;
/* Get board pointer from our array of majors we have allocated */
brd = channel->ch_bd;
@@ -239,12 +242,13 @@ static int jsm_tty_open(struct uart_port *port)
channel->ch_cached_lsr = 0;
channel->ch_stops_sent = 0;
- channel->ch_c_cflag = port->info->tty->termios->c_cflag;
- channel->ch_c_iflag = port->info->tty->termios->c_iflag;
- channel->ch_c_oflag = port->info->tty->termios->c_oflag;
- channel->ch_c_lflag = port->info->tty->termios->c_lflag;
- channel->ch_startc = port->info->tty->termios->c_cc[VSTART];
- channel->ch_stopc = port->info->tty->termios->c_cc[VSTOP];
+ termios = port->info->tty->termios;
+ channel->ch_c_cflag = termios->c_cflag;
+ channel->ch_c_iflag = termios->c_iflag;
+ channel->ch_c_oflag = termios->c_oflag;
+ channel->ch_c_lflag = termios->c_lflag;
+ channel->ch_startc = termios->c_cc[VSTART];
+ channel->ch_stopc = termios->c_cc[VSTOP];
/* Tell UART to init itself */
brd->bd_ops->uart_init(channel);
@@ -784,6 +788,7 @@ static void jsm_carrier(struct jsm_channel *ch)
void jsm_check_queue_flow_control(struct jsm_channel *ch)
{
+ struct board_ops *bd_ops = ch->ch_bd->bd_ops;
int qleft = 0;
/* Store how much space we have left in the queue */
@@ -809,7 +814,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
/* HWFLOW */
if (ch->ch_c_cflag & CRTSCTS) {
if(!(ch->ch_flags & CH_RECEIVER_OFF)) {
- ch->ch_bd->bd_ops->disable_receiver(ch);
+ bd_ops->disable_receiver(ch);
ch->ch_flags |= (CH_RECEIVER_OFF);
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
"Internal queue hit hilevel mark (%d)! Turning off interrupts.\n",
@@ -819,7 +824,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
/* SWFLOW */
else if (ch->ch_c_iflag & IXOFF) {
if (ch->ch_stops_sent <= MAX_STOPS_SENT) {
- ch->ch_bd->bd_ops->send_stop_character(ch);
+ bd_ops->send_stop_character(ch);
ch->ch_stops_sent++;
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
"Sending stop char! Times sent: %x\n", ch->ch_stops_sent);
@@ -846,7 +851,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
/* HWFLOW */
if (ch->ch_c_cflag & CRTSCTS) {
if (ch->ch_flags & CH_RECEIVER_OFF) {
- ch->ch_bd->bd_ops->enable_receiver(ch);
+ bd_ops->enable_receiver(ch);
ch->ch_flags &= ~(CH_RECEIVER_OFF);
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev,
"Internal queue hit lowlevel mark (%d)! Turning on interrupts.\n",
@@ -856,7 +861,7 @@ void jsm_check_queue_flow_control(struct jsm_channel *ch)
/* SWFLOW */
else if (ch->ch_c_iflag & IXOFF && ch->ch_stops_sent) {
ch->ch_stops_sent = 0;
- ch->ch_bd->bd_ops->send_start_character(ch);
+ bd_ops->send_start_character(ch);
jsm_printk(READ, INFO, &ch->ch_bd->pci_dev, "Sending start char!\n");
}
}
diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c
index 868eaf4a1a68..64c0e89124c9 100644
--- a/drivers/serial/mux.c
+++ b/drivers/serial/mux.c
@@ -51,7 +51,7 @@
#define MUX_BREAK(status) ((status & 0xF000) == 0x2000)
#define MUX_NR 256
-static unsigned int port_cnt = 0;
+static unsigned int port_cnt __read_mostly;
static struct uart_port mux_ports[MUX_NR];
static struct uart_driver mux_driver = {
@@ -461,7 +461,7 @@ static int __init mux_probe(struct parisc_device *dev)
port->iobase = 0;
port->mapbase = dev->hpa.start + MUX_OFFSET +
(i * MUX_LINE_OFFSET);
- port->membase = ioremap(port->mapbase, MUX_LINE_OFFSET);
+ port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET);
port->iotype = UPIO_MEM;
port->type = PORT_MUX;
port->irq = NO_IRQ;
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index e0afb5ad29e5..0d2193b69235 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -296,7 +296,7 @@ done:
#ifdef CONFIG_PPC_PMAC
/* Disable ASIC clocks for USB */
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
struct device_node *of_node;
of_node = pci_device_to_OF_node (dev);
@@ -331,7 +331,7 @@ int usb_hcd_pci_resume (struct pci_dev *dev)
#ifdef CONFIG_PPC_PMAC
/* Reenable ASIC clocks for USB */
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
struct device_node *of_node;
of_node = pci_device_to_OF_node (dev);
diff --git a/drivers/usb/net/zd1201.c b/drivers/usb/net/zd1201.c
index fe9b60cd8d95..9b1e4ed1d07e 100644
--- a/drivers/usb/net/zd1201.c
+++ b/drivers/usb/net/zd1201.c
@@ -1736,6 +1736,7 @@ static const struct iw_handler_def zd1201_iw_handlers = {
.standard = (iw_handler *)zd1201_iw_handler,
.private = (iw_handler *)zd1201_private_handler,
.private_args = (struct iw_priv_args *) zd1201_private_args,
+ .get_wireless_stats = zd1201_get_wireless_stats,
};
static int zd1201_probe(struct usb_interface *interface,
@@ -1796,7 +1797,6 @@ static int zd1201_probe(struct usb_interface *interface,
zd->dev->open = zd1201_net_open;
zd->dev->stop = zd1201_net_stop;
zd->dev->get_stats = zd1201_get_stats;
- zd->dev->get_wireless_stats = zd1201_get_wireless_stats;
zd->dev->wireless_handlers =
(struct iw_handler_def *)&zd1201_iw_handlers;
zd->dev->hard_start_xmit = zd1201_hard_start_xmit;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 22e9d696fdd2..f87c0171f4ec 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -904,18 +904,6 @@ config FB_MATROX_MULTIHEAD
There is no need for enabling 'Matrox multihead support' if you have
only one Matrox card in the box.
-config FB_RADEON_OLD
- tristate "ATI Radeon display support (Old driver)"
- depends on FB && PCI
- select FB_CFB_FILLRECT
- select FB_CFB_COPYAREA
- select FB_CFB_IMAGEBLIT
- select FB_MACMODES if PPC
- help
- Choose this option if you want to use an ATI Radeon graphics card as
- a framebuffer device. There are both PCI and AGP versions. You
- don't need to choose this to run the Radeon in plain VGA mode.
-
config FB_RADEON
tristate "ATI Radeon display support"
depends on FB && PCI
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index cb90218515ac..23de3b2c7856 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -39,7 +39,6 @@ obj-$(CONFIG_FB_KYRO) += kyro/
obj-$(CONFIG_FB_SAVAGE) += savage/
obj-$(CONFIG_FB_GEODE) += geode/
obj-$(CONFIG_FB_I810) += vgastate.o
-obj-$(CONFIG_FB_RADEON_OLD) += radeonfb.o
obj-$(CONFIG_FB_NEOMAGIC) += neofb.o vgastate.o
obj-$(CONFIG_FB_VIRGE) += virgefb.o
obj-$(CONFIG_FB_3DFX) += tdfxfb.o
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index 821c6da8e42c..f7bbff4ddc6a 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -67,6 +67,7 @@
#include <asm/io.h>
#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
#include <asm/pmac_feature.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
@@ -1748,7 +1749,7 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id *
var = default_var;
#ifdef CONFIG_PPC_PMAC
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
/* Indicate sleep capability */
if (par->chip_gen == rage_M3) {
pmac_call_feature(PMAC_FTR_DEVICE_CAN_WAKE, NULL, 0, 1);
@@ -2011,7 +2012,7 @@ static int aty128fb_blank(int blank, struct fb_info *fb)
return 0;
#ifdef CONFIG_PMAC_BACKLIGHT
- if ((_machine == _MACH_Pmac) && blank)
+ if (machine_is(powermac) && blank)
set_backlight_enable(0);
#endif /* CONFIG_PMAC_BACKLIGHT */
@@ -2029,7 +2030,7 @@ static int aty128fb_blank(int blank, struct fb_info *fb)
aty128_set_lcd_enable(par, par->lcd_on && !blank);
}
#ifdef CONFIG_PMAC_BACKLIGHT
- if ((_machine == _MACH_Pmac) && !blank)
+ if (machine_is(powermac) && !blank)
set_backlight_enable(1);
#endif /* CONFIG_PMAC_BACKLIGHT */
return 0;
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index e799fcca365a..b39e72d5413b 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -75,6 +75,7 @@
#include "ati_ids.h"
#ifdef __powerpc__
+#include <asm/machdep.h>
#include <asm/prom.h>
#include "../macmodes.h"
#endif
@@ -2518,7 +2519,7 @@ static int __init aty_init(struct fb_info *info, const char *name)
memset(&var, 0, sizeof(var));
#ifdef CONFIG_PPC
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
/*
* FIXME: The NVRAM stuff should be put in a Mac-specific file, as it
* applies to all Mac video cards
@@ -2673,7 +2674,7 @@ static int atyfb_blank(int blank, struct fb_info *info)
return 0;
#ifdef CONFIG_PMAC_BACKLIGHT
- if ((_machine == _MACH_Pmac) && blank > FB_BLANK_NORMAL)
+ if (machine_is(powermac) && blank > FB_BLANK_NORMAL)
set_backlight_enable(0);
#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
if (par->lcd_table && blank > FB_BLANK_NORMAL &&
@@ -2705,7 +2706,7 @@ static int atyfb_blank(int blank, struct fb_info *info)
aty_st_le32(CRTC_GEN_CNTL, gen_cntl, par);
#ifdef CONFIG_PMAC_BACKLIGHT
- if ((_machine == _MACH_Pmac) && blank <= FB_BLANK_NORMAL)
+ if (machine_is(powermac) && blank <= FB_BLANK_NORMAL)
set_backlight_enable(1);
#elif defined(CONFIG_FB_ATY_GENERIC_LCD)
if (par->lcd_table && blank <= FB_BLANK_NORMAL &&
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c
index 5886a2f1323e..c7091761cef4 100644
--- a/drivers/video/aty/radeon_pm.c
+++ b/drivers/video/aty/radeon_pm.c
@@ -20,7 +20,7 @@
#include <linux/agp_backend.h>
#ifdef CONFIG_PPC_PMAC
-#include <asm/processor.h>
+#include <asm/machdep.h>
#include <asm/prom.h>
#include <asm/pmac_feature.h>
#endif
@@ -2745,7 +2745,7 @@ void radeonfb_pm_init(struct radeonfb_info *rinfo, int dynclk)
rinfo->pm_mode |= radeon_pm_off;
}
#if defined(CONFIG_PPC_PMAC)
- if (_machine == _MACH_Pmac && rinfo->of_node) {
+ if (machine_is(powermac) && rinfo->of_node) {
if (rinfo->is_mobility && rinfo->pm_reg &&
rinfo->family <= CHIP_FAMILY_RV250)
rinfo->pm_mode |= radeon_pm_d2;
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 9d996f2c10d5..b895eaaa73fd 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -43,11 +43,11 @@ config LCD_DEVICE
default y
config BACKLIGHT_CORGI
- tristate "Sharp Corgi Backlight Driver (SL-C7xx Series)"
+ tristate "Sharp Corgi Backlight Driver (SL Series)"
depends on BACKLIGHT_DEVICE && PXA_SHARPSL
default y
help
- If you have a Sharp Zaurus SL-C7xx, say y to enable the
+ If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the
backlight driver.
config BACKLIGHT_HP680
diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c
index 151fda8dded0..334b1db1bd7c 100644
--- a/drivers/video/backlight/backlight.c
+++ b/drivers/video/backlight/backlight.c
@@ -16,14 +16,12 @@
static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
{
- int rc;
+ int rc = -ENXIO;
struct backlight_device *bd = to_backlight_device(cdev);
down(&bd->sem);
- if (likely(bd->props && bd->props->get_power))
- rc = sprintf(buf, "%d\n", bd->props->get_power(bd));
- else
- rc = -ENXIO;
+ if (likely(bd->props))
+ rc = sprintf(buf, "%d\n", bd->props->power);
up(&bd->sem);
return rc;
@@ -31,7 +29,7 @@ static ssize_t backlight_show_power(struct class_device *cdev, char *buf)
static ssize_t backlight_store_power(struct class_device *cdev, const char *buf, size_t count)
{
- int rc, power;
+ int rc = -ENXIO, power;
char *endp;
struct backlight_device *bd = to_backlight_device(cdev);
@@ -40,12 +38,13 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf,
return -EINVAL;
down(&bd->sem);
- if (likely(bd->props && bd->props->set_power)) {
+ if (likely(bd->props)) {
pr_debug("backlight: set power to %d\n", power);
- bd->props->set_power(bd, power);
+ bd->props->power = power;
+ if (likely(bd->props->update_status))
+ bd->props->update_status(bd);
rc = count;
- } else
- rc = -ENXIO;
+ }
up(&bd->sem);
return rc;
@@ -53,14 +52,12 @@ static ssize_t backlight_store_power(struct class_device *cdev, const char *buf,
static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
{
- int rc;
+ int rc = -ENXIO;
struct backlight_device *bd = to_backlight_device(cdev);
down(&bd->sem);
- if (likely(bd->props && bd->props->get_brightness))
- rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
- else
- rc = -ENXIO;
+ if (likely(bd->props))
+ rc = sprintf(buf, "%d\n", bd->props->brightness);
up(&bd->sem);
return rc;
@@ -68,7 +65,7 @@ static ssize_t backlight_show_brightness(struct class_device *cdev, char *buf)
static ssize_t backlight_store_brightness(struct class_device *cdev, const char *buf, size_t count)
{
- int rc, brightness;
+ int rc = -ENXIO, brightness;
char *endp;
struct backlight_device *bd = to_backlight_device(cdev);
@@ -77,12 +74,18 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char
return -EINVAL;
down(&bd->sem);
- if (likely(bd->props && bd->props->set_brightness)) {
- pr_debug("backlight: set brightness to %d\n", brightness);
- bd->props->set_brightness(bd, brightness);
- rc = count;
- } else
- rc = -ENXIO;
+ if (likely(bd->props)) {
+ if (brightness > bd->props->max_brightness)
+ rc = -EINVAL;
+ else {
+ pr_debug("backlight: set brightness to %d\n",
+ brightness);
+ bd->props->brightness = brightness;
+ if (likely(bd->props->update_status))
+ bd->props->update_status(bd);
+ rc = count;
+ }
+ }
up(&bd->sem);
return rc;
@@ -90,14 +93,26 @@ static ssize_t backlight_store_brightness(struct class_device *cdev, const char
static ssize_t backlight_show_max_brightness(struct class_device *cdev, char *buf)
{
- int rc;
+ int rc = -ENXIO;
struct backlight_device *bd = to_backlight_device(cdev);
down(&bd->sem);
if (likely(bd->props))
rc = sprintf(buf, "%d\n", bd->props->max_brightness);
- else
- rc = -ENXIO;
+ up(&bd->sem);
+
+ return rc;
+}
+
+static ssize_t backlight_show_actual_brightness(struct class_device *cdev,
+ char *buf)
+{
+ int rc = -ENXIO;
+ struct backlight_device *bd = to_backlight_device(cdev);
+
+ down(&bd->sem);
+ if (likely(bd->props && bd->props->get_brightness))
+ rc = sprintf(buf, "%d\n", bd->props->get_brightness(bd));
up(&bd->sem);
return rc;
@@ -123,7 +138,10 @@ static struct class backlight_class = {
static struct class_device_attribute bl_class_device_attributes[] = {
DECLARE_ATTR(power, 0644, backlight_show_power, backlight_store_power),
- DECLARE_ATTR(brightness, 0644, backlight_show_brightness, backlight_store_brightness),
+ DECLARE_ATTR(brightness, 0644, backlight_show_brightness,
+ backlight_store_brightness),
+ DECLARE_ATTR(actual_brightness, 0444, backlight_show_actual_brightness,
+ NULL),
DECLARE_ATTR(max_brightness, 0444, backlight_show_max_brightness, NULL),
};
@@ -144,8 +162,12 @@ static int fb_notifier_callback(struct notifier_block *self,
bd = container_of(self, struct backlight_device, fb_notif);
down(&bd->sem);
if (bd->props)
- if (!bd->props->check_fb || bd->props->check_fb(evdata->info))
- bd->props->set_power(bd, *(int *)evdata->data);
+ if (!bd->props->check_fb ||
+ bd->props->check_fb(evdata->info)) {
+ bd->props->fb_blank = *(int *)evdata->data;
+ if (likely(bd->props && bd->props->update_status))
+ bd->props->update_status(bd);
+ }
up(&bd->sem);
return 0;
}
@@ -231,6 +253,12 @@ void backlight_device_unregister(struct backlight_device *bd)
&bl_class_device_attributes[i]);
down(&bd->sem);
+ if (likely(bd->props && bd->props->update_status)) {
+ bd->props->brightness = 0;
+ bd->props->power = 0;
+ bd->props->update_status(bd);
+ }
+
bd->props = NULL;
up(&bd->sem);
diff --git a/drivers/video/backlight/corgi_bl.c b/drivers/video/backlight/corgi_bl.c
index d0aaf450e8c7..2ebbfd95145f 100644
--- a/drivers/video/backlight/corgi_bl.c
+++ b/drivers/video/backlight/corgi_bl.c
@@ -1,7 +1,7 @@
/*
- * Backlight Driver for Sharp Corgi
+ * Backlight Driver for Sharp Zaurus Handhelds (various models)
*
- * Copyright (c) 2004-2005 Richard Purdie
+ * Copyright (c) 2004-2006 Richard Purdie
*
* Based on Sharp's 2.4 Backlight Driver
*
@@ -15,80 +15,63 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/platform_device.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/fb.h>
#include <linux/backlight.h>
-
#include <asm/arch/sharpsl.h>
#include <asm/hardware/sharpsl_pm.h>
-#define CORGI_DEFAULT_INTENSITY 0x1f
-#define CORGI_LIMIT_MASK 0x0b
-
-static int corgibl_powermode = FB_BLANK_UNBLANK;
-static int current_intensity = 0;
-static int corgibl_limit = 0;
-static void (*corgibl_mach_set_intensity)(int intensity);
-static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED;
+static int corgibl_intensity;
+static DEFINE_MUTEX(bl_mutex);
static struct backlight_properties corgibl_data;
+static struct backlight_device *corgi_backlight_device;
+static struct corgibl_machinfo *bl_machinfo;
-static void corgibl_send_intensity(int intensity)
+static unsigned long corgibl_flags;
+#define CORGIBL_SUSPENDED 0x01
+#define CORGIBL_BATTLOW 0x02
+
+static int corgibl_send_intensity(struct backlight_device *bd)
{
- unsigned long flags;
void (*corgi_kick_batt)(void);
+ int intensity = bd->props->brightness;
- if (corgibl_powermode != FB_BLANK_UNBLANK) {
+ if (bd->props->power != FB_BLANK_UNBLANK)
intensity = 0;
- } else {
- if (corgibl_limit)
- intensity &= CORGI_LIMIT_MASK;
- }
-
- spin_lock_irqsave(&bl_lock, flags);
+ if (bd->props->fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (corgibl_flags & CORGIBL_SUSPENDED)
+ intensity = 0;
+ if (corgibl_flags & CORGIBL_BATTLOW)
+ intensity &= bl_machinfo->limit_mask;
- corgibl_mach_set_intensity(intensity);
+ mutex_lock(&bl_mutex);
+ bl_machinfo->set_bl_intensity(intensity);
+ mutex_unlock(&bl_mutex);
- spin_unlock_irqrestore(&bl_lock, flags);
+ corgibl_intensity = intensity;
corgi_kick_batt = symbol_get(sharpsl_battery_kick);
if (corgi_kick_batt) {
corgi_kick_batt();
symbol_put(sharpsl_battery_kick);
}
-}
-static void corgibl_blank(int blank)
-{
- switch(blank) {
-
- case FB_BLANK_NORMAL:
- case FB_BLANK_VSYNC_SUSPEND:
- case FB_BLANK_HSYNC_SUSPEND:
- case FB_BLANK_POWERDOWN:
- if (corgibl_powermode == FB_BLANK_UNBLANK) {
- corgibl_send_intensity(0);
- corgibl_powermode = blank;
- }
- break;
- case FB_BLANK_UNBLANK:
- if (corgibl_powermode != FB_BLANK_UNBLANK) {
- corgibl_powermode = blank;
- corgibl_send_intensity(current_intensity);
- }
- break;
- }
+ return 0;
}
#ifdef CONFIG_PM
static int corgibl_suspend(struct platform_device *dev, pm_message_t state)
{
- corgibl_blank(FB_BLANK_POWERDOWN);
+ corgibl_flags |= CORGIBL_SUSPENDED;
+ corgibl_send_intensity(corgi_backlight_device);
return 0;
}
static int corgibl_resume(struct platform_device *dev)
{
- corgibl_blank(FB_BLANK_UNBLANK);
+ corgibl_flags &= ~CORGIBL_SUSPENDED;
+ corgibl_send_intensity(corgi_backlight_device);
return 0;
}
#else
@@ -96,68 +79,55 @@ static int corgibl_resume(struct platform_device *dev)
#define corgibl_resume NULL
#endif
-
-static int corgibl_set_power(struct backlight_device *bd, int state)
-{
- corgibl_blank(state);
- return 0;
-}
-
-static int corgibl_get_power(struct backlight_device *bd)
+static int corgibl_get_intensity(struct backlight_device *bd)
{
- return corgibl_powermode;
+ return corgibl_intensity;
}
-static int corgibl_set_intensity(struct backlight_device *bd, int intensity)
+static int corgibl_set_intensity(struct backlight_device *bd)
{
- if (intensity > corgibl_data.max_brightness)
- intensity = corgibl_data.max_brightness;
- corgibl_send_intensity(intensity);
- current_intensity=intensity;
+ corgibl_send_intensity(corgi_backlight_device);
return 0;
}
-static int corgibl_get_intensity(struct backlight_device *bd)
-{
- return current_intensity;
-}
-
/*
* Called when the battery is low to limit the backlight intensity.
* If limit==0 clear any limit, otherwise limit the intensity
*/
void corgibl_limit_intensity(int limit)
{
- corgibl_limit = (limit ? 1 : 0);
- corgibl_send_intensity(current_intensity);
+ if (limit)
+ corgibl_flags |= CORGIBL_BATTLOW;
+ else
+ corgibl_flags &= ~CORGIBL_BATTLOW;
+ corgibl_send_intensity(corgi_backlight_device);
}
EXPORT_SYMBOL(corgibl_limit_intensity);
static struct backlight_properties corgibl_data = {
- .owner = THIS_MODULE,
- .get_power = corgibl_get_power,
- .set_power = corgibl_set_power,
+ .owner = THIS_MODULE,
.get_brightness = corgibl_get_intensity,
- .set_brightness = corgibl_set_intensity,
+ .update_status = corgibl_set_intensity,
};
-static struct backlight_device *corgi_backlight_device;
-
static int __init corgibl_probe(struct platform_device *pdev)
{
struct corgibl_machinfo *machinfo = pdev->dev.platform_data;
+ bl_machinfo = machinfo;
corgibl_data.max_brightness = machinfo->max_intensity;
- corgibl_mach_set_intensity = machinfo->set_bl_intensity;
+ if (!machinfo->limit_mask)
+ machinfo->limit_mask = -1;
corgi_backlight_device = backlight_device_register ("corgi-bl",
NULL, &corgibl_data);
if (IS_ERR (corgi_backlight_device))
return PTR_ERR (corgi_backlight_device);
- corgibl_set_intensity(NULL, CORGI_DEFAULT_INTENSITY);
- corgibl_limit_intensity(0);
+ corgibl_data.power = FB_BLANK_UNBLANK;
+ corgibl_data.brightness = machinfo->default_intensity;
+ corgibl_send_intensity(corgi_backlight_device);
printk("Corgi Backlight Driver Initialized.\n");
return 0;
@@ -167,8 +137,6 @@ static int corgibl_remove(struct platform_device *dev)
{
backlight_device_unregister(corgi_backlight_device);
- corgibl_set_intensity(NULL, 0);
-
printk("Corgi Backlight Driver Unloaded\n");
return 0;
}
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index 95da4c9ed1f1..a71e984c93d4 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -13,7 +13,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/fb.h>
#include <linux/backlight.h>
@@ -25,66 +25,58 @@
#define HP680_MAX_INTENSITY 255
#define HP680_DEFAULT_INTENSITY 10
-static int hp680bl_powermode = FB_BLANK_UNBLANK;
+static int hp680bl_suspended;
static int current_intensity = 0;
static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED;
+static struct backlight_device *hp680_backlight_device;
-static void hp680bl_send_intensity(int intensity)
+static void hp680bl_send_intensity(struct backlight_device *bd)
{
unsigned long flags;
+ u16 v;
+ int intensity = bd->props->brightness;
- if (hp680bl_powermode != FB_BLANK_UNBLANK)
+ if (bd->props->power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props->fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (hp680bl_suspended)
intensity = 0;
spin_lock_irqsave(&bl_lock, flags);
- sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS);
+ if (intensity && current_intensity == 0) {
+ sh_dac_enable(DAC_LCD_BRIGHTNESS);
+ v = inw(HD64461_GPBDR);
+ v &= ~HD64461_GPBDR_LCDOFF;
+ outw(v, HD64461_GPBDR);
+ sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS);
+ } else if (intensity == 0 && current_intensity != 0) {
+ sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS);
+ sh_dac_disable(DAC_LCD_BRIGHTNESS);
+ v = inw(HD64461_GPBDR);
+ v |= HD64461_GPBDR_LCDOFF;
+ outw(v, HD64461_GPBDR);
+ } else if (intensity) {
+ sh_dac_output(255-(u8)intensity, DAC_LCD_BRIGHTNESS);
+ }
spin_unlock_irqrestore(&bl_lock, flags);
-}
-static void hp680bl_blank(int blank)
-{
- u16 v;
-
- switch(blank) {
-
- case FB_BLANK_NORMAL:
- case FB_BLANK_VSYNC_SUSPEND:
- case FB_BLANK_HSYNC_SUSPEND:
- case FB_BLANK_POWERDOWN:
- if (hp680bl_powermode == FB_BLANK_UNBLANK) {
- hp680bl_send_intensity(0);
- hp680bl_powermode = blank;
- sh_dac_disable(DAC_LCD_BRIGHTNESS);
- v = inw(HD64461_GPBDR);
- v |= HD64461_GPBDR_LCDOFF;
- outw(v, HD64461_GPBDR);
- }
- break;
- case FB_BLANK_UNBLANK:
- if (hp680bl_powermode != FB_BLANK_UNBLANK) {
- sh_dac_enable(DAC_LCD_BRIGHTNESS);
- v = inw(HD64461_GPBDR);
- v &= ~HD64461_GPBDR_LCDOFF;
- outw(v, HD64461_GPBDR);
- hp680bl_powermode = blank;
- hp680bl_send_intensity(current_intensity);
- }
- break;
- }
+ current_intensity = intensity;
}
+
#ifdef CONFIG_PM
-static int hp680bl_suspend(struct device *dev, pm_message_t state, u32 level)
+static int hp680bl_suspend(struct platform_device *dev, pm_message_t state)
{
- if (level == SUSPEND_POWER_DOWN)
- hp680bl_blank(FB_BLANK_POWERDOWN);
+ hp680bl_suspended = 1;
+ hp680bl_send_intensity(hp680_backlight_device);
return 0;
}
-static int hp680bl_resume(struct device *dev, u32 level)
+static int hp680bl_resume(struct platform_device *dev)
{
- if (level == RESUME_POWER_ON)
- hp680bl_blank(FB_BLANK_UNBLANK);
+ hp680bl_suspended = 0;
+ hp680bl_send_intensity(hp680_backlight_device);
return 0;
}
#else
@@ -92,24 +84,9 @@ static int hp680bl_resume(struct device *dev, u32 level)
#define hp680bl_resume NULL
#endif
-
-static int hp680bl_set_power(struct backlight_device *bd, int state)
+static int hp680bl_set_intensity(struct backlight_device *bd)
{
- hp680bl_blank(state);
- return 0;
-}
-
-static int hp680bl_get_power(struct backlight_device *bd)
-{
- return hp680bl_powermode;
-}
-
-static int hp680bl_set_intensity(struct backlight_device *bd, int intensity)
-{
- if (intensity > HP680_MAX_INTENSITY)
- intensity = HP680_MAX_INTENSITY;
- hp680bl_send_intensity(intensity);
- current_intensity = intensity;
+ hp680bl_send_intensity(bd);
return 0;
}
@@ -120,65 +97,67 @@ static int hp680bl_get_intensity(struct backlight_device *bd)
static struct backlight_properties hp680bl_data = {
.owner = THIS_MODULE,
- .get_power = hp680bl_get_power,
- .set_power = hp680bl_set_power,
.max_brightness = HP680_MAX_INTENSITY,
.get_brightness = hp680bl_get_intensity,
- .set_brightness = hp680bl_set_intensity,
+ .update_status = hp680bl_set_intensity,
};
-static struct backlight_device *hp680_backlight_device;
-
-static int __init hp680bl_probe(struct device *dev)
+static int __init hp680bl_probe(struct platform_device *dev)
{
hp680_backlight_device = backlight_device_register ("hp680-bl",
NULL, &hp680bl_data);
if (IS_ERR (hp680_backlight_device))
return PTR_ERR (hp680_backlight_device);
- hp680bl_set_intensity(NULL, HP680_DEFAULT_INTENSITY);
+ hp680_backlight_device->props->brightness = HP680_DEFAULT_INTENSITY;
+ hp680bl_send_intensity(hp680_backlight_device);
return 0;
}
-static int hp680bl_remove(struct device *dev)
+static int hp680bl_remove(struct platform_device *dev)
{
backlight_device_unregister(hp680_backlight_device);
return 0;
}
-static struct device_driver hp680bl_driver = {
- .name = "hp680-bl",
- .bus = &platform_bus_type,
+static struct platform_driver hp680bl_driver = {
.probe = hp680bl_probe,
.remove = hp680bl_remove,
.suspend = hp680bl_suspend,
.resume = hp680bl_resume,
+ .driver = {
+ .name = "hp680-bl",
+ },
};
-static struct platform_device hp680bl_device = {
- .name = "hp680-bl",
- .id = -1,
-};
+static struct platform_device *hp680bl_device;
static int __init hp680bl_init(void)
{
int ret;
- ret=driver_register(&hp680bl_driver);
+ ret = platform_driver_register(&hp680bl_driver);
if (!ret) {
- ret = platform_device_register(&hp680bl_device);
- if (ret)
- driver_unregister(&hp680bl_driver);
+ hp680bl_device = platform_device_alloc("hp680-bl", -1);
+ if (!hp680bl_device)
+ return -ENOMEM;
+
+ ret = platform_device_add(hp680bl_device);
+
+ if (ret) {
+ platform_device_put(hp680bl_device);
+ platform_driver_unregister(&hp680bl_driver);
+ }
}
return ret;
}
static void __exit hp680bl_exit(void)
{
- platform_device_unregister(&hp680bl_device);
- driver_unregister(&hp680bl_driver);
+ platform_device_unregister(hp680bl_device);
+ platform_driver_unregister(&hp680bl_driver);
}
module_init(hp680bl_init);
diff --git a/drivers/video/cfbimgblt.c b/drivers/video/cfbimgblt.c
index 910e2338a27e..8ba6152db2fd 100644
--- a/drivers/video/cfbimgblt.c
+++ b/drivers/video/cfbimgblt.c
@@ -169,7 +169,7 @@ static inline void slow_imageblit(const struct fb_image *image, struct fb_info *
while (j--) {
l--;
- color = (*s & 1 << (FB_BIT_NR(l))) ? fgcolor : bgcolor;
+ color = (*s & (1 << l)) ? fgcolor : bgcolor;
val |= FB_SHIFT_HIGH(color, shift);
/* Did the bitshift spill bits to the next long? */
diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c
index 66d6f2f0a219..1103010af54a 100644
--- a/drivers/video/cirrusfb.c
+++ b/drivers/video/cirrusfb.c
@@ -60,8 +60,8 @@
#include <asm/amigahw.h>
#endif
#ifdef CONFIG_PPC_PREP
-#include <asm/processor.h>
-#define isPReP (_machine == _MACH_prep)
+#include <asm/machdep.h>
+#define isPReP (machine_is(prep))
#else
#define isPReP 0
#endif
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 041d06987861..ca020719d20b 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -466,7 +466,7 @@ static int __init fb_console_setup(char *this_opt)
int i, j;
if (!this_opt || !*this_opt)
- return 0;
+ return 1;
while ((options = strsep(&this_opt, ",")) != NULL) {
if (!strncmp(options, "font:", 5))
@@ -481,10 +481,10 @@ static int __init fb_console_setup(char *this_opt)
options++;
}
if (*options != ',')
- return 0;
+ return 1;
options++;
} else
- return 0;
+ return 1;
}
if (!strncmp(options, "map:", 4)) {
@@ -496,7 +496,7 @@ static int __init fb_console_setup(char *this_opt)
con2fb_map_boot[i] =
(options[j++]-'0') % FB_MAX;
}
- return 0;
+ return 1;
}
if (!strncmp(options, "vc:", 3)) {
@@ -518,7 +518,7 @@ static int __init fb_console_setup(char *this_opt)
rotate = 0;
}
}
- return 0;
+ return 1;
}
__setup("fbcon=", fb_console_setup);
@@ -1142,6 +1142,7 @@ static void fbcon_init(struct vc_data *vc, int init)
set_blitting_type(vc, info);
}
+ ops->p = &fb_display[fg_console];
}
static void fbcon_deinit(struct vc_data *vc)
diff --git a/drivers/video/console/sticore.c b/drivers/video/console/sticore.c
index 0339f5640a78..74ac2acaf72c 100644
--- a/drivers/video/console/sticore.c
+++ b/drivers/video/console/sticore.c
@@ -275,7 +275,7 @@ static int __init sti_setup(char *str)
if (str)
strlcpy (default_sti_path, str, sizeof (default_sti_path));
- return 0;
+ return 1;
}
/* Assuming the machine has multiple STI consoles (=graphic cards) which
@@ -321,7 +321,7 @@ static int __init sti_font_setup(char *str)
i++;
}
- return 0;
+ return 1;
}
/* The optional linux kernel parameter "sti_font" defines which font
@@ -373,7 +373,7 @@ sti_dump_globcfg(struct sti_glob_cfg *glob_cfg, unsigned int sti_mem_request)
glob_cfg->save_addr));
/* dump extended cfg */
- cfg = PTR_STI(glob_cfg->ext_ptr);
+ cfg = PTR_STI((unsigned long)glob_cfg->ext_ptr);
DPRINTK(( KERN_INFO
"monitor %d\n"
"in friendly mode: %d\n"
@@ -453,25 +453,11 @@ sti_init_glob_cfg(struct sti_struct *sti,
sti->regions_phys[i] =
REGION_OFFSET_TO_PHYS(sti->regions[i], newhpa);
- /* remap virtually */
- /* FIXME: add BTLB support if btlb==1 */
len = sti->regions[i].region_desc.length * 4096;
-
-/* XXX: Enabling IOREMAP debugging causes a crash, so we must be passing
- * a virtual address to something expecting a physical address that doesn't
- * go through a readX macro */
-#if 0
- if (len)
- glob_cfg->region_ptrs[i] = (unsigned long) (
- sti->regions[i].region_desc.cache ?
- ioremap(sti->regions_phys[i], len) :
- ioremap_nocache(sti->regions_phys[i], len) );
-#else
if (len)
glob_cfg->region_ptrs[i] = sti->regions_phys[i];
-#endif
- DPRINTK(("region #%d: phys %08lx, virt %08x, len=%lukB, "
+ DPRINTK(("region #%d: phys %08lx, region_ptr %08x, len=%lukB, "
"btlb=%d, sysonly=%d, cache=%d, last=%d\n",
i, sti->regions_phys[i], glob_cfg->region_ptrs[i],
len/1024,
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index b1a8dca76430..944855b3e4af 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1588,7 +1588,7 @@ static int __init video_setup(char *options)
}
}
- return 0;
+ return 1;
}
__setup("video=", video_setup);
#endif
diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
index 951c9974a1d3..23c1827b2d0b 100644
--- a/drivers/video/matrox/matroxfb_base.c
+++ b/drivers/video/matrox/matroxfb_base.c
@@ -115,6 +115,7 @@
#include <asm/uaccess.h>
#ifdef CONFIG_PPC_PMAC
+#include <asm/machdep.h>
unsigned char nvram_read_byte(int);
static int default_vmode = VMODE_NVRAM;
static int default_cmode = CMODE_NVRAM;
@@ -1833,7 +1834,7 @@ static int initMatrox2(WPMINFO struct board* b){
/* FIXME: Where to move this?! */
#if defined(CONFIG_PPC_PMAC)
#ifndef MODULE
- if (_machine == _MACH_Pmac) {
+ if (machine_is(powermac)) {
struct fb_var_screeninfo var;
if (default_vmode <= 0 || default_vmode > VMODE_MAX)
default_vmode = VMODE_640_480_60;
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
index 6d3e4890cb43..093ab9977c7c 100644
--- a/drivers/video/nvidia/nvidia.c
+++ b/drivers/video/nvidia/nvidia.c
@@ -30,6 +30,7 @@
#include <asm/pci-bridge.h>
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/machdep.h>
#include <asm/backlight.h>
#endif
@@ -1355,7 +1356,7 @@ static int nvidiafb_blank(int blank, struct fb_info *info)
NVWriteCrtc(par, 0x1a, vesa);
#ifdef CONFIG_PMAC_BACKLIGHT
- if (par->FlatPanel && _machine == _MACH_Pmac) {
+ if (par->FlatPanel && machine_is(powermac)) {
set_backlight_enable(!blank);
}
#endif
@@ -1741,7 +1742,7 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd,
info->fix.id,
par->FbMapSize / (1024 * 1024), info->fix.smem_start);
#ifdef CONFIG_PMAC_BACKLIGHT
- if (par->FlatPanel && _machine == _MACH_Pmac)
+ if (par->FlatPanel && machine_is(powermac))
register_backlight_controller(&nvidia_backlight_controller,
par, "mnca");
#endif
diff --git a/drivers/video/pxafb.c b/drivers/video/pxafb.c
index 53ad61f1038c..809fc5eefc15 100644
--- a/drivers/video/pxafb.c
+++ b/drivers/video/pxafb.c
@@ -232,9 +232,9 @@ static int pxafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
if (var->yres < MIN_YRES)
var->yres = MIN_YRES;
if (var->xres > fbi->max_xres)
- var->xres = fbi->max_xres;
+ return -EINVAL;
if (var->yres > fbi->max_yres)
- var->yres = fbi->max_yres;
+ return -EINVAL;
var->xres_virtual =
max(var->xres_virtual, var->xres);
var->yres_virtual =
@@ -781,7 +781,7 @@ static void pxafb_disable_controller(struct pxafb_info *fbi)
LCCR0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */
LCCR0 |= LCCR0_DIS; /* Disable LCD Controller */
- schedule_timeout(20 * HZ / 1000);
+ schedule_timeout(200 * HZ / 1000);
remove_wait_queue(&fbi->ctrlr_wait, &wait);
/* disable LCD controller clock */
@@ -1274,7 +1274,7 @@ int __init pxafb_probe(struct platform_device *dev)
struct pxafb_mach_info *inf;
int ret;
- dev_dbg(dev, "pxafb_probe\n");
+ dev_dbg(&dev->dev, "pxafb_probe\n");
inf = dev->dev.platform_data;
ret = -ENOMEM;
diff --git a/drivers/video/radeonfb.c b/drivers/video/radeonfb.c
deleted file mode 100644
index 24982adb3aa2..000000000000
--- a/drivers/video/radeonfb.c
+++ /dev/null
@@ -1,3167 +0,0 @@
-/*
- * drivers/video/radeonfb.c
- * framebuffer driver for ATI Radeon chipset video boards
- *
- * Copyright 2000 Ani Joshi <ajoshi@kernel.crashing.org>
- *
- *
- * ChangeLog:
- * 2000-08-03 initial version 0.0.1
- * 2000-09-10 more bug fixes, public release 0.0.5
- * 2001-02-19 mode bug fixes, 0.0.7
- * 2001-07-05 fixed scrolling issues, engine initialization,
- * and minor mode tweaking, 0.0.9
- * 2001-09-07 Radeon VE support, Nick Kurshev
- * blanking, pan_display, and cmap fixes, 0.1.0
- * 2001-10-10 Radeon 7500 and 8500 support, and experimental
- * flat panel support, 0.1.1
- * 2001-11-17 Radeon M6 (ppc) support, Daniel Berlin, 0.1.2
- * 2001-11-18 DFP fixes, Kevin Hendricks, 0.1.3
- * 2001-11-29 more cmap, backlight fixes, Benjamin Herrenschmidt
- * 2002-01-18 DFP panel detection via BIOS, Michael Clark, 0.1.4
- * 2002-06-02 console switching, mode set fixes, accel fixes
- * 2002-06-03 MTRR support, Peter Horton, 0.1.5
- * 2002-09-21 rv250, r300, m9 initial support,
- * added mirror option, 0.1.6
- *
- * Special thanks to ATI DevRel team for their hardware donations.
- *
- */
-
-
-#define RADEON_VERSION "0.1.6"
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/mm.h>
-#include <linux/tty.h>
-#include <linux/slab.h>
-#include <linux/delay.h>
-#include <linux/fb.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/vmalloc.h>
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#if defined(__powerpc__)
-#include <asm/prom.h>
-#include <asm/pci-bridge.h>
-#include "macmodes.h"
-
-#ifdef CONFIG_NVRAM
-#include <linux/nvram.h>
-#endif
-
-#ifdef CONFIG_PMAC_BACKLIGHT
-#include <asm/backlight.h>
-#endif
-
-#ifdef CONFIG_BOOTX_TEXT
-#include <asm/btext.h>
-#endif
-
-#ifdef CONFIG_ADB_PMU
-#include <linux/adb.h>
-#include <linux/pmu.h>
-#endif
-
-#endif /* __powerpc__ */
-
-#ifdef CONFIG_MTRR
-#include <asm/mtrr.h>
-#endif
-
-#include <video/radeon.h>
-#include <linux/radeonfb.h>
-
-#define DEBUG 0
-
-#if DEBUG
-#define RTRACE printk
-#else
-#define RTRACE if(0) printk
-#endif
-
-// XXX
-#undef CONFIG_PMAC_PBOOK
-
-
-enum radeon_chips {
- RADEON_QD,
- RADEON_QE,
- RADEON_QF,
- RADEON_QG,
- RADEON_QY,
- RADEON_QZ,
- RADEON_LW,
- RADEON_LX,
- RADEON_LY,
- RADEON_LZ,
- RADEON_QL,
- RADEON_QN,
- RADEON_QO,
- RADEON_Ql,
- RADEON_BB,
- RADEON_QW,
- RADEON_QX,
- RADEON_Id,
- RADEON_Ie,
- RADEON_If,
- RADEON_Ig,
- RADEON_Ya,
- RADEON_Yd,
- RADEON_Ld,
- RADEON_Le,
- RADEON_Lf,
- RADEON_Lg,
- RADEON_ND,
- RADEON_NE,
- RADEON_NF,
- RADEON_NG,
- RADEON_QM
-};
-
-enum radeon_arch {
- RADEON_R100,
- RADEON_RV100,
- RADEON_R200,
- RADEON_RV200,
- RADEON_RV250,
- RADEON_R300,
- RADEON_M6,
- RADEON_M7,
- RADEON_M9
-};
-
-static struct radeon_chip_info {
- const char *name;
- unsigned char arch;
-} radeon_chip_info[] __devinitdata = {
- { "QD", RADEON_R100 },
- { "QE", RADEON_R100 },
- { "QF", RADEON_R100 },
- { "QG", RADEON_R100 },
- { "VE QY", RADEON_RV100 },
- { "VE QZ", RADEON_RV100 },
- { "M7 LW", RADEON_M7 },
- { "M7 LX", RADEON_M7 },
- { "M6 LY", RADEON_M6 },
- { "M6 LZ", RADEON_M6 },
- { "8500 QL", RADEON_R200 },
- { "8500 QN", RADEON_R200 },
- { "8500 QO", RADEON_R200 },
- { "8500 Ql", RADEON_R200 },
- { "8500 BB", RADEON_R200 },
- { "7500 QW", RADEON_RV200 },
- { "7500 QX", RADEON_RV200 },
- { "9000 Id", RADEON_RV250 },
- { "9000 Ie", RADEON_RV250 },
- { "9000 If", RADEON_RV250 },
- { "9000 Ig", RADEON_RV250 },
- { "M9 Ld", RADEON_M9 },
- { "M9 Le", RADEON_M9 },
- { "M9 Lf", RADEON_M9 },
- { "M9 Lg", RADEON_M9 },
- { "9700 ND", RADEON_R300 },
- { "9700 NE", RADEON_R300 },
- { "9700 NF", RADEON_R300 },
- { "9700 NG", RADEON_R300 },
- { "9100 QM", RADEON_R200 }
-};
-
-
-enum radeon_montype
-{
- MT_NONE,
- MT_CRT, /* CRT */
- MT_LCD, /* LCD */
- MT_DFP, /* DVI */
- MT_CTV, /* composite TV */
- MT_STV /* S-Video out */
-};
-
-
-static struct pci_device_id radeonfb_pci_table[] = {
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QD, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QD},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QE},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QF},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QG, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QG},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QY, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QY},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QZ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QZ},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_LW, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_LW},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_LX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_LX},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_LY, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_LY},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_LZ, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_LZ},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QL, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QL},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QN, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QN},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QO, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QO},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ql, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ql},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_BB, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_BB},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QW, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QW},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QX, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QX},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Id, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Id},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ie, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ie},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_If, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_If},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ig, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ig},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ya, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ya},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Yd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Yd},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Ld, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Ld},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Le, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Le},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Lf, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Lf},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_Lg, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_Lg},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_ND, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_ND},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_NE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_NE},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_NF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_NF},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_NG, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_NG},
- { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RADEON_QM, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RADEON_QM},
- { 0, }
-};
-MODULE_DEVICE_TABLE(pci, radeonfb_pci_table);
-
-
-typedef struct {
- u16 reg;
- u32 val;
-} reg_val;
-
-
-/* these common regs are cleared before mode setting so they do not
- * interfere with anything
- */
-static reg_val common_regs[] = {
- { OVR_CLR, 0 },
- { OVR_WID_LEFT_RIGHT, 0 },
- { OVR_WID_TOP_BOTTOM, 0 },
- { OV0_SCALE_CNTL, 0 },
- { SUBPIC_CNTL, 0 },
- { VIPH_CONTROL, 0 },
- { I2C_CNTL_1, 0 },
- { GEN_INT_CNTL, 0 },
- { CAP0_TRIG_CNTL, 0 },
-};
-
-static reg_val common_regs_m6[] = {
- { OVR_CLR, 0 },
- { OVR_WID_LEFT_RIGHT, 0 },
- { OVR_WID_TOP_BOTTOM, 0 },
- { OV0_SCALE_CNTL, 0 },
- { SUBPIC_CNTL, 0 },
- { GEN_INT_CNTL, 0 },
- { CAP0_TRIG_CNTL, 0 }
-};
-
-typedef struct {
- u8 clock_chip_type;
- u8 struct_size;
- u8 accelerator_entry;
- u8 VGA_entry;
- u16 VGA_table_offset;
- u16 POST_table_offset;
- u16 XCLK;
- u16 MCLK;
- u8 num_PLL_blocks;
- u8 size_PLL_blocks;
- u16 PCLK_ref_freq;
- u16 PCLK_ref_divider;
- u32 PCLK_min_freq;
- u32 PCLK_max_freq;
- u16 MCLK_ref_freq;
- u16 MCLK_ref_divider;
- u32 MCLK_min_freq;
- u32 MCLK_max_freq;
- u16 XCLK_ref_freq;
- u16 XCLK_ref_divider;
- u32 XCLK_min_freq;
- u32 XCLK_max_freq;
-} __attribute__ ((packed)) PLL_BLOCK;
-
-
-struct pll_info {
- int ppll_max;
- int ppll_min;
- int xclk;
- int ref_div;
- int ref_clk;
-};
-
-
-struct ram_info {
- int ml;
- int mb;
- int trcd;
- int trp;
- int twr;
- int cl;
- int tr2w;
- int loop_latency;
- int rloop;
-};
-
-
-struct radeon_regs {
- /* CRTC regs */
- u32 crtc_h_total_disp;
- u32 crtc_h_sync_strt_wid;
- u32 crtc_v_total_disp;
- u32 crtc_v_sync_strt_wid;
- u32 crtc_pitch;
- u32 crtc_gen_cntl;
- u32 crtc_ext_cntl;
- u32 dac_cntl;
-
- u32 flags;
- u32 pix_clock;
- int xres, yres;
-
- /* DDA regs */
- u32 dda_config;
- u32 dda_on_off;
-
- /* PLL regs */
- u32 ppll_div_3;
- u32 ppll_ref_div;
- u32 vclk_ecp_cntl;
-
- /* Flat panel regs */
- u32 fp_crtc_h_total_disp;
- u32 fp_crtc_v_total_disp;
- u32 fp_gen_cntl;
- u32 fp_h_sync_strt_wid;
- u32 fp_horz_stretch;
- u32 fp_panel_cntl;
- u32 fp_v_sync_strt_wid;
- u32 fp_vert_stretch;
- u32 lvds_gen_cntl;
- u32 lvds_pll_cntl;
- u32 tmds_crc;
- u32 tmds_transmitter_cntl;
-
-#if defined(__BIG_ENDIAN)
- u32 surface_cntl;
-#endif
-};
-
-
-struct radeonfb_info {
- struct fb_info info;
-
- struct radeon_regs state;
- struct radeon_regs init_state;
-
- char name[32];
- char ram_type[12];
-
- unsigned long mmio_base_phys;
- unsigned long fb_base_phys;
-
- void __iomem *mmio_base;
- void __iomem *fb_base;
-
- struct pci_dev *pdev;
-
- unsigned char *EDID;
- unsigned char __iomem *bios_seg;
-
- u32 pseudo_palette[17];
- struct { u8 red, green, blue, pad; } palette[256];
-
- int chipset;
- unsigned char arch;
- int video_ram;
- u8 rev;
- int pitch, bpp, depth;
- int xres, yres, pixclock;
- int xres_virtual, yres_virtual;
- u32 accel_flags;
-
- int use_default_var;
- int got_dfpinfo;
-
- int hasCRTC2;
- int crtDisp_type;
- int dviDisp_type;
-
- int panel_xres, panel_yres;
- int clock;
- int hOver_plus, hSync_width, hblank;
- int vOver_plus, vSync_width, vblank;
- int hAct_high, vAct_high, interlaced;
- int synct, misc;
-
- u32 dp_gui_master_cntl;
-
- struct pll_info pll;
- int pll_output_freq, post_div, fb_div;
-
- struct ram_info ram;
-
- int mtrr_hdl;
-
-#ifdef CONFIG_PMAC_PBOOK
- int pm_reg;
- u32 save_regs[64];
- u32 mdll, mdll2;
-#endif /* CONFIG_PMAC_PBOOK */
- int asleep;
-
- struct radeonfb_info *next;
-};
-
-
-static struct fb_var_screeninfo radeonfb_default_var = {
- 640, 480, 640, 480, 0, 0, 8, 0,
- {0, 6, 0}, {0, 6, 0}, {0, 6, 0}, {0, 0, 0},
- 0, 0, -1, -1, 0, 39721, 40, 24, 32, 11, 96, 2,
- 0, FB_VMODE_NONINTERLACED
-};
-
-/*
- * IO macros
- */
-
-#define INREG8(addr) readb((rinfo->mmio_base)+addr)
-#define OUTREG8(addr,val) writeb(val, (rinfo->mmio_base)+addr)
-#define INREG(addr) readl((rinfo->mmio_base)+addr)
-#define OUTREG(addr,val) writel(val, (rinfo->mmio_base)+addr)
-
-#define OUTPLL(addr,val) \
- do { \
- OUTREG8(CLOCK_CNTL_INDEX, (addr & 0x0000003f) | 0x00000080); \
- OUTREG(CLOCK_CNTL_DATA, val); \
- } while(0)
-
-#define OUTPLLP(addr,val,mask) \
- do { \
- unsigned int _tmp = INPLL(addr); \
- _tmp &= (mask); \
- _tmp |= (val); \
- OUTPLL(addr, _tmp); \
- } while (0)
-
-#define OUTREGP(addr,val,mask) \
- do { \
- unsigned int _tmp = INREG(addr); \
- _tmp &= (mask); \
- _tmp |= (val); \
- OUTREG(addr, _tmp); \
- } while (0)
-
-
-static __inline__ u32 _INPLL(struct radeonfb_info *rinfo, u32 addr)
-{
- OUTREG8(CLOCK_CNTL_INDEX, addr & 0x0000003f);
- return (INREG(CLOCK_CNTL_DATA));
-}
-
-#define INPLL(addr) _INPLL(rinfo, addr)
-
-#define PRIMARY_MONITOR(rinfo) ((rinfo->dviDisp_type != MT_NONE) && \
- (rinfo->dviDisp_type != MT_STV) && \
- (rinfo->dviDisp_type != MT_CTV) ? \
- rinfo->dviDisp_type : rinfo->crtDisp_type)
-
-static char *GET_MON_NAME(int type)
-{
- char *pret = NULL;
-
- switch (type) {
- case MT_NONE:
- pret = "no";
- break;
- case MT_CRT:
- pret = "CRT";
- break;
- case MT_DFP:
- pret = "DFP";
- break;
- case MT_LCD:
- pret = "LCD";
- break;
- case MT_CTV:
- pret = "CTV";
- break;
- case MT_STV:
- pret = "STV";
- break;
- }
-
- return pret;
-}
-
-
-/*
- * 2D engine routines
- */
-
-static __inline__ void radeon_engine_flush (struct radeonfb_info *rinfo)
-{
- int i;
-
- /* initiate flush */
- OUTREGP(RB2D_DSTCACHE_CTLSTAT, RB2D_DC_FLUSH_ALL,
- ~RB2D_DC_FLUSH_ALL);
-
- for (i=0; i < 2000000; i++) {
- if (!(INREG(RB2D_DSTCACHE_CTLSTAT) & RB2D_DC_BUSY))
- break;
- }
-}
-
-
-static __inline__ void _radeon_fifo_wait (struct radeonfb_info *rinfo, int entries)
-{
- int i;
-
- for (i=0; i<2000000; i++)
- if ((INREG(RBBM_STATUS) & 0x7f) >= entries)
- return;
-}
-
-
-static __inline__ void _radeon_engine_idle (struct radeonfb_info *rinfo)
-{
- int i;
-
- /* ensure FIFO is empty before waiting for idle */
- _radeon_fifo_wait (rinfo, 64);
-
- for (i=0; i<2000000; i++) {
- if (((INREG(RBBM_STATUS) & GUI_ACTIVE)) == 0) {
- radeon_engine_flush (rinfo);
- return;
- }
- }
-}
-
-
-#define radeon_engine_idle() _radeon_engine_idle(rinfo)
-#define radeon_fifo_wait(entries) _radeon_fifo_wait(rinfo,entries)
-
-
-
-/*
- * helper routines
- */
-
-static __inline__ u32 radeon_get_dstbpp(u16 depth)
-{
- switch (depth) {
- case 8:
- return DST_8BPP;
- case 15:
- return DST_15BPP;
- case 16:
- return DST_16BPP;
- case 32:
- return DST_32BPP;
- default:
- return 0;
- }
-}
-
-
-static inline int var_to_depth(const struct fb_var_screeninfo *var)
-{
- if (var->bits_per_pixel != 16)
- return var->bits_per_pixel;
- return (var->green.length == 6) ? 16 : 15;
-}
-
-
-static void _radeon_engine_reset(struct radeonfb_info *rinfo)
-{
- u32 clock_cntl_index, mclk_cntl, rbbm_soft_reset;
-
- radeon_engine_flush (rinfo);
-
- clock_cntl_index = INREG(CLOCK_CNTL_INDEX);
- mclk_cntl = INPLL(MCLK_CNTL);
-
- OUTPLL(MCLK_CNTL, (mclk_cntl |
- FORCEON_MCLKA |
- FORCEON_MCLKB |
- FORCEON_YCLKA |
- FORCEON_YCLKB |
- FORCEON_MC |
- FORCEON_AIC));
- rbbm_soft_reset = INREG(RBBM_SOFT_RESET);
-
- OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset |
- SOFT_RESET_CP |
- SOFT_RESET_HI |
- SOFT_RESET_SE |
- SOFT_RESET_RE |
- SOFT_RESET_PP |
- SOFT_RESET_E2 |
- SOFT_RESET_RB);
- INREG(RBBM_SOFT_RESET);
- OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset & (u32)
- ~(SOFT_RESET_CP |
- SOFT_RESET_HI |
- SOFT_RESET_SE |
- SOFT_RESET_RE |
- SOFT_RESET_PP |
- SOFT_RESET_E2 |
- SOFT_RESET_RB));
- INREG(RBBM_SOFT_RESET);
-
- OUTPLL(MCLK_CNTL, mclk_cntl);
- OUTREG(CLOCK_CNTL_INDEX, clock_cntl_index);
- OUTREG(RBBM_SOFT_RESET, rbbm_soft_reset);
-
- return;
-}
-
-#define radeon_engine_reset() _radeon_engine_reset(rinfo)
-
-
-static __inline__ int round_div(int num, int den)
-{
- return (num + (den / 2)) / den;
-}
-
-
-
-static __inline__ int min_bits_req(int val)
-{
- int bits_req = 0;
-
- if (val == 0)
- bits_req = 1;
-
- while (val) {
- val >>= 1;
- bits_req++;
- }
-
- return (bits_req);
-}
-
-
-static __inline__ int _max(int val1, int val2)
-{
- if (val1 >= val2)
- return val1;
- else
- return val2;
-}
-
-
-
-/*
- * globals
- */
-
-#ifndef MODULE
-static char *mode_option;
-#endif
-
-static char noaccel = 0;
-static char mirror = 0;
-static int panel_yres = 0;
-static char force_dfp = 0;
-static struct radeonfb_info *board_list = NULL;
-static char nomtrr = 0;
-
-/*
- * prototypes
- */
-
-static void radeon_save_state (struct radeonfb_info *rinfo,
- struct radeon_regs *save);
-static void radeon_engine_init (struct radeonfb_info *rinfo);
-static void radeon_write_mode (struct radeonfb_info *rinfo,
- struct radeon_regs *mode);
-static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo);
-static int __devinit radeon_init_disp (struct radeonfb_info *rinfo);
-static int radeon_init_disp_var (struct radeonfb_info *rinfo, struct fb_var_screeninfo *var);
-static void __iomem *radeon_find_rom(struct radeonfb_info *rinfo);
-static void radeon_get_pllinfo(struct radeonfb_info *rinfo, void __iomem *bios_seg);
-static void radeon_get_moninfo (struct radeonfb_info *rinfo);
-static int radeon_get_dfpinfo (struct radeonfb_info *rinfo);
-static int radeon_get_dfpinfo_BIOS(struct radeonfb_info *rinfo);
-static void radeon_get_EDID(struct radeonfb_info *rinfo);
-static int radeon_dfp_parse_EDID(struct radeonfb_info *rinfo);
-static void radeon_update_default_var(struct radeonfb_info *rinfo);
-
-#ifdef CONFIG_PPC_OF
-
-static int radeon_read_OF (struct radeonfb_info *rinfo);
-static int radeon_get_EDID_OF(struct radeonfb_info *rinfo);
-extern struct device_node *pci_device_to_OF_node(struct pci_dev *dev);
-
-#ifdef CONFIG_PMAC_PBOOK
-int radeon_sleep_notify(struct pmu_sleep_notifier *self, int when);
-static struct pmu_sleep_notifier radeon_sleep_notifier = {
- radeon_sleep_notify, SLEEP_LEVEL_VIDEO,
-};
-#endif /* CONFIG_PMAC_PBOOK */
-#ifdef CONFIG_PMAC_BACKLIGHT
-static int radeon_set_backlight_enable(int on, int level, void *data);
-static int radeon_set_backlight_level(int level, void *data);
-static struct backlight_controller radeon_backlight_controller = {
- radeon_set_backlight_enable,
- radeon_set_backlight_level
-};
-#endif /* CONFIG_PMAC_BACKLIGHT */
-
-#endif /* CONFIG_PPC_OF */
-
-
-static void __iomem *radeon_find_rom(struct radeonfb_info *rinfo)
-{
-#if defined(__i386__)
- u32 segstart;
- char __iomem *rom_base;
- char __iomem *rom;
- int stage;
- int i,j;
- char aty_rom_sig[] = "761295520";
- char *radeon_sig[] = {
- "RG6",
- "RADEON"
- };
-
- for(segstart=0x000c0000; segstart<0x000f0000; segstart+=0x00001000) {
-
- stage = 1;
-
- rom_base = ioremap(segstart, 0x1000);
-
- if ((*rom_base == 0x55) && (((*(rom_base + 1)) & 0xff) == 0xaa))
- stage = 2;
-
-
- if (stage != 2) {
- iounmap(rom_base);
- continue;
- }
-
- rom = rom_base;
-
- for (i = 0; (i < 128 - strlen(aty_rom_sig)) && (stage != 3); i++) {
- if (aty_rom_sig[0] == *rom)
- if (strncmp(aty_rom_sig, rom,
- strlen(aty_rom_sig)) == 0)
- stage = 3;
- rom++;
- }
- if (stage != 3) {
- iounmap(rom_base);
- continue;
- }
- rom = rom_base;
-
- for (i = 0; (i < 512) && (stage != 4); i++) {
- for (j = 0; j < ARRAY_SIZE(radeon_sig); j++) {
- if (radeon_sig[j][0] == *rom)
- if (strncmp(radeon_sig[j], rom,
- strlen(radeon_sig[j])) == 0) {
- stage = 4;
- break;
- }
- }
- rom++;
- }
- if (stage != 4) {
- iounmap(rom_base);
- continue;
- }
-
- return rom_base;
- }
-#endif
- return NULL;
-}
-
-
-
-
-static void radeon_get_pllinfo(struct radeonfb_info *rinfo, void __iomem *bios_seg)
-{
- void __iomem *bios_header;
- void __iomem *header_ptr;
- u16 bios_header_offset, pll_info_offset;
- PLL_BLOCK pll;
-
- if (bios_seg) {
- bios_header = bios_seg + 0x48L;
- header_ptr = bios_header;
-
- bios_header_offset = readw(header_ptr);
- bios_header = bios_seg + bios_header_offset;
- bios_header += 0x30;
-
- header_ptr = bios_header;
- pll_info_offset = readw(header_ptr);
- header_ptr = bios_seg + pll_info_offset;
-
- memcpy_fromio(&pll, header_ptr, 50);
-
- rinfo->pll.xclk = (u32)pll.XCLK;
- rinfo->pll.ref_clk = (u32)pll.PCLK_ref_freq;
- rinfo->pll.ref_div = (u32)pll.PCLK_ref_divider;
- rinfo->pll.ppll_min = pll.PCLK_min_freq;
- rinfo->pll.ppll_max = pll.PCLK_max_freq;
-
- printk("radeonfb: ref_clk=%d, ref_div=%d, xclk=%d from BIOS\n",
- rinfo->pll.ref_clk, rinfo->pll.ref_div, rinfo->pll.xclk);
- } else {
-#ifdef CONFIG_PPC_OF
- if (radeon_read_OF(rinfo)) {
- unsigned int tmp, Nx, M, ref_div, xclk;
-
- tmp = INPLL(M_SPLL_REF_FB_DIV);
- ref_div = INPLL(PPLL_REF_DIV) & 0x3ff;
-
- Nx = (tmp & 0xff00) >> 8;
- M = (tmp & 0xff);
- xclk = ((((2 * Nx * rinfo->pll.ref_clk) + (M)) /
- (2 * M)));
-
- rinfo->pll.xclk = xclk;
- rinfo->pll.ref_div = ref_div;
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.ppll_max = 35000;
-
- printk("radeonfb: ref_clk=%d, ref_div=%d, xclk=%d from OF\n",
- rinfo->pll.ref_clk, rinfo->pll.ref_div, rinfo->pll.xclk);
-
- return;
- }
-#endif
- /* no BIOS or BIOS not found, use defaults */
- switch (rinfo->chipset) {
- case PCI_DEVICE_ID_ATI_RADEON_QW:
- case PCI_DEVICE_ID_ATI_RADEON_QX:
- rinfo->pll.ppll_max = 35000;
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.xclk = 23000;
- rinfo->pll.ref_div = 12;
- rinfo->pll.ref_clk = 2700;
- break;
- case PCI_DEVICE_ID_ATI_RADEON_QL:
- case PCI_DEVICE_ID_ATI_RADEON_QN:
- case PCI_DEVICE_ID_ATI_RADEON_QO:
- case PCI_DEVICE_ID_ATI_RADEON_Ql:
- case PCI_DEVICE_ID_ATI_RADEON_BB:
- rinfo->pll.ppll_max = 35000;
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.xclk = 27500;
- rinfo->pll.ref_div = 12;
- rinfo->pll.ref_clk = 2700;
- break;
- case PCI_DEVICE_ID_ATI_RADEON_Id:
- case PCI_DEVICE_ID_ATI_RADEON_Ie:
- case PCI_DEVICE_ID_ATI_RADEON_If:
- case PCI_DEVICE_ID_ATI_RADEON_Ig:
- rinfo->pll.ppll_max = 35000;
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.xclk = 25000;
- rinfo->pll.ref_div = 12;
- rinfo->pll.ref_clk = 2700;
- break;
- case PCI_DEVICE_ID_ATI_RADEON_ND:
- case PCI_DEVICE_ID_ATI_RADEON_NE:
- case PCI_DEVICE_ID_ATI_RADEON_NF:
- case PCI_DEVICE_ID_ATI_RADEON_NG:
- rinfo->pll.ppll_max = 40000;
- rinfo->pll.ppll_min = 20000;
- rinfo->pll.xclk = 27000;
- rinfo->pll.ref_div = 12;
- rinfo->pll.ref_clk = 2700;
- break;
- case PCI_DEVICE_ID_ATI_RADEON_QD:
- case PCI_DEVICE_ID_ATI_RADEON_QE:
- case PCI_DEVICE_ID_ATI_RADEON_QF:
- case PCI_DEVICE_ID_ATI_RADEON_QG:
- default:
- rinfo->pll.ppll_max = 35000;
- rinfo->pll.ppll_min = 12000;
- rinfo->pll.xclk = 16600;
- rinfo->pll.ref_div = 67;
- rinfo->pll.ref_clk = 2700;
- break;
- }
-
- printk("radeonfb: ref_clk=%d, ref_div=%d, xclk=%d defaults\n",
- rinfo->pll.ref_clk, rinfo->pll.ref_div, rinfo->pll.xclk);
- }
-}
-
-
-static void radeon_get_moninfo (struct radeonfb_info *rinfo)
-{
- unsigned int tmp;
-
- if (force_dfp) {
- rinfo->dviDisp_type = MT_DFP;
- return;
- }
-
- tmp = INREG(BIOS_4_SCRATCH);
- printk(KERN_DEBUG "radeon_get_moninfo: bios 4 scratch = %x\n", tmp);
-
- if (rinfo->hasCRTC2) {
- /* primary DVI port */
- if (tmp & 0x08)
- rinfo->dviDisp_type = MT_DFP;
- else if (tmp & 0x4)
- rinfo->dviDisp_type = MT_LCD;
- else if (tmp & 0x200)
- rinfo->dviDisp_type = MT_CRT;
- else if (tmp & 0x10)
- rinfo->dviDisp_type = MT_CTV;
- else if (tmp & 0x20)
- rinfo->dviDisp_type = MT_STV;
-
- /* secondary CRT port */
- if (tmp & 0x2)
- rinfo->crtDisp_type = MT_CRT;
- else if (tmp & 0x800)
- rinfo->crtDisp_type = MT_DFP;
- else if (tmp & 0x400)
- rinfo->crtDisp_type = MT_LCD;
- else if (tmp & 0x1000)
- rinfo->crtDisp_type = MT_CTV;
- else if (tmp & 0x2000)
- rinfo->crtDisp_type = MT_STV;
- } else {
- rinfo->dviDisp_type = MT_NONE;
-
- tmp = INREG(FP_GEN_CNTL);
-
- if (tmp & FP_EN_TMDS)
- rinfo->crtDisp_type = MT_DFP;
- else
- rinfo->crtDisp_type = MT_CRT;
- }
-}
-
-
-
-static void radeon_get_EDID(struct radeonfb_info *rinfo)
-{
-#ifdef CONFIG_PPC_OF
- if (!radeon_get_EDID_OF(rinfo))
- RTRACE("radeonfb: could not retrieve EDID from OF\n");
-#else
- /* XXX use other methods later */
-#endif
-}
-
-
-#ifdef CONFIG_PPC_OF
-static int radeon_get_EDID_OF(struct radeonfb_info *rinfo)
-{
- struct device_node *dp;
- unsigned char *pedid = NULL;
- static char *propnames[] = { "DFP,EDID", "LCD,EDID", "EDID", "EDID1", NULL };
- int i;
-
- dp = pci_device_to_OF_node(rinfo->pdev);
- while (dp != NULL) {
- for (i = 0; propnames[i] != NULL; ++i) {
- pedid = (unsigned char *)
- get_property(dp, propnames[i], NULL);
- if (pedid != NULL) {
- rinfo->EDID = pedid;
- return 1;
- }
- }
- dp = dp->child;
- }
- return 0;
-}
-#endif /* CONFIG_PPC_OF */
-
-
-static int radeon_dfp_parse_EDID(struct radeonfb_info *rinfo)
-{
- unsigned char *block = rinfo->EDID;
-
- if (!block)
- return 0;
-
- /* jump to the detailed timing block section */
- block += 54;
-
- rinfo->clock = (block[0] + (block[1] << 8));
- rinfo->panel_xres = (block[2] + ((block[4] & 0xf0) << 4));
- rinfo->hblank = (block[3] + ((block[4] & 0x0f) << 8));
- rinfo->panel_yres = (block[5] + ((block[7] & 0xf0) << 4));
- rinfo->vblank = (block[6] + ((block[7] & 0x0f) << 8));
- rinfo->hOver_plus = (block[8] + ((block[11] & 0xc0) << 2));
- rinfo->hSync_width = (block[9] + ((block[11] & 0x30) << 4));
- rinfo->vOver_plus = ((block[10] >> 4) + ((block[11] & 0x0c) << 2));
- rinfo->vSync_width = ((block[10] & 0x0f) + ((block[11] & 0x03) << 4));
- rinfo->interlaced = ((block[17] & 0x80) >> 7);
- rinfo->synct = ((block[17] & 0x18) >> 3);
- rinfo->misc = ((block[17] & 0x06) >> 1);
- rinfo->hAct_high = rinfo->vAct_high = 0;
- if (rinfo->synct == 3) {
- if (rinfo->misc & 2)
- rinfo->hAct_high = 1;
- if (rinfo->misc & 1)
- rinfo->vAct_high = 1;
- }
-
- printk("radeonfb: detected DFP panel size from EDID: %dx%d\n",
- rinfo->panel_xres, rinfo->panel_yres);
-
- rinfo->got_dfpinfo = 1;
-
- return 1;
-}
-
-
-static void radeon_update_default_var(struct radeonfb_info *rinfo)
-{
- struct fb_var_screeninfo *var = &radeonfb_default_var;
-
- var->xres = rinfo->panel_xres;
- var->yres = rinfo->panel_yres;
- var->xres_virtual = rinfo->panel_xres;
- var->yres_virtual = rinfo->panel_yres;
- var->xoffset = var->yoffset = 0;
- var->bits_per_pixel = 8;
- var->pixclock = 100000000 / rinfo->clock;
- var->left_margin = (rinfo->hblank - rinfo->hOver_plus - rinfo->hSync_width);
- var->right_margin = rinfo->hOver_plus;
- var->upper_margin = (rinfo->vblank - rinfo->vOver_plus - rinfo->vSync_width);
- var->lower_margin = rinfo->vOver_plus;
- var->hsync_len = rinfo->hSync_width;
- var->vsync_len = rinfo->vSync_width;
- var->sync = 0;
- if (rinfo->synct == 3) {
- if (rinfo->hAct_high)
- var->sync |= FB_SYNC_HOR_HIGH_ACT;
- if (rinfo->vAct_high)
- var->sync |= FB_SYNC_VERT_HIGH_ACT;
- }
-
- var->vmode = 0;
- if (rinfo->interlaced)
- var->vmode |= FB_VMODE_INTERLACED;
-
- rinfo->use_default_var = 1;
-}
-
-
-static int radeon_get_dfpinfo_BIOS(struct radeonfb_info *rinfo)
-{
- char __iomem *fpbiosstart, *tmp, *tmp0;
- char stmp[30];
- int i;
-
- if (!rinfo->bios_seg)
- return 0;
-
- if (!(fpbiosstart = rinfo->bios_seg + readw(rinfo->bios_seg + 0x48))) {
- printk("radeonfb: Failed to detect DFP panel info using BIOS\n");
- return 0;
- }
-
- if (!(tmp = rinfo->bios_seg + readw(fpbiosstart + 0x40))) {
- printk("radeonfb: Failed to detect DFP panel info using BIOS\n");
- return 0;
- }
-
- for(i=0; i<24; i++)
- stmp[i] = readb(tmp+i+1);
- stmp[24] = 0;
- printk("radeonfb: panel ID string: %s\n", stmp);
- rinfo->panel_xres = readw(tmp + 25);
- rinfo->panel_yres = readw(tmp + 27);
- printk("radeonfb: detected DFP panel size from BIOS: %dx%d\n",
- rinfo->panel_xres, rinfo->panel_yres);
-
- for(i=0; i<32; i++) {
- tmp0 = rinfo->bios_seg + readw(tmp+64+i*2);
- if (tmp0 == 0)
- break;
- if ((readw(tmp0) == rinfo->panel_xres) &&
- (readw(tmp0+2) == rinfo->panel_yres)) {
- rinfo->hblank = (readw(tmp0+17) - readw(tmp0+19)) * 8;
- rinfo->hOver_plus = ((readw(tmp0+21) - readw(tmp0+19) -1) * 8) & 0x7fff;
- rinfo->hSync_width = readb(tmp0+23) * 8;
- rinfo->vblank = readw(tmp0+24) - readw(tmp0+26);
- rinfo->vOver_plus = (readw(tmp0+28) & 0x7ff) - readw(tmp0+26);
- rinfo->vSync_width = (readw(tmp0+28) & 0xf800) >> 11;
- rinfo->clock = readw(tmp0+9);
-
- rinfo->got_dfpinfo = 1;
- return 1;
- }
- }
-
- return 0;
-}
-
-
-
-static int radeon_get_dfpinfo (struct radeonfb_info *rinfo)
-{
- unsigned int tmp;
- unsigned short a, b;
-
- if (radeon_get_dfpinfo_BIOS(rinfo))
- radeon_update_default_var(rinfo);
-
- if (radeon_dfp_parse_EDID(rinfo))
- radeon_update_default_var(rinfo);
-
- if (!rinfo->got_dfpinfo) {
- /*
- * it seems all else has failed now and we
- * resort to probing registers for our DFP info
- */
- if (panel_yres) {
- rinfo->panel_yres = panel_yres;
- } else {
- tmp = INREG(FP_VERT_STRETCH);
- tmp &= 0x00fff000;
- rinfo->panel_yres = (unsigned short)(tmp >> 0x0c) + 1;
- }
-
- switch (rinfo->panel_yres) {
- case 480:
- rinfo->panel_xres = 640;
- break;
- case 600:
- rinfo->panel_xres = 800;
- break;
- case 768:
-#if defined(__powerpc__)
- if (rinfo->dviDisp_type == MT_LCD)
- rinfo->panel_xres = 1152;
- else
-#endif
- rinfo->panel_xres = 1024;
- break;
- case 1024:
- rinfo->panel_xres = 1280;
- break;
- case 1050:
- rinfo->panel_xres = 1400;
- break;
- case 1200:
- rinfo->panel_xres = 1600;
- break;
- default:
- printk("radeonfb: Failed to detect DFP panel size\n");
- return 0;
- }
-
- printk("radeonfb: detected DFP panel size from registers: %dx%d\n",
- rinfo->panel_xres, rinfo->panel_yres);
-
- tmp = INREG(FP_CRTC_H_TOTAL_DISP);
- a = (tmp & FP_CRTC_H_TOTAL_MASK) + 4;
- b = (tmp & 0x01ff0000) >> FP_CRTC_H_DISP_SHIFT;
- rinfo->hblank = (a - b + 1) * 8;
-
- tmp = INREG(FP_H_SYNC_STRT_WID);
- rinfo->hOver_plus = (unsigned short) ((tmp & FP_H_SYNC_STRT_CHAR_MASK) >>
- FP_H_SYNC_STRT_CHAR_SHIFT) - b - 1;
- rinfo->hOver_plus *= 8;
- rinfo->hSync_width = (unsigned short) ((tmp & FP_H_SYNC_WID_MASK) >>
- FP_H_SYNC_WID_SHIFT);
- rinfo->hSync_width *= 8;
- tmp = INREG(FP_CRTC_V_TOTAL_DISP);
- a = (tmp & FP_CRTC_V_TOTAL_MASK) + 1;
- b = (tmp & FP_CRTC_V_DISP_MASK) >> FP_CRTC_V_DISP_SHIFT;
- rinfo->vblank = a - b /* + 24 */ ;
-
- tmp = INREG(FP_V_SYNC_STRT_WID);
- rinfo->vOver_plus = (unsigned short) (tmp & FP_V_SYNC_STRT_MASK)
- - b + 1;
- rinfo->vSync_width = (unsigned short) ((tmp & FP_V_SYNC_WID_MASK) >>
- FP_V_SYNC_WID_SHIFT);
-
- return 1;
- }
-
- return 1;
-}
-
-
-#ifdef CONFIG_PPC_OF
-static int radeon_read_OF (struct radeonfb_info *rinfo)
-{
- struct device_node *dp;
- unsigned int *xtal;
-
- dp = pci_device_to_OF_node(rinfo->pdev);
-
- xtal = (unsigned int *) get_property(dp, "ATY,RefCLK", NULL);
-
- rinfo->pll.ref_clk = *xtal / 10;
-
- if (*xtal)
- return 1;
- else
- return 0;
-}
-#endif
-
-
-static void radeon_engine_init (struct radeonfb_info *rinfo)
-{
- u32 temp;
-
- /* disable 3D engine */
- OUTREG(RB3D_CNTL, 0);
-
- radeon_engine_reset ();
-
- radeon_fifo_wait (1);
- OUTREG(RB2D_DSTCACHE_MODE, 0);
-
- radeon_fifo_wait (1);
- temp = INREG(DEFAULT_PITCH_OFFSET);
- OUTREG(DEFAULT_PITCH_OFFSET, ((temp & 0xc0000000) |
- (rinfo->pitch << 0x16)));
-
- radeon_fifo_wait (1);
- OUTREGP(DP_DATATYPE, 0, ~HOST_BIG_ENDIAN_EN);
-
- radeon_fifo_wait (1);
- OUTREG(DEFAULT_SC_BOTTOM_RIGHT, (DEFAULT_SC_RIGHT_MAX |
- DEFAULT_SC_BOTTOM_MAX));
-
- temp = radeon_get_dstbpp(rinfo->depth);
- rinfo->dp_gui_master_cntl = ((temp << 8) | GMC_CLR_CMP_CNTL_DIS);
- radeon_fifo_wait (1);
- OUTREG(DP_GUI_MASTER_CNTL, (rinfo->dp_gui_master_cntl |
- GMC_BRUSH_SOLID_COLOR |
- GMC_SRC_DATATYPE_COLOR));
-
- radeon_fifo_wait (7);
-
- /* clear line drawing regs */
- OUTREG(DST_LINE_START, 0);
- OUTREG(DST_LINE_END, 0);
-
- /* set brush color regs */
- OUTREG(DP_BRUSH_FRGD_CLR, 0xffffffff);
- OUTREG(DP_BRUSH_BKGD_CLR, 0x00000000);
-
- /* set source color regs */
- OUTREG(DP_SRC_FRGD_CLR, 0xffffffff);
- OUTREG(DP_SRC_BKGD_CLR, 0x00000000);
-
- /* default write mask */
- OUTREG(DP_WRITE_MSK, 0xffffffff);
-
- radeon_engine_idle ();
-}
-
-
-static int __devinit radeon_init_disp (struct radeonfb_info *rinfo)
-{
- struct fb_info *info = &rinfo->info;
- struct fb_var_screeninfo var;
-
- var = radeonfb_default_var;
- if ((radeon_init_disp_var(rinfo, &var)) < 0)
- return -1;
-
- rinfo->depth = var_to_depth(&var);
- rinfo->bpp = var.bits_per_pixel;
-
- info->var = var;
- fb_alloc_cmap(&info->cmap, 256, 0);
-
- var.activate = FB_ACTIVATE_NOW;
- return 0;
-}
-
-
-static int radeon_init_disp_var (struct radeonfb_info *rinfo,
- struct fb_var_screeninfo *var)
-{
-#ifndef MODULE
- if (mode_option)
- fb_find_mode (var, &rinfo->info, mode_option,
- NULL, 0, NULL, 8);
- else
-#endif
- if (rinfo->use_default_var)
- /* We will use the modified default far */
- *var = radeonfb_default_var;
- else
-
- fb_find_mode (var, &rinfo->info, "640x480-8@60",
- NULL, 0, NULL, 0);
-
- if (noaccel)
- var->accel_flags &= ~FB_ACCELF_TEXT;
- else
- var->accel_flags |= FB_ACCELF_TEXT;
-
- return 0;
-}
-
-
-static int radeon_do_maximize(struct radeonfb_info *rinfo,
- struct fb_var_screeninfo *var,
- struct fb_var_screeninfo *v,
- int nom, int den)
-{
- static struct {
- int xres, yres;
- } modes[] = {
- {1600, 1280},
- {1280, 1024},
- {1024, 768},
- {800, 600},
- {640, 480},
- {-1, -1}
- };
- int i;
-
- /* use highest possible virtual resolution */
- if (v->xres_virtual == -1 && v->yres_virtual == -1) {
- printk("radeonfb: using max available virtual resolution\n");
- for (i=0; modes[i].xres != -1; i++) {
- if (modes[i].xres * nom / den * modes[i].yres <
- rinfo->video_ram / 2)
- break;
- }
- if (modes[i].xres == -1) {
- printk("radeonfb: could not find virtual resolution that fits into video memory!\n");
- return -EINVAL;
- }
- v->xres_virtual = modes[i].xres;
- v->yres_virtual = modes[i].yres;
-
- printk("radeonfb: virtual resolution set to max of %dx%d\n",
- v->xres_virtual, v->yres_virtual);
- } else if (v->xres_virtual == -1) {
- v->xres_virtual = (rinfo->video_ram * den /
- (nom * v->yres_virtual * 2)) & ~15;
- } else if (v->yres_virtual == -1) {
- v->xres_virtual = (v->xres_virtual + 15) & ~15;
- v->yres_virtual = rinfo->video_ram * den /
- (nom * v->xres_virtual *2);
- } else {
- if (v->xres_virtual * nom / den * v->yres_virtual >
- rinfo->video_ram) {
- return -EINVAL;
- }
- }
-
- if (v->xres_virtual * nom / den >= 8192) {
- v->xres_virtual = 8192 * den / nom - 16;
- }
-
- if (v->xres_virtual < v->xres)
- return -EINVAL;
-
- if (v->yres_virtual < v->yres)
- return -EINVAL;
-
- return 0;
-}
-
-
-static int radeonfb_check_var (struct fb_var_screeninfo *var, struct fb_info *info)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *) info->par;
- struct fb_var_screeninfo v;
- int nom, den;
-
- memcpy (&v, var, sizeof (v));
-
- switch (v.bits_per_pixel) {
- case 0 ... 8:
- v.bits_per_pixel = 8;
- break;
- case 9 ... 16:
- v.bits_per_pixel = 16;
- break;
- case 17 ... 24:
-#if 0 /* Doesn't seem to work */
- v.bits_per_pixel = 24;
- break;
-#endif
- return -EINVAL;
- case 25 ... 32:
- v.bits_per_pixel = 32;
- break;
- default:
- return -EINVAL;
- }
-
- switch (var_to_depth(&v)) {
- case 8:
- nom = den = 1;
- v.red.offset = v.green.offset = v.blue.offset = 0;
- v.red.length = v.green.length = v.blue.length = 8;
- v.transp.offset = v.transp.length = 0;
- break;
- case 15:
- nom = 2;
- den = 1;
- v.red.offset = 10;
- v.green.offset = 5;
- v.blue.offset = 0;
- v.red.length = v.green.length = v.blue.length = 5;
- v.transp.offset = v.transp.length = 0;
- break;
- case 16:
- nom = 2;
- den = 1;
- v.red.offset = 11;
- v.green.offset = 5;
- v.blue.offset = 0;
- v.red.length = 5;
- v.green.length = 6;
- v.blue.length = 5;
- v.transp.offset = v.transp.length = 0;
- break;
- case 24:
- nom = 4;
- den = 1;
- v.red.offset = 16;
- v.green.offset = 8;
- v.blue.offset = 0;
- v.red.length = v.blue.length = v.green.length = 8;
- v.transp.offset = v.transp.length = 0;
- break;
- case 32:
- nom = 4;
- den = 1;
- v.red.offset = 16;
- v.green.offset = 8;
- v.blue.offset = 0;
- v.red.length = v.blue.length = v.green.length = 8;
- v.transp.offset = 24;
- v.transp.length = 8;
- break;
- default:
- printk ("radeonfb: mode %dx%dx%d rejected, color depth invalid\n",
- var->xres, var->yres, var->bits_per_pixel);
- return -EINVAL;
- }
-
- if (radeon_do_maximize(rinfo, var, &v, nom, den) < 0)
- return -EINVAL;
-
- if (v.xoffset < 0)
- v.xoffset = 0;
- if (v.yoffset < 0)
- v.yoffset = 0;
-
- if (v.xoffset > v.xres_virtual - v.xres)
- v.xoffset = v.xres_virtual - v.xres - 1;
-
- if (v.yoffset > v.yres_virtual - v.yres)
- v.yoffset = v.yres_virtual - v.yres - 1;
-
- v.red.msb_right = v.green.msb_right = v.blue.msb_right =
- v.transp.offset = v.transp.length =
- v.transp.msb_right = 0;
-
- if (noaccel)
- v.accel_flags = 0;
-
- memcpy(var, &v, sizeof(v));
-
- return 0;
-}
-
-
-static int radeonfb_pan_display (struct fb_var_screeninfo *var,
- struct fb_info *info)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
-
- if ((var->xoffset + var->xres > var->xres_virtual)
- || (var->yoffset + var->yres > var->yres_virtual))
- return -EINVAL;
-
- if (rinfo->asleep)
- return 0;
-
- OUTREG(CRTC_OFFSET, ((var->yoffset * var->xres_virtual + var->xoffset)
- * var->bits_per_pixel / 8) & ~7);
- return 0;
-}
-
-
-static int radeonfb_ioctl (struct fb_info *info, unsigned int cmd,
- unsigned long arg)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
- unsigned int tmp;
- u32 value = 0;
- int rc;
-
- switch (cmd) {
- /*
- * TODO: set mirror accordingly for non-Mobility chipsets with 2 CRTC's
- */
- case FBIO_RADEON_SET_MIRROR:
- switch (rinfo->arch) {
- case RADEON_R100:
- case RADEON_RV100:
- case RADEON_R200:
- case RADEON_RV200:
- case RADEON_RV250:
- case RADEON_R300:
- return -EINVAL;
- default:
- /* RADEON M6, RADEON_M7, RADEON_M9 */
- break;
- }
-
- rc = get_user(value, (__u32 __user *)arg);
-
- if (rc)
- return rc;
-
- if (value & 0x01) {
- tmp = INREG(LVDS_GEN_CNTL);
-
- tmp |= (LVDS_ON | LVDS_BLON);
- } else {
- tmp = INREG(LVDS_GEN_CNTL);
-
- tmp &= ~(LVDS_ON | LVDS_BLON);
- }
-
- OUTREG(LVDS_GEN_CNTL, tmp);
-
- if (value & 0x02) {
- tmp = INREG(CRTC_EXT_CNTL);
- tmp |= CRTC_CRT_ON;
-
- mirror = 1;
- } else {
- tmp = INREG(CRTC_EXT_CNTL);
- tmp &= ~CRTC_CRT_ON;
-
- mirror = 0;
- }
-
- OUTREG(CRTC_EXT_CNTL, tmp);
-
- break;
- case FBIO_RADEON_GET_MIRROR:
- switch (rinfo->arch) {
- case RADEON_R100:
- case RADEON_RV100:
- case RADEON_R200:
- case RADEON_RV200:
- case RADEON_RV250:
- case RADEON_R300:
- return -EINVAL;
- default:
- /* RADEON M6, RADEON_M7, RADEON_M9 */
- break;
- }
-
- tmp = INREG(LVDS_GEN_CNTL);
- if ((LVDS_ON | LVDS_BLON) & tmp)
- value |= 0x01;
-
- tmp = INREG(CRTC_EXT_CNTL);
- if (CRTC_CRT_ON & tmp)
- value |= 0x02;
-
- return put_user(value, (__u32 __user *)arg);
- default:
- return -EINVAL;
- }
-
- return -EINVAL;
-}
-
-
-static int radeonfb_blank (int blank, struct fb_info *info)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
- u32 val = INREG(CRTC_EXT_CNTL);
- u32 val2 = INREG(LVDS_GEN_CNTL);
-
- if (rinfo->asleep)
- return 0;
-
-#ifdef CONFIG_PMAC_BACKLIGHT
- if (rinfo->dviDisp_type == MT_LCD && _machine == _MACH_Pmac) {
- set_backlight_enable(!blank);
- return 0;
- }
-#endif
-
- /* reset it */
- val &= ~(CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS |
- CRTC_VSYNC_DIS);
- val2 &= ~(LVDS_DISPLAY_DIS);
-
- switch (blank) {
- case FB_BLANK_UNBLANK:
- case FB_BLANK_NORMAL:
- break;
- case FB_BLANK_VSYNC_SUSPEND:
- val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS);
- break;
- case FB_BLANK_HSYNC_SUSPEND:
- val |= (CRTC_DISPLAY_DIS | CRTC_HSYNC_DIS);
- break;
- case FB_BLANK_POWERDOWN:
- val |= (CRTC_DISPLAY_DIS | CRTC_VSYNC_DIS |
- CRTC_HSYNC_DIS);
- val2 |= (LVDS_DISPLAY_DIS);
- break;
- }
-
- switch (rinfo->dviDisp_type) {
- case MT_LCD:
- OUTREG(LVDS_GEN_CNTL, val2);
- break;
- case MT_CRT:
- default:
- OUTREG(CRTC_EXT_CNTL, val);
- break;
- }
-
- /* let fbcon do a soft blank for us */
- return (blank == FB_BLANK_NORMAL) ? 1 : 0;
-}
-
-
-static int radeonfb_setcolreg (unsigned regno, unsigned red, unsigned green,
- unsigned blue, unsigned transp, struct fb_info *info)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *) info;
- u32 pindex, vclk_cntl;
- unsigned int i;
-
- if (regno > 255)
- return 1;
-
- red >>= 8;
- green >>= 8;
- blue >>= 8;
- rinfo->palette[regno].red = red;
- rinfo->palette[regno].green = green;
- rinfo->palette[regno].blue = blue;
-
- /* default */
- pindex = regno;
-
- if (!rinfo->asleep) {
- vclk_cntl = INPLL(VCLK_ECP_CNTL);
- OUTPLL(VCLK_ECP_CNTL, vclk_cntl & ~PIXCLK_DAC_ALWAYS_ONb);
-
- if (rinfo->bpp == 16) {
- pindex = regno * 8;
-
- if (rinfo->depth == 16 && regno > 63)
- return 1;
- if (rinfo->depth == 15 && regno > 31)
- return 1;
-
- /* For 565, the green component is mixed one order below */
- if (rinfo->depth == 16) {
- OUTREG(PALETTE_INDEX, pindex>>1);
- OUTREG(PALETTE_DATA, (rinfo->palette[regno>>1].red << 16) |
- (green << 8) | (rinfo->palette[regno>>1].blue));
- green = rinfo->palette[regno<<1].green;
- }
- }
-
- if (rinfo->depth != 16 || regno < 32) {
- OUTREG(PALETTE_INDEX, pindex);
- OUTREG(PALETTE_DATA, (red << 16) | (green << 8) | blue);
- }
-
- OUTPLL(VCLK_ECP_CNTL, vclk_cntl);
- }
- if (regno < 16) {
- switch (rinfo->depth) {
- case 15:
- ((u16 *) (info->pseudo_palette))[regno] =
- (regno << 10) | (regno << 5) | regno;
- break;
- case 16:
- ((u16 *) (info->pseudo_palette))[regno] =
- (regno << 11) | (regno << 6) | regno;
- break;
- case 24:
- ((u32 *) (info->pseudo_palette))[regno] =
- (regno << 16) | (regno << 8) | regno;
- break;
- case 32:
- i = (regno << 8) | regno;
- ((u32 *) (info->pseudo_palette))[regno] =
- (i << 16) | i;
- break;
- }
- }
- return 0;
-}
-
-
-
-static void radeon_save_state (struct radeonfb_info *rinfo,
- struct radeon_regs *save)
-{
- /* CRTC regs */
- save->crtc_gen_cntl = INREG(CRTC_GEN_CNTL);
- save->crtc_ext_cntl = INREG(CRTC_EXT_CNTL);
- save->dac_cntl = INREG(DAC_CNTL);
- save->crtc_h_total_disp = INREG(CRTC_H_TOTAL_DISP);
- save->crtc_h_sync_strt_wid = INREG(CRTC_H_SYNC_STRT_WID);
- save->crtc_v_total_disp = INREG(CRTC_V_TOTAL_DISP);
- save->crtc_v_sync_strt_wid = INREG(CRTC_V_SYNC_STRT_WID);
- save->crtc_pitch = INREG(CRTC_PITCH);
-#if defined(__BIG_ENDIAN)
- save->surface_cntl = INREG(SURFACE_CNTL);
-#endif
-
- /* FP regs */
- save->fp_crtc_h_total_disp = INREG(FP_CRTC_H_TOTAL_DISP);
- save->fp_crtc_v_total_disp = INREG(FP_CRTC_V_TOTAL_DISP);
- save->fp_gen_cntl = INREG(FP_GEN_CNTL);
- save->fp_h_sync_strt_wid = INREG(FP_H_SYNC_STRT_WID);
- save->fp_horz_stretch = INREG(FP_HORZ_STRETCH);
- save->fp_v_sync_strt_wid = INREG(FP_V_SYNC_STRT_WID);
- save->fp_vert_stretch = INREG(FP_VERT_STRETCH);
- save->lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
- save->lvds_pll_cntl = INREG(LVDS_PLL_CNTL);
- save->tmds_crc = INREG(TMDS_CRC);
- save->tmds_transmitter_cntl = INREG(TMDS_TRANSMITTER_CNTL);
- save->vclk_ecp_cntl = INPLL(VCLK_ECP_CNTL);
-}
-
-
-
-static int radeonfb_set_par (struct fb_info *info)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *)info->par;
- struct fb_var_screeninfo *mode = &info->var;
- struct radeon_regs newmode;
- int hTotal, vTotal, hSyncStart, hSyncEnd,
- hSyncPol, vSyncStart, vSyncEnd, vSyncPol, cSync;
- u8 hsync_adj_tab[] = {0, 0x12, 9, 9, 6, 5};
- u8 hsync_fudge_fp[] = {2, 2, 0, 0, 5, 5};
- u32 dotClock = 1000000000 / mode->pixclock,
- sync, h_sync_pol, v_sync_pol;
- int freq = dotClock / 10; /* x 100 */
- int xclk_freq, vclk_freq, xclk_per_trans, xclk_per_trans_precise;
- int useable_precision, roff, ron;
- int min_bits, format = 0;
- int hsync_start, hsync_fudge, bytpp, hsync_wid, vsync_wid;
- int primary_mon = PRIMARY_MONITOR(rinfo);
- int depth = var_to_depth(mode);
- int accel = (mode->accel_flags & FB_ACCELF_TEXT) != 0;
-
- rinfo->xres = mode->xres;
- rinfo->yres = mode->yres;
- rinfo->xres_virtual = mode->xres_virtual;
- rinfo->yres_virtual = mode->yres_virtual;
- rinfo->pixclock = mode->pixclock;
-
- hSyncStart = mode->xres + mode->right_margin;
- hSyncEnd = hSyncStart + mode->hsync_len;
- hTotal = hSyncEnd + mode->left_margin;
-
- vSyncStart = mode->yres + mode->lower_margin;
- vSyncEnd = vSyncStart + mode->vsync_len;
- vTotal = vSyncEnd + mode->upper_margin;
-
- if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
- if (rinfo->panel_xres < mode->xres)
- rinfo->xres = mode->xres = rinfo->panel_xres;
- if (rinfo->panel_yres < mode->yres)
- rinfo->yres = mode->yres = rinfo->panel_yres;
-
- hTotal = mode->xres + rinfo->hblank;
- hSyncStart = mode->xres + rinfo->hOver_plus;
- hSyncEnd = hSyncStart + rinfo->hSync_width;
-
- vTotal = mode->yres + rinfo->vblank;
- vSyncStart = mode->yres + rinfo->vOver_plus;
- vSyncEnd = vSyncStart + rinfo->vSync_width;
- }
-
- sync = mode->sync;
- h_sync_pol = sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
- v_sync_pol = sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
-
- RTRACE("hStart = %d, hEnd = %d, hTotal = %d\n",
- hSyncStart, hSyncEnd, hTotal);
- RTRACE("vStart = %d, vEnd = %d, vTotal = %d\n",
- vSyncStart, vSyncEnd, vTotal);
-
- hsync_wid = (hSyncEnd - hSyncStart) / 8;
- vsync_wid = vSyncEnd - vSyncStart;
- if (hsync_wid == 0)
- hsync_wid = 1;
- else if (hsync_wid > 0x3f) /* max */
- hsync_wid = 0x3f;
-
- if (vsync_wid == 0)
- vsync_wid = 1;
- else if (vsync_wid > 0x1f) /* max */
- vsync_wid = 0x1f;
-
- hSyncPol = mode->sync & FB_SYNC_HOR_HIGH_ACT ? 0 : 1;
- vSyncPol = mode->sync & FB_SYNC_VERT_HIGH_ACT ? 0 : 1;
-
- cSync = mode->sync & FB_SYNC_COMP_HIGH_ACT ? (1 << 4) : 0;
-
- format = radeon_get_dstbpp(depth);
- bytpp = mode->bits_per_pixel >> 3;
-
- if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD))
- hsync_fudge = hsync_fudge_fp[format-1];
- else
- hsync_fudge = hsync_adj_tab[format-1];
-
- hsync_start = hSyncStart - 8 + hsync_fudge;
-
- newmode.crtc_gen_cntl = CRTC_EXT_DISP_EN | CRTC_EN |
- (format << 8);
-
- if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
- newmode.crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN;
- if (mirror)
- newmode.crtc_ext_cntl |= CRTC_CRT_ON;
-
- newmode.crtc_gen_cntl &= ~(CRTC_DBL_SCAN_EN |
- CRTC_INTERLACE_EN);
- } else {
- newmode.crtc_ext_cntl = VGA_ATI_LINEAR | XCRT_CNT_EN |
- CRTC_CRT_ON;
- }
-
- newmode.dac_cntl = /* INREG(DAC_CNTL) | */ DAC_MASK_ALL | DAC_VGA_ADR_EN |
- DAC_8BIT_EN;
-
- newmode.crtc_h_total_disp = ((((hTotal / 8) - 1) & 0x3ff) |
- (((mode->xres / 8) - 1) << 16));
-
- newmode.crtc_h_sync_strt_wid = ((hsync_start & 0x1fff) |
- (hsync_wid << 16) | (h_sync_pol << 23));
-
- newmode.crtc_v_total_disp = ((vTotal - 1) & 0xffff) |
- ((mode->yres - 1) << 16);
-
- newmode.crtc_v_sync_strt_wid = (((vSyncStart - 1) & 0xfff) |
- (vsync_wid << 16) | (v_sync_pol << 23));
-
- if (accel) {
- /* We first calculate the engine pitch */
- rinfo->pitch = ((mode->xres_virtual * ((mode->bits_per_pixel + 1) / 8) + 0x3f)
- & ~(0x3f)) >> 6;
-
- /* Then, re-multiply it to get the CRTC pitch */
- newmode.crtc_pitch = (rinfo->pitch << 3) / ((mode->bits_per_pixel + 1) / 8);
- } else
- newmode.crtc_pitch = (mode->xres_virtual >> 3);
- newmode.crtc_pitch |= (newmode.crtc_pitch << 16);
-
-#if defined(__BIG_ENDIAN)
- /*
- * It looks like recent chips have a problem with SURFACE_CNTL,
- * setting SURF_TRANSLATION_DIS completely disables the
- * swapper as well, so we leave it unset now.
- */
- newmode.surface_cntl = 0;
-
- /* Setup swapping on both apertures, though we currently
- * only use aperture 0, enabling swapper on aperture 1
- * won't harm
- */
- switch (mode->bits_per_pixel) {
- case 16:
- newmode.surface_cntl |= NONSURF_AP0_SWP_16BPP;
- newmode.surface_cntl |= NONSURF_AP1_SWP_16BPP;
- break;
- case 24:
- case 32:
- newmode.surface_cntl |= NONSURF_AP0_SWP_32BPP;
- newmode.surface_cntl |= NONSURF_AP1_SWP_32BPP;
- break;
- }
-#endif
-
- rinfo->pitch = ((mode->xres_virtual * ((mode->bits_per_pixel + 1) / 8) + 0x3f)
- & ~(0x3f)) / 64;
-
- RTRACE("h_total_disp = 0x%x\t hsync_strt_wid = 0x%x\n",
- newmode.crtc_h_total_disp, newmode.crtc_h_sync_strt_wid);
- RTRACE("v_total_disp = 0x%x\t vsync_strt_wid = 0x%x\n",
- newmode.crtc_v_total_disp, newmode.crtc_v_sync_strt_wid);
-
- newmode.xres = mode->xres;
- newmode.yres = mode->yres;
-
- rinfo->bpp = mode->bits_per_pixel;
- rinfo->depth = depth;
-
- if (freq > rinfo->pll.ppll_max)
- freq = rinfo->pll.ppll_max;
- if (freq*12 < rinfo->pll.ppll_min)
- freq = rinfo->pll.ppll_min / 12;
-
- {
- struct {
- int divider;
- int bitvalue;
- } *post_div,
- post_divs[] = {
- { 1, 0 },
- { 2, 1 },
- { 4, 2 },
- { 8, 3 },
- { 3, 4 },
- { 16, 5 },
- { 6, 6 },
- { 12, 7 },
- { 0, 0 },
- };
-
- for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
- rinfo->pll_output_freq = post_div->divider * freq;
- if (rinfo->pll_output_freq >= rinfo->pll.ppll_min &&
- rinfo->pll_output_freq <= rinfo->pll.ppll_max)
- break;
- }
-
- rinfo->post_div = post_div->divider;
- rinfo->fb_div = round_div(rinfo->pll.ref_div*rinfo->pll_output_freq,
- rinfo->pll.ref_clk);
- newmode.ppll_ref_div = rinfo->pll.ref_div;
- newmode.ppll_div_3 = rinfo->fb_div | (post_div->bitvalue << 16);
- }
- newmode.vclk_ecp_cntl = rinfo->init_state.vclk_ecp_cntl;
-
-#ifdef CONFIG_PPC_OF
- /* Gross hack for iBook with M7 until I find out a proper fix */
- if (machine_is_compatible("PowerBook4,3") && rinfo->arch == RADEON_M7)
- newmode.ppll_div_3 = 0x000600ad;
-#endif /* CONFIG_PPC_OF */
-
- RTRACE("post div = 0x%x\n", rinfo->post_div);
- RTRACE("fb_div = 0x%x\n", rinfo->fb_div);
- RTRACE("ppll_div_3 = 0x%x\n", newmode.ppll_div_3);
-
- /* DDA */
- vclk_freq = round_div(rinfo->pll.ref_clk * rinfo->fb_div,
- rinfo->pll.ref_div * rinfo->post_div);
- xclk_freq = rinfo->pll.xclk;
-
- xclk_per_trans = round_div(xclk_freq * 128, vclk_freq * mode->bits_per_pixel);
-
- min_bits = min_bits_req(xclk_per_trans);
- useable_precision = min_bits + 1;
-
- xclk_per_trans_precise = round_div((xclk_freq * 128) << (11 - useable_precision),
- vclk_freq * mode->bits_per_pixel);
-
- ron = (4 * rinfo->ram.mb + 3 * _max(rinfo->ram.trcd - 2, 0) +
- 2 * rinfo->ram.trp + rinfo->ram.twr + rinfo->ram.cl + rinfo->ram.tr2w +
- xclk_per_trans) << (11 - useable_precision);
- roff = xclk_per_trans_precise * (32 - 4);
-
- RTRACE("ron = %d, roff = %d\n", ron, roff);
- RTRACE("vclk_freq = %d, per = %d\n", vclk_freq, xclk_per_trans_precise);
-
- if ((ron + rinfo->ram.rloop) >= roff) {
- printk("radeonfb: error ron out of range\n");
- return -EINVAL;
- }
-
- newmode.dda_config = (xclk_per_trans_precise |
- (useable_precision << 16) |
- (rinfo->ram.rloop << 20));
- newmode.dda_on_off = (ron << 16) | roff;
-
- if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
- unsigned int hRatio, vRatio;
-
- /* We force the pixel clock to be always enabled. Allowing it
- * to be power managed during blanking would save power, but has
- * nasty interactions with the 2D engine & sleep code that haven't
- * been solved yet. --BenH
- */
- newmode.vclk_ecp_cntl &= ~PIXCLK_DAC_ALWAYS_ONb;
-
- if (mode->xres > rinfo->panel_xres)
- mode->xres = rinfo->panel_xres;
- if (mode->yres > rinfo->panel_yres)
- mode->yres = rinfo->panel_yres;
-
- newmode.fp_horz_stretch = (((rinfo->panel_xres / 8) - 1)
- << HORZ_PANEL_SHIFT);
- newmode.fp_vert_stretch = ((rinfo->panel_yres - 1)
- << VERT_PANEL_SHIFT);
-
- if (mode->xres != rinfo->panel_xres) {
- hRatio = round_div(mode->xres * HORZ_STRETCH_RATIO_MAX,
- rinfo->panel_xres);
- newmode.fp_horz_stretch = (((((unsigned long)hRatio) & HORZ_STRETCH_RATIO_MASK)) |
- (newmode.fp_horz_stretch &
- (HORZ_PANEL_SIZE | HORZ_FP_LOOP_STRETCH |
- HORZ_AUTO_RATIO_INC)));
- newmode.fp_horz_stretch |= (HORZ_STRETCH_BLEND |
- HORZ_STRETCH_ENABLE);
- }
- newmode.fp_horz_stretch &= ~HORZ_AUTO_RATIO;
-
- if (mode->yres != rinfo->panel_yres) {
- vRatio = round_div(mode->yres * VERT_STRETCH_RATIO_MAX,
- rinfo->panel_yres);
- newmode.fp_vert_stretch = (((((unsigned long)vRatio) & VERT_STRETCH_RATIO_MASK)) |
- (newmode.fp_vert_stretch &
- (VERT_PANEL_SIZE | VERT_STRETCH_RESERVED)));
- newmode.fp_vert_stretch |= (VERT_STRETCH_BLEND |
- VERT_STRETCH_ENABLE);
- }
- newmode.fp_vert_stretch &= ~VERT_AUTO_RATIO_EN;
-
- newmode.fp_gen_cntl = (rinfo->init_state.fp_gen_cntl & (u32)
- ~(FP_SEL_CRTC2 |
- FP_RMX_HVSYNC_CONTROL_EN |
- FP_DFP_SYNC_SEL |
- FP_CRT_SYNC_SEL |
- FP_CRTC_LOCK_8DOT |
- FP_USE_SHADOW_EN |
- FP_CRTC_USE_SHADOW_VEND |
- FP_CRT_SYNC_ALT));
-
- newmode.fp_gen_cntl |= (FP_CRTC_DONT_SHADOW_VPAR |
- FP_CRTC_DONT_SHADOW_HEND);
-
- newmode.lvds_gen_cntl = rinfo->init_state.lvds_gen_cntl;
- newmode.lvds_pll_cntl = rinfo->init_state.lvds_pll_cntl;
- newmode.tmds_crc = rinfo->init_state.tmds_crc;
- newmode.tmds_transmitter_cntl = rinfo->init_state.tmds_transmitter_cntl;
-
- if (primary_mon == MT_LCD) {
- newmode.lvds_gen_cntl |= (LVDS_ON | LVDS_BLON);
- newmode.fp_gen_cntl &= ~(FP_FPON | FP_TMDS_EN);
- } else {
- /* DFP */
- newmode.fp_gen_cntl |= (FP_FPON | FP_TMDS_EN);
- newmode.tmds_transmitter_cntl = (TMDS_RAN_PAT_RST |
- TMDS_ICHCSEL | TMDS_PLL_EN) &
- ~(TMDS_PLLRST);
- newmode.crtc_ext_cntl &= ~CRTC_CRT_ON;
- }
-
- newmode.fp_crtc_h_total_disp = (((rinfo->hblank / 8) & 0x3ff) |
- (((mode->xres / 8) - 1) << 16));
- newmode.fp_crtc_v_total_disp = (rinfo->vblank & 0xffff) |
- ((mode->yres - 1) << 16);
- newmode.fp_h_sync_strt_wid = ((rinfo->hOver_plus & 0x1fff) |
- (hsync_wid << 16) | (h_sync_pol << 23));
- newmode.fp_v_sync_strt_wid = ((rinfo->vOver_plus & 0xfff) |
- (vsync_wid << 16) | (v_sync_pol << 23));
- }
-
- /* do it! */
- if (!rinfo->asleep) {
- radeon_write_mode (rinfo, &newmode);
- /* (re)initialize the engine */
- if (noaccel)
- radeon_engine_init (rinfo);
-
- }
- /* Update fix */
- if (accel)
- info->fix.line_length = rinfo->pitch*64;
- else
- info->fix.line_length = mode->xres_virtual * ((mode->bits_per_pixel + 1) / 8);
- info->fix.visual = rinfo->depth == 8 ? FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
-
-#ifdef CONFIG_BOOTX_TEXT
- /* Update debug text engine */
- btext_update_display(rinfo->fb_base_phys, mode->xres, mode->yres,
- rinfo->depth, info->fix.line_length);
-#endif
-
- return 0;
-}
-
-
-static void radeon_write_mode (struct radeonfb_info *rinfo,
- struct radeon_regs *mode)
-{
- int i;
- int primary_mon = PRIMARY_MONITOR(rinfo);
-
- radeonfb_blank(VESA_POWERDOWN, (struct fb_info *)rinfo);
-
-
- if (rinfo->arch == RADEON_M6) {
- for (i=0; i<7; i++)
- OUTREG(common_regs_m6[i].reg, common_regs_m6[i].val);
- } else {
- for (i=0; i<9; i++)
- OUTREG(common_regs[i].reg, common_regs[i].val);
- }
-
- OUTREG(CRTC_GEN_CNTL, mode->crtc_gen_cntl);
- OUTREGP(CRTC_EXT_CNTL, mode->crtc_ext_cntl,
- CRTC_HSYNC_DIS | CRTC_VSYNC_DIS | CRTC_DISPLAY_DIS);
- OUTREGP(DAC_CNTL, mode->dac_cntl, DAC_RANGE_CNTL | DAC_BLANKING);
- OUTREG(CRTC_H_TOTAL_DISP, mode->crtc_h_total_disp);
- OUTREG(CRTC_H_SYNC_STRT_WID, mode->crtc_h_sync_strt_wid);
- OUTREG(CRTC_V_TOTAL_DISP, mode->crtc_v_total_disp);
- OUTREG(CRTC_V_SYNC_STRT_WID, mode->crtc_v_sync_strt_wid);
- OUTREG(CRTC_OFFSET, 0);
- OUTREG(CRTC_OFFSET_CNTL, 0);
- OUTREG(CRTC_PITCH, mode->crtc_pitch);
-
-#if defined(__BIG_ENDIAN)
- OUTREG(SURFACE_CNTL, mode->surface_cntl);
-#endif
-
- while ((INREG(CLOCK_CNTL_INDEX) & PPLL_DIV_SEL_MASK) !=
- PPLL_DIV_SEL_MASK) {
- OUTREGP(CLOCK_CNTL_INDEX, PPLL_DIV_SEL_MASK, 0xffff);
- }
-
- OUTPLLP(PPLL_CNTL, PPLL_RESET, 0xffff);
-
- while ((INPLL(PPLL_REF_DIV) & PPLL_REF_DIV_MASK) !=
- (mode->ppll_ref_div & PPLL_REF_DIV_MASK)) {
- OUTPLLP(PPLL_REF_DIV, mode->ppll_ref_div, ~PPLL_REF_DIV_MASK);
- }
-
- while ((INPLL(PPLL_DIV_3) & PPLL_FB3_DIV_MASK) !=
- (mode->ppll_div_3 & PPLL_FB3_DIV_MASK)) {
- OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_FB3_DIV_MASK);
- }
-
- while ((INPLL(PPLL_DIV_3) & PPLL_POST3_DIV_MASK) !=
- (mode->ppll_div_3 & PPLL_POST3_DIV_MASK)) {
- OUTPLLP(PPLL_DIV_3, mode->ppll_div_3, ~PPLL_POST3_DIV_MASK);
- }
-
- OUTPLL(HTOTAL_CNTL, 0);
-
- OUTPLLP(PPLL_CNTL, 0, ~PPLL_RESET);
-
-// OUTREG(DDA_CONFIG, mode->dda_config);
-// OUTREG(DDA_ON_OFF, mode->dda_on_off);
-
- if ((primary_mon == MT_DFP) || (primary_mon == MT_LCD)) {
- OUTREG(FP_CRTC_H_TOTAL_DISP, mode->fp_crtc_h_total_disp);
- OUTREG(FP_CRTC_V_TOTAL_DISP, mode->fp_crtc_v_total_disp);
- OUTREG(FP_H_SYNC_STRT_WID, mode->fp_h_sync_strt_wid);
- OUTREG(FP_V_SYNC_STRT_WID, mode->fp_v_sync_strt_wid);
- OUTREG(FP_HORZ_STRETCH, mode->fp_horz_stretch);
- OUTREG(FP_VERT_STRETCH, mode->fp_vert_stretch);
- OUTREG(FP_GEN_CNTL, mode->fp_gen_cntl);
- OUTREG(TMDS_CRC, mode->tmds_crc);
- OUTREG(TMDS_TRANSMITTER_CNTL, mode->tmds_transmitter_cntl);
-
- if (primary_mon == MT_LCD) {
- unsigned int tmp = INREG(LVDS_GEN_CNTL);
-
- mode->lvds_gen_cntl &= ~LVDS_STATE_MASK;
- mode->lvds_gen_cntl |= (rinfo->init_state.lvds_gen_cntl & LVDS_STATE_MASK);
-
- if ((tmp & (LVDS_ON | LVDS_BLON)) ==
- (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON))) {
- OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
- } else {
- if (mode->lvds_gen_cntl & (LVDS_ON | LVDS_BLON)) {
- udelay(1000);
- OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
- } else {
- OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl |
- LVDS_BLON);
- udelay(1000);
- OUTREG(LVDS_GEN_CNTL, mode->lvds_gen_cntl);
- }
- }
- }
- }
-
- radeonfb_blank(VESA_NO_BLANKING, (struct fb_info *)rinfo);
-
- OUTPLL(VCLK_ECP_CNTL, mode->vclk_ecp_cntl);
-
- return;
-}
-
-static struct fb_ops radeonfb_ops = {
- .owner = THIS_MODULE,
- .fb_check_var = radeonfb_check_var,
- .fb_set_par = radeonfb_set_par,
- .fb_setcolreg = radeonfb_setcolreg,
- .fb_pan_display = radeonfb_pan_display,
- .fb_blank = radeonfb_blank,
- .fb_ioctl = radeonfb_ioctl,
-#if 0
- .fb_fillrect = radeonfb_fillrect,
- .fb_copyarea = radeonfb_copyarea,
- .fb_imageblit = radeonfb_imageblit,
- .fb_rasterimg = radeonfb_rasterimg,
-#else
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
-#endif
-};
-
-
-static int __devinit radeon_set_fbinfo (struct radeonfb_info *rinfo)
-{
- struct fb_info *info;
-
- info = &rinfo->info;
-
- info->par = rinfo;
- info->pseudo_palette = rinfo->pseudo_palette;
- info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN;
- info->fbops = &radeonfb_ops;
- info->screen_base = rinfo->fb_base;
-
- /* Fill fix common fields */
- strlcpy(info->fix.id, rinfo->name, sizeof(info->fix.id));
- info->fix.smem_start = rinfo->fb_base_phys;
- info->fix.smem_len = rinfo->video_ram;
- info->fix.type = FB_TYPE_PACKED_PIXELS;
- info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
- info->fix.xpanstep = 8;
- info->fix.ypanstep = 1;
- info->fix.ywrapstep = 0;
- info->fix.type_aux = 0;
- info->fix.mmio_start = rinfo->mmio_base_phys;
- info->fix.mmio_len = RADEON_REGSIZE;
- if (noaccel)
- info->fix.accel = FB_ACCEL_NONE;
- else
- info->fix.accel = FB_ACCEL_ATI_RADEON;
-
- if (radeon_init_disp (rinfo) < 0)
- return -1;
-
- return 0;
-}
-
-
-#ifdef CONFIG_PMAC_BACKLIGHT
-
-/* TODO: Dbl check these tables, we don't go up to full ON backlight
- * in these, possibly because we noticed MacOS doesn't, but I'd prefer
- * having some more official numbers from ATI
- */
-static int backlight_conv_m6[] = {
- 0xff, 0xc0, 0xb5, 0xaa, 0x9f, 0x94, 0x89, 0x7e,
- 0x73, 0x68, 0x5d, 0x52, 0x47, 0x3c, 0x31, 0x24
-};
-static int backlight_conv_m7[] = {
- 0x00, 0x3f, 0x4a, 0x55, 0x60, 0x6b, 0x76, 0x81,
- 0x8c, 0x97, 0xa2, 0xad, 0xb8, 0xc3, 0xce, 0xd9
-};
-
-#define BACKLIGHT_LVDS_OFF
-#undef BACKLIGHT_DAC_OFF
-
-/* We turn off the LCD completely instead of just dimming the backlight.
- * This provides some greater power saving and the display is useless
- * without backlight anyway.
- */
-
-static int radeon_set_backlight_enable(int on, int level, void *data)
-{
- struct radeonfb_info *rinfo = (struct radeonfb_info *)data;
- unsigned int lvds_gen_cntl = INREG(LVDS_GEN_CNTL);
- int* conv_table;
-
- /* Pardon me for that hack... maybe some day we can figure
- * out in what direction backlight should work on a given
- * panel ?
- */
- if ((rinfo->arch == RADEON_M7 || rinfo->arch == RADEON_M9)
- && !machine_is_compatible("PowerBook4,3"))
- conv_table = backlight_conv_m7;
- else
- conv_table = backlight_conv_m6;
-
- lvds_gen_cntl |= (LVDS_BL_MOD_EN | LVDS_BLON);
- if (on && (level > BACKLIGHT_OFF)) {
- lvds_gen_cntl |= LVDS_DIGON;
- if (!(lvds_gen_cntl & LVDS_ON)) {
- lvds_gen_cntl &= ~LVDS_BLON;
- OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- (void)INREG(LVDS_GEN_CNTL);
- mdelay(10);
- lvds_gen_cntl |= LVDS_BLON;
- OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- }
- lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
- lvds_gen_cntl |= (conv_table[level] <<
- LVDS_BL_MOD_LEVEL_SHIFT);
- lvds_gen_cntl |= (LVDS_ON | LVDS_EN);
- lvds_gen_cntl &= ~LVDS_DISPLAY_DIS;
- } else {
- lvds_gen_cntl &= ~LVDS_BL_MOD_LEVEL_MASK;
- lvds_gen_cntl |= (conv_table[0] <<
- LVDS_BL_MOD_LEVEL_SHIFT);
- lvds_gen_cntl |= LVDS_DISPLAY_DIS;
- OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- udelay(10);
- lvds_gen_cntl &= ~(LVDS_ON | LVDS_EN | LVDS_BLON | LVDS_DIGON);
- }
-
- OUTREG(LVDS_GEN_CNTL, lvds_gen_cntl);
- rinfo->init_state.lvds_gen_cntl &= ~LVDS_STATE_MASK;
- rinfo->init_state.lvds_gen_cntl |= (lvds_gen_cntl & LVDS_STATE_MASK);
-
- return 0;
-}
-
-static int radeon_set_backlight_level(int level, void *data)
-{
- return radeon_set_backlight_enable(1, level, data);
-}
-#endif /* CONFIG_PMAC_BACKLIGHT */
-
-
-#ifdef CONFIG_PMAC_PBOOK
-
-static u32 dbg_clk;
-
-/*
- * Radeon M6 Power Management code. This code currently only supports
- * the mobile chips, it's based from some informations provided by ATI
- * along with hours of tracing of MacOS drivers
- */
-
-static void radeon_pm_save_regs(struct radeonfb_info *rinfo)
-{
- rinfo->save_regs[0] = INPLL(PLL_PWRMGT_CNTL);
- rinfo->save_regs[1] = INPLL(CLK_PWRMGT_CNTL);
- rinfo->save_regs[2] = INPLL(MCLK_CNTL);
- rinfo->save_regs[3] = INPLL(SCLK_CNTL);
- rinfo->save_regs[4] = INPLL(CLK_PIN_CNTL);
- rinfo->save_regs[5] = INPLL(VCLK_ECP_CNTL);
- rinfo->save_regs[6] = INPLL(PIXCLKS_CNTL);
- rinfo->save_regs[7] = INPLL(MCLK_MISC);
- rinfo->save_regs[8] = INPLL(P2PLL_CNTL);
-
- rinfo->save_regs[9] = INREG(DISP_MISC_CNTL);
- rinfo->save_regs[10] = INREG(DISP_PWR_MAN);
- rinfo->save_regs[11] = INREG(LVDS_GEN_CNTL);
- rinfo->save_regs[12] = INREG(LVDS_PLL_CNTL);
- rinfo->save_regs[13] = INREG(TV_DAC_CNTL);
- rinfo->save_regs[14] = INREG(BUS_CNTL1);
- rinfo->save_regs[15] = INREG(CRTC_OFFSET_CNTL);
- rinfo->save_regs[16] = INREG(AGP_CNTL);
- rinfo->save_regs[17] = (INREG(CRTC_GEN_CNTL) & 0xfdffffff) | 0x04000000;
- rinfo->save_regs[18] = (INREG(CRTC2_GEN_CNTL) & 0xfdffffff) | 0x04000000;
- rinfo->save_regs[19] = INREG(GPIOPAD_A);
- rinfo->save_regs[20] = INREG(GPIOPAD_EN);
- rinfo->save_regs[21] = INREG(GPIOPAD_MASK);
- rinfo->save_regs[22] = INREG(ZV_LCDPAD_A);
- rinfo->save_regs[23] = INREG(ZV_LCDPAD_EN);
- rinfo->save_regs[24] = INREG(ZV_LCDPAD_MASK);
- rinfo->save_regs[25] = INREG(GPIO_VGA_DDC);
- rinfo->save_regs[26] = INREG(GPIO_DVI_DDC);
- rinfo->save_regs[27] = INREG(GPIO_MONID);
- rinfo->save_regs[28] = INREG(GPIO_CRT2_DDC);
-
- rinfo->save_regs[29] = INREG(SURFACE_CNTL);
- rinfo->save_regs[30] = INREG(MC_FB_LOCATION);
- rinfo->save_regs[31] = INREG(DISPLAY_BASE_ADDR);
- rinfo->save_regs[32] = INREG(MC_AGP_LOCATION);
- rinfo->save_regs[33] = INREG(CRTC2_DISPLAY_BASE_ADDR);
-}
-
-static void radeon_pm_restore_regs(struct radeonfb_info *rinfo)
-{
- OUTPLL(P2PLL_CNTL, rinfo->save_regs[8] & 0xFFFFFFFE); /* First */
-
- OUTPLL(PLL_PWRMGT_CNTL, rinfo->save_regs[0]);
- OUTPLL(CLK_PWRMGT_CNTL, rinfo->save_regs[1]);
- OUTPLL(MCLK_CNTL, rinfo->save_regs[2]);
- OUTPLL(SCLK_CNTL, rinfo->save_regs[3]);
- OUTPLL(CLK_PIN_CNTL, rinfo->save_regs[4]);
- OUTPLL(VCLK_ECP_CNTL, rinfo->save_regs[5]);
- OUTPLL(PIXCLKS_CNTL, rinfo->save_regs[6]);
- OUTPLL(MCLK_MISC, rinfo->save_regs[7]);
-
- OUTREG(DISP_MISC_CNTL, rinfo->save_regs[9]);
- OUTREG(DISP_PWR_MAN, rinfo->save_regs[10]);
- OUTREG(LVDS_GEN_CNTL, rinfo->save_regs[11]);
- OUTREG(LVDS_PLL_CNTL,rinfo->save_regs[12]);
- OUTREG(TV_DAC_CNTL, rinfo->save_regs[13]);
- OUTREG(BUS_CNTL1, rinfo->save_regs[14]);
- OUTREG(CRTC_OFFSET_CNTL, rinfo->save_regs[15]);
- OUTREG(AGP_CNTL, rinfo->save_regs[16]);
- OUTREG(CRTC_GEN_CNTL, rinfo->save_regs[17]);
- OUTREG(CRTC2_GEN_CNTL, rinfo->save_regs[18]);
-
- // wait VBL before that one ?
- OUTPLL(P2PLL_CNTL, rinfo->save_regs[8]);
-
- OUTREG(GPIOPAD_A, rinfo->save_regs[19]);
- OUTREG(GPIOPAD_EN, rinfo->save_regs[20]);
- OUTREG(GPIOPAD_MASK, rinfo->save_regs[21]);
- OUTREG(ZV_LCDPAD_A, rinfo->save_regs[22]);
- OUTREG(ZV_LCDPAD_EN, rinfo->save_regs[23]);
- OUTREG(ZV_LCDPAD_MASK, rinfo->save_regs[24]);
- OUTREG(GPIO_VGA_DDC, rinfo->save_regs[25]);
- OUTREG(GPIO_DVI_DDC, rinfo->save_regs[26]);
- OUTREG(GPIO_MONID, rinfo->save_regs[27]);
- OUTREG(GPIO_CRT2_DDC, rinfo->save_regs[28]);
-}
-
-static void radeon_pm_disable_iopad(struct radeonfb_info *rinfo)
-{
- OUTREG(GPIOPAD_MASK, 0x0001ffff);
- OUTREG(GPIOPAD_EN, 0x00000400);
- OUTREG(GPIOPAD_A, 0x00000000);
- OUTREG(ZV_LCDPAD_MASK, 0x00000000);
- OUTREG(ZV_LCDPAD_EN, 0x00000000);
- OUTREG(ZV_LCDPAD_A, 0x00000000);
- OUTREG(GPIO_VGA_DDC, 0x00030000);
- OUTREG(GPIO_DVI_DDC, 0x00000000);
- OUTREG(GPIO_MONID, 0x00030000);
- OUTREG(GPIO_CRT2_DDC, 0x00000000);
-}
-
-static void radeon_pm_program_v2clk(struct radeonfb_info *rinfo)
-{
-//
-// u32 reg;
-//
-// OUTPLL(P2PLL_REF_DIV, 0x0c);
-//
-// .../... figure out what macos does here
-}
-
-static void radeon_pm_low_current(struct radeonfb_info *rinfo)
-{
- u32 reg;
-
- reg = INREG(BUS_CNTL1);
- reg &= ~BUS_CNTL1_MOBILE_PLATFORM_SEL_MASK;
- reg |= BUS_CNTL1_AGPCLK_VALID | (1<<BUS_CNTL1_MOBILE_PLATFORM_SEL_SHIFT);
- OUTREG(BUS_CNTL1, reg);
-
- reg = INPLL(PLL_PWRMGT_CNTL);
- reg |= PLL_PWRMGT_CNTL_SPLL_TURNOFF | PLL_PWRMGT_CNTL_PPLL_TURNOFF |
- PLL_PWRMGT_CNTL_P2PLL_TURNOFF | PLL_PWRMGT_CNTL_TVPLL_TURNOFF;
- reg &= ~PLL_PWRMGT_CNTL_SU_MCLK_USE_BCLK;
- reg &= ~PLL_PWRMGT_CNTL_MOBILE_SU;
- OUTPLL(PLL_PWRMGT_CNTL, reg);
-
-// reg = INPLL(TV_PLL_CNTL1);
-// reg |= TV_PLL_CNTL1__TVPLL_RESET | TV_PLL_CNTL1__TVPLL_SLEEP;
-// OUTPLL(TV_PLL_CNTL1, reg);
-
- reg = INREG(TV_DAC_CNTL);
- reg &= ~(TV_DAC_CNTL_BGADJ_MASK |TV_DAC_CNTL_DACADJ_MASK);
- reg |=TV_DAC_CNTL_BGSLEEP | TV_DAC_CNTL_RDACPD | TV_DAC_CNTL_GDACPD |
- TV_DAC_CNTL_BDACPD |
- (8<<TV_DAC_CNTL_BGADJ__SHIFT) | (8<<TV_DAC_CNTL_DACADJ__SHIFT);
- OUTREG(TV_DAC_CNTL, reg);
-
- reg = INREG(TMDS_TRANSMITTER_CNTL);
- reg &= ~(TMDS_PLL_EN |TMDS_PLLRST);
- OUTREG(TMDS_TRANSMITTER_CNTL, reg);
-
-// lvds_pll_cntl = regr32(g, LVDS_PLL_CNTL);
-// lvds_pll_cntl &= ~LVDS_PLL_CNTL__LVDS_PLL_EN;
-// lvds_pll_cntl |= LVDS_PLL_CNTL__LVDS_PLL_RESET;
-// regw32(g, LVDS_PLL_CNTL, lvds_pll_cntl);
-
- reg = INREG(DAC_CNTL);
- reg &= ~DAC_CMP_EN;
- OUTREG(DAC_CNTL, reg);
-
- reg = INREG(DAC_CNTL2);
- reg &= ~DAC2_CMP_EN;
- OUTREG(DAC_CNTL2, reg);
-
- reg = INREG(TV_DAC_CNTL);
- reg &= ~TV_DAC_CNTL_DETECT;
- OUTREG(TV_DAC_CNTL, reg);
-}
-
-static void radeon_pm_setup_for_suspend(struct radeonfb_info *rinfo)
-{
- /* This code is disabled. It does what is in the pm_init
- * function of the MacOS driver code ATI sent me. However,
- * it doesn't fix my sleep problem, and is causing other issues
- * on wakeup (bascially the machine dying when switching consoles
- * I haven't had time to investigate this yet
- */
-#if 0
- u32 disp_misc_cntl;
- u32 disp_pwr_man;
- u32 temp;
-
- // set SPLL, MPLL, PPLL, P2PLL, TVPLL, SCLK, MCLK, PCLK, P2CLK,
- // TCLK and TEST_MODE to 0
- temp = INPLL(CLK_PWRMGT_CNTL);
- OUTPLL(CLK_PWRMGT_CNTL , temp & ~0xc00002ff);
-
- // Turn on Power Management
- temp = INPLL(CLK_PWRMGT_CNTL);
- OUTPLL(CLK_PWRMGT_CNTL , temp | 0x00000400);
-
- // Turn off display clock if using mobile chips
- temp = INPLL(CLK_PWRMGT_CNTL);
- OUTREG(CLK_PWRMGT_CNTL , temp | 0x00100000);
-
- // Force PIXCLK_ALWAYS_ON and PIXCLK_DAC_ALWAYS_ON
- temp = INPLL(VCLK_ECP_CNTL);
- OUTPLL(VCLK_ECP_CNTL, temp & ~0x000000c0);
-
- // Force ECP_FORCE_ON to 1
- temp = INPLL(VCLK_ECP_CNTL);
- OUTPLL(VCLK_ECP_CNTL, temp | 0x00040000);
-
- // Force PIXCLK_BLEND_ALWAYS_ON and PIXCLK_GV_ALWAYS_ON
- temp = INPLL(PIXCLKS_CNTL);
- OUTPLL(PIXCLKS_CNTL, temp & ~0x00001800);
-
- // Forcing SCLK_CNTL to ON
- OUTPLL(SCLK_CNTL, (INPLL(SCLK_CNTL)& 0x00000007) | 0xffff8000 );
-
- // Set PM control over XTALIN pad
- temp = INPLL(CLK_PIN_CNTL);
- OUTPLL(CLK_PIN_CNTL, temp | 0x00080000);
-
- // Force MCLK and YCLK and MC as dynamic
- temp = INPLL(MCLK_CNTL);
- OUTPLL(MCLK_CNTL, temp & 0xffeaffff);
-
- // PLL_TURNOFF
- temp = INPLL(PLL_PWRMGT_CNTL);
- OUTPLL(PLL_PWRMGT_CNTL, temp | 0x0000001f);
-
- // set MOBILE_SU to 1 if M6 or DDR64 is detected
- temp = INPLL(PLL_PWRMGT_CNTL);
- OUTPLL(PLL_PWRMGT_CNTL, temp | 0x00010000);
-
- // select PM access mode (PM_MODE_SEL) (use ACPI mode)
-// temp = INPLL(PLL_PWRMGT_CNTL);
-// OUTPLL(PLL_PWRMGT_CNTL, temp | 0x00002000);
- temp = INPLL(PLL_PWRMGT_CNTL);
- OUTPLL(PLL_PWRMGT_CNTL, temp & ~0x00002000);
-
- // set DISP_MISC_CNTL register
- disp_misc_cntl = INREG(DISP_MISC_CNTL);
- disp_misc_cntl &= ~( DISP_MISC_CNTL_SOFT_RESET_GRPH_PP |
- DISP_MISC_CNTL_SOFT_RESET_SUBPIC_PP |
- DISP_MISC_CNTL_SOFT_RESET_OV0_PP |
- DISP_MISC_CNTL_SOFT_RESET_GRPH_SCLK |
- DISP_MISC_CNTL_SOFT_RESET_SUBPIC_SCLK |
- DISP_MISC_CNTL_SOFT_RESET_OV0_SCLK |
- DISP_MISC_CNTL_SOFT_RESET_GRPH2_PP |
- DISP_MISC_CNTL_SOFT_RESET_GRPH2_SCLK |
- DISP_MISC_CNTL_SOFT_RESET_LVDS |
- DISP_MISC_CNTL_SOFT_RESET_TMDS |
- DISP_MISC_CNTL_SOFT_RESET_DIG_TMDS |
- DISP_MISC_CNTL_SOFT_RESET_TV);
- OUTREG(DISP_MISC_CNTL, disp_misc_cntl);
-
- // set DISP_PWR_MAN register
- disp_pwr_man = INREG(DISP_PWR_MAN);
- // clau - 9.29.2000 - changes made to bit23:18 to set to 1 as requested by George
- disp_pwr_man |= (DISP_PWR_MAN_DIG_TMDS_ENABLE_RST |
- DISP_PWR_MAN_TV_ENABLE_RST |
- // DISP_PWR_MAN_AUTO_PWRUP_EN |
- DISP_PWR_MAN_DISP_D3_GRPH_RST |
- DISP_PWR_MAN_DISP_D3_SUBPIC_RST |
- DISP_PWR_MAN_DISP_D3_OV0_RST |
- DISP_PWR_MAN_DISP_D1D2_GRPH_RST |
- DISP_PWR_MAN_DISP_D1D2_SUBPIC_RST |
- DISP_PWR_MAN_DISP_D1D2_OV0_RST);
- disp_pwr_man &= ~(DISP_PWR_MAN_DISP_PWR_MAN_D3_CRTC_EN |
- DISP_PWR_MAN_DISP2_PWR_MAN_D3_CRTC2_EN|
- DISP_PWR_MAN_DISP_D3_RST |
- DISP_PWR_MAN_DISP_D3_REG_RST);
- OUTREG(DISP_PWR_MAN, disp_pwr_man);
-
- // clau - 10.24.2000
- // - add in setting for BUS_CNTL1 b27:26 = 0x01 and b31 = 0x1
- // - add in setting for AGP_CNTL b7:0 = 0x20
- // - add in setting for DVI_DDC_DATA_OUT_EN b17:16 = 0x0
-
- // the following settings (two lines) are applied at a later part of this function, only on mobile platform
- // requres -mobile flag
- OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1) & 0xf3ffffff) | 0x04000000);
- OUTREG(BUS_CNTL1, INREG(BUS_CNTL1) | 0x80000000);
- OUTREG(AGP_CNTL, (INREG(AGP_CNTL) & 0xffffff00) | 0x20);
- OUTREG(GPIO_DVI_DDC, INREG(GPIO_DVI_DDC) & 0xfffcffff);
-
- // yulee - 12.12.2000
- // A12 only
- // EN_MCLK_TRISTATE_IN_SUSPEND@MCLK_MISC = 1
- // ACCESS_REGS_IN_SUSPEND@CLK_PIN_CNTL = 0
- // only on mobile platform
- OUTPLL(MCLK_MISC, INPLL(MCLK_MISC) | 0x00040000 );
-
- // yulee -12.12.2000
- // AGPCLK_VALID@BUS_CNTL1 = 1
- // MOBILE_PLATFORM_SEL@BUS_CNTL1 = 01
- // CRTC_STEREO_SYNC_OUT_EN@CRTC_OFFSET_CNTL = 0
- // CG_CLK_TO_OUTPIN@CLK_PIN_CNTL = 0
- // only on mobile platform
- OUTPLL(CLK_PIN_CNTL, INPLL(CLK_PIN_CNTL ) & 0xFFFFF7FF );
- OUTREG(BUS_CNTL1, (INREG(BUS_CNTL1 ) & 0xF3FFFFFF) | 0x84000000 );
- OUTREG(CRTC_OFFSET_CNTL, INREG(CRTC_OFFSET_CNTL ) & 0xFFEFFFFF );
-
- mdelay(100);
-#endif
-
- /* Disable CRTCs */
- OUTREG(CRTC_GEN_CNTL, (INREG(CRTC_GEN_CNTL) & ~CRTC_EN) | CRTC_DISP_REQ_EN_B);
- OUTREG(CRTC2_GEN_CNTL, (INREG(CRTC2_GEN_CNTL) & ~CRTC2_EN) | CRTC2_DISP_REQ_EN_B);
- (void)INREG(CRTC2_GEN_CNTL);
- mdelay(17);
-}
-
-static void radeon_set_suspend(struct radeonfb_info *rinfo, int suspend)
-{
- u16 pwr_cmd;
-
- if (!rinfo->pm_reg)
- return;
-
- /* Set the chip into appropriate suspend mode (we use D2,
- * D3 would require a compete re-initialization of the chip,
- * including PCI config registers, clocks, AGP conf, ...)
- */
- if (suspend) {
- /* According to ATI, we should program V2CLK here, I have
- * to verify what's up exactly
- */
- /* Save some registers */
- radeon_pm_save_regs(rinfo);
-
- /* Check that on M7 too, might work might not. M7 may also
- * need explicit enabling of PM
- */
- if (rinfo->arch == RADEON_M6) {
- /* Program V2CLK */
- radeon_pm_program_v2clk(rinfo);
-
- /* Disable IO PADs */
- radeon_pm_disable_iopad(rinfo);
-
- /* Set low current */
- radeon_pm_low_current(rinfo);
-
- /* Prepare chip for power management */
- radeon_pm_setup_for_suspend(rinfo);
-
- /* Reset the MDLL */
- OUTPLL(MDLL_CKO, INPLL(MDLL_CKO) | MCKOA_RESET);
- (void)INPLL(MDLL_RDCKA);
- OUTPLL(MDLL_CKO, INPLL(MDLL_CKO) & ~MCKOA_RESET);
- (void)INPLL(MDLL_RDCKA);
- }
-
- /* Switch PCI power managment to D2. */
- for (;;) {
- pci_read_config_word(
- rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
- &pwr_cmd);
- if (pwr_cmd & 2)
- break;
- pci_write_config_word(
- rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL,
- (pwr_cmd & ~PCI_PM_CTRL_STATE_MASK) | 2);
- mdelay(500);
- }
- } else {
- /* Switch back PCI powermanagment to D0 */
- mdelay(200);
- pci_write_config_word(rinfo->pdev, rinfo->pm_reg+PCI_PM_CTRL, 0);
- mdelay(500);
-
- dbg_clk = INPLL(1);
-
- /* Do we need that on M7 ? */
- if (rinfo->arch == RADEON_M6) {
- /* Restore the MDLL */
- OUTPLL(MDLL_CKO, INPLL(MDLL_CKO) & ~MCKOA_RESET);
- (void)INPLL(MDLL_CKO);
- }
-
- /* Restore some registers */
- radeon_pm_restore_regs(rinfo);
- }
-}
-
-/*
- * Save the contents of the framebuffer when we go to sleep,
- * and restore it when we wake up again.
- */
-
-int radeon_sleep_notify(struct pmu_sleep_notifier *self, int when)
-{
- struct radeonfb_info *rinfo;
-
- for (rinfo = board_list; rinfo != NULL; rinfo = rinfo->next) {
- struct fb_fix_screeninfo fix;
- int nb;
- struct display *disp;
-
- disp = (rinfo->currcon < 0) ? rinfo->info.disp : &fb_display[rinfo->currcon];
-
- switch (rinfo->arch) {
- case RADEON_M6:
- case RADEON_M7:
- case RADEON_M9:
- break;
- default:
- return PBOOK_SLEEP_REFUSE;
- }
-
- radeonfb_get_fix(&fix, fg_console, (struct fb_info *)rinfo);
- nb = fb_display[fg_console].var.yres * fix.line_length;
-
- switch (when) {
- case PBOOK_SLEEP_NOW:
- acquire_console_sem();
- disp->dispsw = &fbcon_dummy;
-
- if (!noaccel) {
- /* Make sure engine is reset */
- radeon_engine_reset();
- radeon_engine_idle();
- }
-
- /* Blank display and LCD */
- radeonfb_blank(VESA_POWERDOWN+1,
- (struct fb_info *)rinfo);
-
- /* Sleep */
- rinfo->asleep = 1;
- radeon_set_suspend(rinfo, 1);
- release_console_sem();
-
- break;
- case PBOOK_WAKE:
- acquire_console_sem();
- /* Wakeup */
- radeon_set_suspend(rinfo, 0);
-
- if (!noaccel)
- radeon_engine_init(rinfo);
- rinfo->asleep = 0;
- radeon_set_dispsw(rinfo, disp);
- radeon_load_video_mode(rinfo, &disp->var);
- do_install_cmap(rinfo->currcon < 0 ? 0 : rinfo->currcon,
- (struct fb_info *)rinfo);
-
- radeonfb_blank(0, (struct fb_info *)rinfo);
- release_console_sem();
- printk("CLK_PIN_CNTL on wakeup was: %08x\n", dbg_clk);
- break;
- }
- }
-
- return PBOOK_SLEEP_OK;
-}
-
-#endif /* CONFIG_PMAC_PBOOK */
-
-static int radeonfb_pci_register (struct pci_dev *pdev,
- const struct pci_device_id *ent)
-{
- struct radeonfb_info *rinfo;
- struct radeon_chip_info *rci = &radeon_chip_info[ent->driver_data];
- u32 tmp;
-
- RTRACE("radeonfb_pci_register BEGIN\n");
-
- /* Enable device in PCI config */
- if (pci_enable_device(pdev) != 0) {
- printk(KERN_ERR "radeonfb: Cannot enable PCI device\n");
- return -ENODEV;
- }
-
- rinfo = kmalloc (sizeof (struct radeonfb_info), GFP_KERNEL);
- if (!rinfo) {
- printk ("radeonfb: could not allocate memory\n");
- return -ENODEV;
- }
-
- memset (rinfo, 0, sizeof (struct radeonfb_info));
- //info = &rinfo->info;
- rinfo->pdev = pdev;
- strcpy(rinfo->name, rci->name);
- rinfo->arch = rci->arch;
-
- /* Set base addrs */
- rinfo->fb_base_phys = pci_resource_start (pdev, 0);
- rinfo->mmio_base_phys = pci_resource_start (pdev, 2);
-
- /* request the mem regions */
- if (!request_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0), "radeonfb")) {
- printk ("radeonfb: cannot reserve FB region\n");
- kfree (rinfo);
- return -ENODEV;
- }
-
- if (!request_mem_region (rinfo->mmio_base_phys,
- pci_resource_len(pdev, 2), "radeonfb")) {
- printk ("radeonfb: cannot reserve MMIO region\n");
- release_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0));
- kfree (rinfo);
- return -ENODEV;
- }
-
- /* map the regions */
- rinfo->mmio_base = ioremap (rinfo->mmio_base_phys, RADEON_REGSIZE);
- if (!rinfo->mmio_base) {
- printk ("radeonfb: cannot map MMIO\n");
- release_mem_region (rinfo->mmio_base_phys,
- pci_resource_len(pdev, 2));
- release_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0));
- kfree (rinfo);
- return -ENODEV;
- }
-
- rinfo->chipset = pdev->device;
-
- switch (rinfo->arch) {
- case RADEON_R100:
- rinfo->hasCRTC2 = 0;
- break;
- default:
- /* all the rest have it */
- rinfo->hasCRTC2 = 1;
- break;
- }
-#if 0
- if (rinfo->arch == RADEON_M7) {
- /*
- * Noticed some errors in accel with M7, will have to work these out...
- */
- noaccel = 1;
- }
-#endif
- if (mirror)
- printk("radeonfb: mirroring display to CRT\n");
-
- /* framebuffer size */
- tmp = INREG(CONFIG_MEMSIZE);
-
- /* mem size is bits [28:0], mask off the rest */
- rinfo->video_ram = tmp & CONFIG_MEMSIZE_MASK;
-
- /* ram type */
- tmp = INREG(MEM_SDRAM_MODE_REG);
- switch ((MEM_CFG_TYPE & tmp) >> 30) {
- case 0:
- /* SDR SGRAM (2:1) */
- strcpy(rinfo->ram_type, "SDR SGRAM");
- rinfo->ram.ml = 4;
- rinfo->ram.mb = 4;
- rinfo->ram.trcd = 1;
- rinfo->ram.trp = 2;
- rinfo->ram.twr = 1;
- rinfo->ram.cl = 2;
- rinfo->ram.loop_latency = 16;
- rinfo->ram.rloop = 16;
-
- break;
- case 1:
- /* DDR SGRAM */
- strcpy(rinfo->ram_type, "DDR SGRAM");
- rinfo->ram.ml = 4;
- rinfo->ram.mb = 4;
- rinfo->ram.trcd = 3;
- rinfo->ram.trp = 3;
- rinfo->ram.twr = 2;
- rinfo->ram.cl = 3;
- rinfo->ram.tr2w = 1;
- rinfo->ram.loop_latency = 16;
- rinfo->ram.rloop = 16;
-
- break;
- default:
- /* 64-bit SDR SGRAM */
- strcpy(rinfo->ram_type, "SDR SGRAM 64");
- rinfo->ram.ml = 4;
- rinfo->ram.mb = 8;
- rinfo->ram.trcd = 3;
- rinfo->ram.trp = 3;
- rinfo->ram.twr = 1;
- rinfo->ram.cl = 3;
- rinfo->ram.tr2w = 1;
- rinfo->ram.loop_latency = 17;
- rinfo->ram.rloop = 17;
-
- break;
- }
-
- rinfo->bios_seg = radeon_find_rom(rinfo);
- radeon_get_pllinfo(rinfo, rinfo->bios_seg);
-
- /*
- * Hack to get around some busted production M6's
- * reporting no ram
- */
- if (rinfo->video_ram == 0) {
- switch (pdev->device) {
- case PCI_DEVICE_ID_ATI_RADEON_LY:
- case PCI_DEVICE_ID_ATI_RADEON_LZ:
- rinfo->video_ram = 8192 * 1024;
- break;
- default:
- break;
- }
- }
-
-
- RTRACE("radeonfb: probed %s %dk videoram\n", (rinfo->ram_type), (rinfo->video_ram/1024));
-
-#if !defined(__powerpc__)
- radeon_get_moninfo(rinfo);
-#else
- switch (pdev->device) {
- case PCI_DEVICE_ID_ATI_RADEON_LW:
- case PCI_DEVICE_ID_ATI_RADEON_LX:
- case PCI_DEVICE_ID_ATI_RADEON_LY:
- case PCI_DEVICE_ID_ATI_RADEON_LZ:
- rinfo->dviDisp_type = MT_LCD;
- break;
- default:
- radeon_get_moninfo(rinfo);
- break;
- }
-#endif
-
- radeon_get_EDID(rinfo);
-
- if ((rinfo->dviDisp_type == MT_DFP) || (rinfo->dviDisp_type == MT_LCD) ||
- (rinfo->crtDisp_type == MT_DFP)) {
- if (!radeon_get_dfpinfo(rinfo)) {
- iounmap(rinfo->mmio_base);
- release_mem_region (rinfo->mmio_base_phys,
- pci_resource_len(pdev, 2));
- release_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0));
- kfree (rinfo);
- return -ENODEV;
- }
- }
-
- rinfo->fb_base = ioremap (rinfo->fb_base_phys, rinfo->video_ram);
- if (!rinfo->fb_base) {
- printk ("radeonfb: cannot map FB\n");
- iounmap(rinfo->mmio_base);
- release_mem_region (rinfo->mmio_base_phys,
- pci_resource_len(pdev, 2));
- release_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0));
- kfree (rinfo);
- return -ENODEV;
- }
-
- /* I SHOULD FIX THAT CRAP ! I should probably mimmic XFree DRI
- * driver setup here.
- *
- * On PPC, OF based cards setup the internal memory
- * mapping in strange ways. We change it so that the
- * framebuffer is mapped at 0 and given half of the card's
- * address space (2Gb). AGP is mapped high (0xe0000000) and
- * can use up to 512Mb. Once DRI is fully implemented, we
- * will have to setup the PCI remapper to remap the agp_special_page
- * memory page somewhere between those regions so that the card
- * use a normal PCI bus master cycle to access the ring read ptr.
- * --BenH.
- */
-#ifdef CONFIG_ALL_PPC
- if (rinfo->hasCRTC2)
- OUTREG(CRTC2_GEN_CNTL,
- (INREG(CRTC2_GEN_CNTL) & ~CRTC2_EN) | CRTC2_DISP_REQ_EN_B);
- OUTREG(CRTC_EXT_CNTL, INREG(CRTC_EXT_CNTL) | CRTC_DISPLAY_DIS);
- OUTREG(MC_FB_LOCATION, 0x7fff0000);
- OUTREG(MC_AGP_LOCATION, 0xffffe000);
- OUTREG(DISPLAY_BASE_ADDR, 0x00000000);
- if (rinfo->hasCRTC2)
- OUTREG(CRTC2_DISPLAY_BASE_ADDR, 0x00000000);
- OUTREG(SRC_OFFSET, 0x00000000);
- OUTREG(DST_OFFSET, 0x00000000);
- mdelay(10);
- OUTREG(CRTC_EXT_CNTL, INREG(CRTC_EXT_CNTL) & ~CRTC_DISPLAY_DIS);
-#endif /* CONFIG_ALL_PPC */
-
- /* save current mode regs before we switch into the new one
- * so we can restore this upon __exit
- */
- radeon_save_state (rinfo, &rinfo->init_state);
-
- /* set all the vital stuff */
- radeon_set_fbinfo (rinfo);
-
- pci_set_drvdata(pdev, rinfo);
- rinfo->next = board_list;
- board_list = rinfo;
- ((struct fb_info *) rinfo)->device = &pdev->dev;
- if (register_framebuffer ((struct fb_info *) rinfo) < 0) {
- printk ("radeonfb: could not register framebuffer\n");
- iounmap(rinfo->fb_base);
- iounmap(rinfo->mmio_base);
- release_mem_region (rinfo->mmio_base_phys,
- pci_resource_len(pdev, 2));
- release_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0));
- kfree (rinfo);
- return -ENODEV;
- }
-
-#ifdef CONFIG_MTRR
- rinfo->mtrr_hdl = nomtrr ? -1 : mtrr_add(rinfo->fb_base_phys,
- rinfo->video_ram,
- MTRR_TYPE_WRCOMB, 1);
-#endif
-
-#ifdef CONFIG_PMAC_BACKLIGHT
- if (rinfo->dviDisp_type == MT_LCD)
- register_backlight_controller(&radeon_backlight_controller,
- rinfo, "ati");
-#endif
-
-#ifdef CONFIG_PMAC_PBOOK
- if (rinfo->dviDisp_type == MT_LCD) {
- rinfo->pm_reg = pci_find_capability(pdev, PCI_CAP_ID_PM);
- pmu_register_sleep_notifier(&radeon_sleep_notifier);
- }
-#endif
-
- printk ("radeonfb: ATI Radeon %s %s %d MB\n", rinfo->name, rinfo->ram_type,
- (rinfo->video_ram/(1024*1024)));
-
- if (rinfo->hasCRTC2) {
- printk("radeonfb: DVI port %s monitor connected\n",
- GET_MON_NAME(rinfo->dviDisp_type));
- printk("radeonfb: CRT port %s monitor connected\n",
- GET_MON_NAME(rinfo->crtDisp_type));
- } else {
- printk("radeonfb: CRT port %s monitor connected\n",
- GET_MON_NAME(rinfo->crtDisp_type));
- }
-
- RTRACE("radeonfb_pci_register END\n");
-
- return 0;
-}
-
-
-
-static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
-{
- struct radeonfb_info *rinfo = pci_get_drvdata(pdev);
-
- if (!rinfo)
- return;
-
- /* restore original state
- *
- * Doesn't quite work yet, possibly because of the PPC hacking
- * I do on startup, disable for now. --BenH
- */
- radeon_write_mode (rinfo, &rinfo->init_state);
-
-#ifdef CONFIG_MTRR
- if (rinfo->mtrr_hdl >= 0)
- mtrr_del(rinfo->mtrr_hdl, 0, 0);
-#endif
-
- unregister_framebuffer ((struct fb_info *) rinfo);
-
- iounmap(rinfo->mmio_base);
- iounmap(rinfo->fb_base);
-
- release_mem_region (rinfo->mmio_base_phys,
- pci_resource_len(pdev, 2));
- release_mem_region (rinfo->fb_base_phys,
- pci_resource_len(pdev, 0));
-
- kfree (rinfo);
-}
-
-
-static struct pci_driver radeonfb_driver = {
- .name = "radeonfb",
- .id_table = radeonfb_pci_table,
- .probe = radeonfb_pci_register,
- .remove = __devexit_p(radeonfb_pci_unregister),
-};
-
-#ifndef MODULE
-static int __init radeonfb_old_setup (char *options)
-{
- char *this_opt;
-
- if (!options || !*options)
- return 0;
-
- while ((this_opt = strsep (&options, ",")) != NULL) {
- if (!*this_opt)
- continue;
- if (!strncmp(this_opt, "noaccel", 7)) {
- noaccel = 1;
- } else if (!strncmp(this_opt, "mirror", 6)) {
- mirror = 1;
- } else if (!strncmp(this_opt, "dfp", 3)) {
- force_dfp = 1;
- } else if (!strncmp(this_opt, "panel_yres:", 11)) {
- panel_yres = simple_strtoul((this_opt+11), NULL, 0);
- } else if (!strncmp(this_opt, "nomtrr", 6)) {
- nomtrr = 1;
- } else
- mode_option = this_opt;
- }
-
- return 0;
-}
-#endif /* MODULE */
-
-static int __init radeonfb_old_init (void)
-{
-#ifndef MODULE
- char *option = NULL;
-
- if (fb_get_options("radeonfb_old", &option))
- return -ENODEV;
- radeonfb_old_setup(option);
-#endif
- return pci_register_driver (&radeonfb_driver);
-}
-
-
-static void __exit radeonfb_old_exit (void)
-{
- pci_unregister_driver (&radeonfb_driver);
-}
-
-module_init(radeonfb_old_init);
-module_exit(radeonfb_old_exit);
-
-
-MODULE_AUTHOR("Ani Joshi");
-MODULE_DESCRIPTION("framebuffer driver for ATI Radeon chipset");
-MODULE_LICENSE("GPL");
diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c
index f841f013b96f..3e9308f0f165 100644
--- a/drivers/video/riva/fbdev.c
+++ b/drivers/video/riva/fbdev.c
@@ -49,6 +49,7 @@
#include <asm/pci-bridge.h>
#endif
#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/machdep.h>
#include <asm/backlight.h>
#endif
@@ -1247,7 +1248,7 @@ static int rivafb_blank(int blank, struct fb_info *info)
CRTCout(par, 0x1a, vesa);
#ifdef CONFIG_PMAC_BACKLIGHT
- if ( par->FlatPanel && _machine == _MACH_Pmac) {
+ if ( par->FlatPanel && machine_is(powermac)) {
set_backlight_enable(!blank);
}
#endif
@@ -2037,9 +2038,9 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
info->fix.smem_len / (1024 * 1024),
info->fix.smem_start);
#ifdef CONFIG_PMAC_BACKLIGHT
- if (default_par->FlatPanel && _machine == _MACH_Pmac)
- register_backlight_controller(&riva_backlight_controller,
- default_par, "mnca");
+ if (default_par->FlatPanel && machine_is(powermac))
+ register_backlight_controller(&riva_backlight_controller,
+ default_par, "mnca");
#endif
NVTRACE_LEAVE();
return 0;
diff --git a/drivers/video/sticore.h b/drivers/video/sticore.h
index dc93336af557..1a9a60c74be3 100644
--- a/drivers/video/sticore.h
+++ b/drivers/video/sticore.h
@@ -34,36 +34,20 @@
* for them to fix it and steal their solution. prumpf
*/
-#define STI_WAIT 1
-
-#include <asm/io.h> /* for USE_HPPA_IOREMAP */
-
-#if USE_HPPA_IOREMAP
+#include <asm/io.h>
-#define STI_PTR(p) (p)
-#define PTR_STI(p) (p)
-static inline int STI_CALL( unsigned long func,
- void *flags, void *inptr, void *outptr, void *glob_cfg )
-{
- int (*f)(void *,void *,void *,void *);
- f = (void*)func;
- return f(flags, inptr, outptr, glob_cfg);
-}
-
-#else /* !USE_HPPA_IOREMAP */
+#define STI_WAIT 1
#define STI_PTR(p) ( virt_to_phys(p) )
-#define PTR_STI(p) ( phys_to_virt((long)p) )
-#define STI_CALL(func, flags, inptr, outptr, glob_cfg) \
- ({ \
- pdc_sti_call( func, (unsigned long)STI_PTR(flags), \
- (unsigned long)STI_PTR(inptr), \
- (unsigned long)STI_PTR(outptr), \
- (unsigned long)STI_PTR(glob_cfg)); \
+#define PTR_STI(p) ( phys_to_virt((unsigned long)p) )
+#define STI_CALL(func, flags, inptr, outptr, glob_cfg) \
+ ({ \
+ pdc_sti_call( func, STI_PTR(flags), \
+ STI_PTR(inptr), \
+ STI_PTR(outptr), \
+ STI_PTR(glob_cfg)); \
})
-#endif /* USE_HPPA_IOREMAP */
-
#define sti_onscreen_x(sti) (sti->glob_cfg->onscreen_x)
#define sti_onscreen_y(sti) (sti->glob_cfg->onscreen_y)
@@ -352,8 +336,9 @@ struct sti_struct {
struct sti_conf_outptr outptr; /* configuration */
struct sti_conf_outptr_ext outptr_ext;
- /* PCI data structures (pg. 17ff from sti.pdf) */
struct pci_dev *pd;
+
+ /* PCI data structures (pg. 17ff from sti.pdf) */
u8 rm_entry[16]; /* pci region mapper array == pci config space offset */
/* pointer to the fb_info where this STI device is used */
diff --git a/drivers/video/stifb.c b/drivers/video/stifb.c
index 56d71d6e9a72..4a292aae6eb2 100644
--- a/drivers/video/stifb.c
+++ b/drivers/video/stifb.c
@@ -3,7 +3,7 @@
* Low level Frame buffer driver for HP workstations with
* STI (standard text interface) video firmware.
*
- * Copyright (C) 2001-2005 Helge Deller <deller@gmx.de>
+ * Copyright (C) 2001-2006 Helge Deller <deller@gmx.de>
* Portions Copyright (C) 2001 Thomas Bogendoerfer <tsbogend@alpha.franken.de>
*
* Based on:
@@ -514,7 +514,7 @@ rattlerSetupPlanes(struct stifb_info *fb)
SETUP_HW(fb);
WRITE_BYTE(1, fb, REG_16b1);
- fb_memset(fb->info.fix.smem_start, 0xff,
+ fb_memset((void*)fb->info.fix.smem_start, 0xff,
fb->info.var.yres*fb->info.fix.line_length);
CRX24_SET_OVLY_MASK(fb);
@@ -908,83 +908,6 @@ SETUP_HCRX(struct stifb_info *fb)
/* ------------------- driver specific functions --------------------------- */
-#define TMPBUFLEN 2048
-
-static ssize_t
-stifb_read(struct file *file, char *buf, size_t count, loff_t *ppos)
-{
- unsigned long p = *ppos;
- struct inode *inode = file->f_dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- char tmpbuf[TMPBUFLEN];
-
- if (!info || ! info->screen_base)
- return -ENODEV;
-
- if (p >= info->fix.smem_len)
- return 0;
- if (count >= info->fix.smem_len)
- count = info->fix.smem_len;
- if (count + p > info->fix.smem_len)
- count = info->fix.smem_len - p;
- if (count > sizeof(tmpbuf))
- count = sizeof(tmpbuf);
- if (count) {
- char *base_addr;
-
- base_addr = info->screen_base;
- memcpy_fromio(&tmpbuf, base_addr+p, count);
- count -= copy_to_user(buf, &tmpbuf, count);
- if (!count)
- return -EFAULT;
- *ppos += count;
- }
- return count;
-}
-
-static ssize_t
-stifb_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
-{
- struct inode *inode = file->f_dentry->d_inode;
- int fbidx = iminor(inode);
- struct fb_info *info = registered_fb[fbidx];
- unsigned long p = *ppos;
- size_t c;
- int err;
- char tmpbuf[TMPBUFLEN];
-
- if (!info || !info->screen_base)
- return -ENODEV;
-
- if (p > info->fix.smem_len)
- return -ENOSPC;
- if (count >= info->fix.smem_len)
- count = info->fix.smem_len;
- err = 0;
- if (count + p > info->fix.smem_len) {
- count = info->fix.smem_len - p;
- err = -ENOSPC;
- }
-
- p += (unsigned long)info->screen_base;
- c = count;
- while (c) {
- int len = c > sizeof(tmpbuf) ? sizeof(tmpbuf) : c;
- err = -EFAULT;
- if (copy_from_user(&tmpbuf, buf, len))
- break;
- memcpy_toio(p, &tmpbuf, len);
- c -= len;
- p += len;
- buf += len;
- *ppos += len;
- }
- if (count-c)
- return (count-c);
- return err;
-}
-
static int
stifb_setcolreg(u_int regno, u_int red, u_int green,
u_int blue, u_int transp, struct fb_info *info)
@@ -1137,8 +1060,6 @@ stifb_init_display(struct stifb_info *fb)
static struct fb_ops stifb_ops = {
.owner = THIS_MODULE,
- .fb_read = stifb_read,
- .fb_write = stifb_write,
.fb_setcolreg = stifb_setcolreg,
.fb_blank = stifb_blank,
.fb_fillrect = cfb_fillrect,
@@ -1162,7 +1083,7 @@ stifb_init_fb(struct sti_struct *sti, int bpp_pref)
char *dev_name;
int bpp, xres, yres;
- fb = kmalloc(sizeof(*fb), GFP_ATOMIC);
+ fb = kzalloc(sizeof(*fb), GFP_ATOMIC);
if (!fb) {
printk(KERN_ERR "stifb: Could not allocate stifb structure\n");
return -ENODEV;
@@ -1171,7 +1092,6 @@ stifb_init_fb(struct sti_struct *sti, int bpp_pref)
info = &fb->info;
/* set struct to a known state */
- memset(fb, 0, sizeof(*fb));
fix = &info->fix;
var = &info->var;
@@ -1234,7 +1154,7 @@ stifb_init_fb(struct sti_struct *sti, int bpp_pref)
case S9000_ID_TOMCAT: /* Dual CRX, behaves else like a CRX */
/* FIXME: TomCat supports two heads:
* fb.iobase = REGION_BASE(fb_info,3);
- * fb.screen_base = (void*) REGION_BASE(fb_info,2);
+ * fb.screen_base = ioremap_nocache(REGION_BASE(fb_info,2),xxx);
* for now we only support the left one ! */
xres = fb->ngle_rom.x_size_visible;
yres = fb->ngle_rom.y_size_visible;
@@ -1327,7 +1247,8 @@ stifb_init_fb(struct sti_struct *sti, int bpp_pref)
strcpy(fix->id, "stifb");
info->fbops = &stifb_ops;
- info->screen_base = (void*) REGION_BASE(fb,1);
+ info->screen_base = ioremap_nocache(REGION_BASE(fb,1), fix->smem_len);
+ info->screen_size = fix->smem_len;
info->flags = FBINFO_DEFAULT;
info->pseudo_palette = &fb->pseudo_palette;
@@ -1457,7 +1378,7 @@ stifb_setup(char *options)
int i;
if (!options || !*options)
- return 0;
+ return 1;
if (strncmp(options, "off", 3) == 0) {
stifb_disabled = 1;
@@ -1472,7 +1393,7 @@ stifb_setup(char *options)
stifb_bpp_pref[i] = simple_strtoul(options, &options, 10);
}
}
- return 0;
+ return 1;
}
__setup("stifb=", stifb_setup);
diff --git a/drivers/video/w100fb.c b/drivers/video/w100fb.c
index f6e24ee85f07..5fc86ea20692 100644
--- a/drivers/video/w100fb.c
+++ b/drivers/video/w100fb.c
@@ -4,8 +4,9 @@
* Frame Buffer Device for ATI Imageon w100 (Wallaby)
*
* Copyright (C) 2002, ATI Corp.
- * Copyright (C) 2004-2005 Richard Purdie
+ * Copyright (C) 2004-2006 Richard Purdie
* Copyright (c) 2005 Ian Molton
+ * Copyright (c) 2006 Alberto Mardegan
*
* Rewritten for 2.6 by Richard Purdie <rpurdie@rpsys.net>
*
@@ -14,6 +15,9 @@
*
* w32xx support by Ian Molton
*
+ * Hardware acceleration support by Alberto Mardegan
+ * <mardy@users.sourceforge.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.
@@ -47,6 +51,7 @@ static void w100_set_dispregs(struct w100fb_par*);
static void w100_update_enable(void);
static void w100_update_disable(void);
static void calc_hsync(struct w100fb_par *par);
+static void w100_init_graphic_engine(struct w100fb_par *par);
struct w100_pll_info *w100_get_xtal_table(unsigned int freq);
/* Pseudo palette size */
@@ -248,6 +253,152 @@ static int w100fb_blank(int blank_mode, struct fb_info *info)
}
+static void w100_fifo_wait(int entries)
+{
+ union rbbm_status_u status;
+ int i;
+
+ for (i = 0; i < 2000000; i++) {
+ status.val = readl(remapped_regs + mmRBBM_STATUS);
+ if (status.f.cmdfifo_avail >= entries)
+ return;
+ udelay(1);
+ }
+ printk(KERN_ERR "w100fb: FIFO Timeout!\n");
+}
+
+
+static int w100fb_sync(struct fb_info *info)
+{
+ union rbbm_status_u status;
+ int i;
+
+ for (i = 0; i < 2000000; i++) {
+ status.val = readl(remapped_regs + mmRBBM_STATUS);
+ if (!status.f.gui_active)
+ return 0;
+ udelay(1);
+ }
+ printk(KERN_ERR "w100fb: Graphic engine timeout!\n");
+ return -EBUSY;
+}
+
+
+static void w100_init_graphic_engine(struct w100fb_par *par)
+{
+ union dp_gui_master_cntl_u gmc;
+ union dp_mix_u dp_mix;
+ union dp_datatype_u dp_datatype;
+ union dp_cntl_u dp_cntl;
+
+ w100_fifo_wait(4);
+ writel(W100_FB_BASE, remapped_regs + mmDST_OFFSET);
+ writel(par->xres, remapped_regs + mmDST_PITCH);
+ writel(W100_FB_BASE, remapped_regs + mmSRC_OFFSET);
+ writel(par->xres, remapped_regs + mmSRC_PITCH);
+
+ w100_fifo_wait(3);
+ writel(0, remapped_regs + mmSC_TOP_LEFT);
+ writel((par->yres << 16) | par->xres, remapped_regs + mmSC_BOTTOM_RIGHT);
+ writel(0x1fff1fff, remapped_regs + mmSRC_SC_BOTTOM_RIGHT);
+
+ w100_fifo_wait(4);
+ dp_cntl.val = 0;
+ dp_cntl.f.dst_x_dir = 1;
+ dp_cntl.f.dst_y_dir = 1;
+ dp_cntl.f.src_x_dir = 1;
+ dp_cntl.f.src_y_dir = 1;
+ dp_cntl.f.dst_major_x = 1;
+ dp_cntl.f.src_major_x = 1;
+ writel(dp_cntl.val, remapped_regs + mmDP_CNTL);
+
+ gmc.val = 0;
+ gmc.f.gmc_src_pitch_offset_cntl = 1;
+ gmc.f.gmc_dst_pitch_offset_cntl = 1;
+ gmc.f.gmc_src_clipping = 1;
+ gmc.f.gmc_dst_clipping = 1;
+ gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
+ gmc.f.gmc_dst_datatype = 3; /* from DstType_16Bpp_444 */
+ gmc.f.gmc_src_datatype = SRC_DATATYPE_EQU_DST;
+ gmc.f.gmc_byte_pix_order = 1;
+ gmc.f.gmc_default_sel = 0;
+ gmc.f.gmc_rop3 = ROP3_SRCCOPY;
+ gmc.f.gmc_dp_src_source = DP_SRC_MEM_RECTANGULAR;
+ gmc.f.gmc_clr_cmp_fcn_dis = 1;
+ gmc.f.gmc_wr_msk_dis = 1;
+ gmc.f.gmc_dp_op = DP_OP_ROP;
+ writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
+
+ dp_datatype.val = dp_mix.val = 0;
+ dp_datatype.f.dp_dst_datatype = gmc.f.gmc_dst_datatype;
+ dp_datatype.f.dp_brush_datatype = gmc.f.gmc_brush_datatype;
+ dp_datatype.f.dp_src2_type = 0;
+ dp_datatype.f.dp_src2_datatype = gmc.f.gmc_src_datatype;
+ dp_datatype.f.dp_src_datatype = gmc.f.gmc_src_datatype;
+ dp_datatype.f.dp_byte_pix_order = gmc.f.gmc_byte_pix_order;
+ writel(dp_datatype.val, remapped_regs + mmDP_DATATYPE);
+
+ dp_mix.f.dp_src_source = gmc.f.gmc_dp_src_source;
+ dp_mix.f.dp_src2_source = 1;
+ dp_mix.f.dp_rop3 = gmc.f.gmc_rop3;
+ dp_mix.f.dp_op = gmc.f.gmc_dp_op;
+ writel(dp_mix.val, remapped_regs + mmDP_MIX);
+}
+
+
+static void w100fb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect)
+{
+ union dp_gui_master_cntl_u gmc;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ cfb_fillrect(info, rect);
+ return;
+ }
+
+ gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
+ gmc.f.gmc_rop3 = ROP3_PATCOPY;
+ gmc.f.gmc_brush_datatype = GMC_BRUSH_SOLID_COLOR;
+ w100_fifo_wait(2);
+ writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
+ writel(rect->color, remapped_regs + mmDP_BRUSH_FRGD_CLR);
+
+ w100_fifo_wait(2);
+ writel((rect->dy << 16) | (rect->dx & 0xffff), remapped_regs + mmDST_Y_X);
+ writel((rect->width << 16) | (rect->height & 0xffff),
+ remapped_regs + mmDST_WIDTH_HEIGHT);
+}
+
+
+static void w100fb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *area)
+{
+ u32 dx = area->dx, dy = area->dy, sx = area->sx, sy = area->sy;
+ u32 h = area->height, w = area->width;
+ union dp_gui_master_cntl_u gmc;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+ if (info->flags & FBINFO_HWACCEL_DISABLED) {
+ cfb_copyarea(info, area);
+ return;
+ }
+
+ gmc.val = readl(remapped_regs + mmDP_GUI_MASTER_CNTL);
+ gmc.f.gmc_rop3 = ROP3_SRCCOPY;
+ gmc.f.gmc_brush_datatype = GMC_BRUSH_NONE;
+ w100_fifo_wait(1);
+ writel(gmc.val, remapped_regs + mmDP_GUI_MASTER_CNTL);
+
+ w100_fifo_wait(3);
+ writel((sy << 16) | (sx & 0xffff), remapped_regs + mmSRC_Y_X);
+ writel((dy << 16) | (dx & 0xffff), remapped_regs + mmDST_Y_X);
+ writel((w << 16) | (h & 0xffff), remapped_regs + mmDST_WIDTH_HEIGHT);
+}
+
+
/*
* Change the resolution by calling the appropriate hardware functions
*/
@@ -265,6 +416,7 @@ static void w100fb_activate_var(struct w100fb_par *par)
w100_init_lcd(par);
w100_set_dispregs(par);
w100_update_enable();
+ w100_init_graphic_engine(par);
calc_hsync(par);
@@ -394,9 +546,10 @@ static struct fb_ops w100fb_ops = {
.fb_set_par = w100fb_set_par,
.fb_setcolreg = w100fb_setcolreg,
.fb_blank = w100fb_blank,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
+ .fb_fillrect = w100fb_fillrect,
+ .fb_copyarea = w100fb_copyarea,
.fb_imageblit = cfb_imageblit,
+ .fb_sync = w100fb_sync,
};
#ifdef CONFIG_PM
@@ -543,7 +696,8 @@ int __init w100fb_probe(struct platform_device *pdev)
}
info->fbops = &w100fb_ops;
- info->flags = FBINFO_DEFAULT;
+ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
+ FBINFO_HWACCEL_FILLRECT;
info->node = -1;
info->screen_base = remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE);
info->screen_size = REMAPPED_FB_LEN;
diff --git a/drivers/video/w100fb.h b/drivers/video/w100fb.h
index 7a58a1e3e427..fffae7b4f6e9 100644
--- a/drivers/video/w100fb.h
+++ b/drivers/video/w100fb.h
@@ -122,15 +122,32 @@
/* Block DISPLAY End: */
/* Block GFX Start: */
+#define mmDST_OFFSET 0x1004
+#define mmDST_PITCH 0x1008
+#define mmDST_Y_X 0x1038
+#define mmDST_WIDTH_HEIGHT 0x1198
+#define mmDP_GUI_MASTER_CNTL 0x106C
#define mmBRUSH_OFFSET 0x108C
#define mmBRUSH_Y_X 0x1074
+#define mmDP_BRUSH_FRGD_CLR 0x107C
+#define mmSRC_OFFSET 0x11AC
+#define mmSRC_PITCH 0x11B0
+#define mmSRC_Y_X 0x1034
#define mmDEFAULT_PITCH_OFFSET 0x10A0
#define mmDEFAULT_SC_BOTTOM_RIGHT 0x10A8
#define mmDEFAULT2_SC_BOTTOM_RIGHT 0x10AC
+#define mmSC_TOP_LEFT 0x11BC
+#define mmSC_BOTTOM_RIGHT 0x11C0
+#define mmSRC_SC_BOTTOM_RIGHT 0x11C4
#define mmGLOBAL_ALPHA 0x1210
#define mmFILTER_COEF 0x1214
#define mmMVC_CNTL_START 0x11E0
#define mmE2_ARITHMETIC_CNTL 0x1220
+#define mmDP_CNTL 0x11C8
+#define mmDP_CNTL_DST_DIR 0x11CC
+#define mmDP_DATATYPE 0x12C4
+#define mmDP_MIX 0x12C8
+#define mmDP_WRITE_MSK 0x12CC
#define mmENG_CNTL 0x13E8
#define mmENG_PERF_CNT 0x13F0
/* Block GFX End: */
@@ -179,6 +196,7 @@
/* Block RBBM Start: */
#define mmWAIT_UNTIL 0x1400
#define mmISYNC_CNTL 0x1404
+#define mmRBBM_STATUS 0x0140
#define mmRBBM_CNTL 0x0144
#define mmNQWAIT_UNTIL 0x0150
/* Block RBBM End: */
@@ -225,147 +243,147 @@
/* Register structure definitions */
struct wrap_top_dir_t {
- unsigned long top_addr : 23;
- unsigned long : 9;
+ u32 top_addr : 23;
+ u32 : 9;
} __attribute__((packed));
union wrap_top_dir_u {
- unsigned long val : 32;
+ u32 val : 32;
struct wrap_top_dir_t f;
} __attribute__((packed));
struct wrap_start_dir_t {
- unsigned long start_addr : 23;
- unsigned long : 9;
+ u32 start_addr : 23;
+ u32 : 9;
} __attribute__((packed));
union wrap_start_dir_u {
- unsigned long val : 32;
+ u32 val : 32;
struct wrap_start_dir_t f;
} __attribute__((packed));
struct cif_cntl_t {
- unsigned long swap_reg : 2;
- unsigned long swap_fbuf_1 : 2;
- unsigned long swap_fbuf_2 : 2;
- unsigned long swap_fbuf_3 : 2;
- unsigned long pmi_int_disable : 1;
- unsigned long pmi_schmen_disable : 1;
- unsigned long intb_oe : 1;
- unsigned long en_wait_to_compensate_dq_prop_dly : 1;
- unsigned long compensate_wait_rd_size : 2;
- unsigned long wait_asserted_timeout_val : 2;
- unsigned long wait_masked_val : 2;
- unsigned long en_wait_timeout : 1;
- unsigned long en_one_clk_setup_before_wait : 1;
- unsigned long interrupt_active_high : 1;
- unsigned long en_overwrite_straps : 1;
- unsigned long strap_wait_active_hi : 1;
- unsigned long lat_busy_count : 2;
- unsigned long lat_rd_pm4_sclk_busy : 1;
- unsigned long dis_system_bits : 1;
- unsigned long dis_mr : 1;
- unsigned long cif_spare_1 : 4;
+ u32 swap_reg : 2;
+ u32 swap_fbuf_1 : 2;
+ u32 swap_fbuf_2 : 2;
+ u32 swap_fbuf_3 : 2;
+ u32 pmi_int_disable : 1;
+ u32 pmi_schmen_disable : 1;
+ u32 intb_oe : 1;
+ u32 en_wait_to_compensate_dq_prop_dly : 1;
+ u32 compensate_wait_rd_size : 2;
+ u32 wait_asserted_timeout_val : 2;
+ u32 wait_masked_val : 2;
+ u32 en_wait_timeout : 1;
+ u32 en_one_clk_setup_before_wait : 1;
+ u32 interrupt_active_high : 1;
+ u32 en_overwrite_straps : 1;
+ u32 strap_wait_active_hi : 1;
+ u32 lat_busy_count : 2;
+ u32 lat_rd_pm4_sclk_busy : 1;
+ u32 dis_system_bits : 1;
+ u32 dis_mr : 1;
+ u32 cif_spare_1 : 4;
} __attribute__((packed));
union cif_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct cif_cntl_t f;
} __attribute__((packed));
struct cfgreg_base_t {
- unsigned long cfgreg_base : 24;
- unsigned long : 8;
+ u32 cfgreg_base : 24;
+ u32 : 8;
} __attribute__((packed));
union cfgreg_base_u {
- unsigned long val : 32;
+ u32 val : 32;
struct cfgreg_base_t f;
} __attribute__((packed));
struct cif_io_t {
- unsigned long dq_srp : 1;
- unsigned long dq_srn : 1;
- unsigned long dq_sp : 4;
- unsigned long dq_sn : 4;
- unsigned long waitb_srp : 1;
- unsigned long waitb_srn : 1;
- unsigned long waitb_sp : 4;
- unsigned long waitb_sn : 4;
- unsigned long intb_srp : 1;
- unsigned long intb_srn : 1;
- unsigned long intb_sp : 4;
- unsigned long intb_sn : 4;
- unsigned long : 2;
+ u32 dq_srp : 1;
+ u32 dq_srn : 1;
+ u32 dq_sp : 4;
+ u32 dq_sn : 4;
+ u32 waitb_srp : 1;
+ u32 waitb_srn : 1;
+ u32 waitb_sp : 4;
+ u32 waitb_sn : 4;
+ u32 intb_srp : 1;
+ u32 intb_srn : 1;
+ u32 intb_sp : 4;
+ u32 intb_sn : 4;
+ u32 : 2;
} __attribute__((packed));
union cif_io_u {
- unsigned long val : 32;
+ u32 val : 32;
struct cif_io_t f;
} __attribute__((packed));
struct cif_read_dbg_t {
- unsigned long unpacker_pre_fetch_trig_gen : 2;
- unsigned long dly_second_rd_fetch_trig : 1;
- unsigned long rst_rd_burst_id : 1;
- unsigned long dis_rd_burst_id : 1;
- unsigned long en_block_rd_when_packer_is_not_emp : 1;
- unsigned long dis_pre_fetch_cntl_sm : 1;
- unsigned long rbbm_chrncy_dis : 1;
- unsigned long rbbm_rd_after_wr_lat : 2;
- unsigned long dis_be_during_rd : 1;
- unsigned long one_clk_invalidate_pulse : 1;
- unsigned long dis_chnl_priority : 1;
- unsigned long rst_read_path_a_pls : 1;
- unsigned long rst_read_path_b_pls : 1;
- unsigned long dis_reg_rd_fetch_trig : 1;
- unsigned long dis_rd_fetch_trig_from_ind_addr : 1;
- unsigned long dis_rd_same_byte_to_trig_fetch : 1;
- unsigned long dis_dir_wrap : 1;
- unsigned long dis_ring_buf_to_force_dec : 1;
- unsigned long dis_addr_comp_in_16bit : 1;
- unsigned long clr_w : 1;
- unsigned long err_rd_tag_is_3 : 1;
- unsigned long err_load_when_ful_a : 1;
- unsigned long err_load_when_ful_b : 1;
- unsigned long : 7;
+ u32 unpacker_pre_fetch_trig_gen : 2;
+ u32 dly_second_rd_fetch_trig : 1;
+ u32 rst_rd_burst_id : 1;
+ u32 dis_rd_burst_id : 1;
+ u32 en_block_rd_when_packer_is_not_emp : 1;
+ u32 dis_pre_fetch_cntl_sm : 1;
+ u32 rbbm_chrncy_dis : 1;
+ u32 rbbm_rd_after_wr_lat : 2;
+ u32 dis_be_during_rd : 1;
+ u32 one_clk_invalidate_pulse : 1;
+ u32 dis_chnl_priority : 1;
+ u32 rst_read_path_a_pls : 1;
+ u32 rst_read_path_b_pls : 1;
+ u32 dis_reg_rd_fetch_trig : 1;
+ u32 dis_rd_fetch_trig_from_ind_addr : 1;
+ u32 dis_rd_same_byte_to_trig_fetch : 1;
+ u32 dis_dir_wrap : 1;
+ u32 dis_ring_buf_to_force_dec : 1;
+ u32 dis_addr_comp_in_16bit : 1;
+ u32 clr_w : 1;
+ u32 err_rd_tag_is_3 : 1;
+ u32 err_load_when_ful_a : 1;
+ u32 err_load_when_ful_b : 1;
+ u32 : 7;
} __attribute__((packed));
union cif_read_dbg_u {
- unsigned long val : 32;
+ u32 val : 32;
struct cif_read_dbg_t f;
} __attribute__((packed));
struct cif_write_dbg_t {
- unsigned long packer_timeout_count : 2;
- unsigned long en_upper_load_cond : 1;
- unsigned long en_chnl_change_cond : 1;
- unsigned long dis_addr_comp_cond : 1;
- unsigned long dis_load_same_byte_addr_cond : 1;
- unsigned long dis_timeout_cond : 1;
- unsigned long dis_timeout_during_rbbm : 1;
- unsigned long dis_packer_ful_during_rbbm_timeout : 1;
- unsigned long en_dword_split_to_rbbm : 1;
- unsigned long en_dummy_val : 1;
- unsigned long dummy_val_sel : 1;
- unsigned long mask_pm4_wrptr_dec : 1;
- unsigned long dis_mc_clean_cond : 1;
- unsigned long err_two_reqi_during_ful : 1;
- unsigned long err_reqi_during_idle_clk : 1;
- unsigned long err_global : 1;
- unsigned long en_wr_buf_dbg_load : 1;
- unsigned long en_wr_buf_dbg_path : 1;
- unsigned long sel_wr_buf_byte : 3;
- unsigned long dis_rd_flush_wr : 1;
- unsigned long dis_packer_ful_cond : 1;
- unsigned long dis_invalidate_by_ops_chnl : 1;
- unsigned long en_halt_when_reqi_err : 1;
- unsigned long cif_spare_2 : 5;
- unsigned long : 1;
+ u32 packer_timeout_count : 2;
+ u32 en_upper_load_cond : 1;
+ u32 en_chnl_change_cond : 1;
+ u32 dis_addr_comp_cond : 1;
+ u32 dis_load_same_byte_addr_cond : 1;
+ u32 dis_timeout_cond : 1;
+ u32 dis_timeout_during_rbbm : 1;
+ u32 dis_packer_ful_during_rbbm_timeout : 1;
+ u32 en_dword_split_to_rbbm : 1;
+ u32 en_dummy_val : 1;
+ u32 dummy_val_sel : 1;
+ u32 mask_pm4_wrptr_dec : 1;
+ u32 dis_mc_clean_cond : 1;
+ u32 err_two_reqi_during_ful : 1;
+ u32 err_reqi_during_idle_clk : 1;
+ u32 err_global : 1;
+ u32 en_wr_buf_dbg_load : 1;
+ u32 en_wr_buf_dbg_path : 1;
+ u32 sel_wr_buf_byte : 3;
+ u32 dis_rd_flush_wr : 1;
+ u32 dis_packer_ful_cond : 1;
+ u32 dis_invalidate_by_ops_chnl : 1;
+ u32 en_halt_when_reqi_err : 1;
+ u32 cif_spare_2 : 5;
+ u32 : 1;
} __attribute__((packed));
union cif_write_dbg_u {
- unsigned long val : 32;
+ u32 val : 32;
struct cif_write_dbg_t f;
} __attribute__((packed));
@@ -403,327 +421,327 @@ union cpu_defaults_u {
} __attribute__((packed));
struct crtc_total_t {
- unsigned long crtc_h_total : 10;
- unsigned long : 6;
- unsigned long crtc_v_total : 10;
- unsigned long : 6;
+ u32 crtc_h_total : 10;
+ u32 : 6;
+ u32 crtc_v_total : 10;
+ u32 : 6;
} __attribute__((packed));
union crtc_total_u {
- unsigned long val : 32;
+ u32 val : 32;
struct crtc_total_t f;
} __attribute__((packed));
struct crtc_ss_t {
- unsigned long ss_start : 10;
- unsigned long : 6;
- unsigned long ss_end : 10;
- unsigned long : 2;
- unsigned long ss_align : 1;
- unsigned long ss_pol : 1;
- unsigned long ss_run_mode : 1;
- unsigned long ss_en : 1;
+ u32 ss_start : 10;
+ u32 : 6;
+ u32 ss_end : 10;
+ u32 : 2;
+ u32 ss_align : 1;
+ u32 ss_pol : 1;
+ u32 ss_run_mode : 1;
+ u32 ss_en : 1;
} __attribute__((packed));
union crtc_ss_u {
- unsigned long val : 32;
+ u32 val : 32;
struct crtc_ss_t f;
} __attribute__((packed));
struct active_h_disp_t {
- unsigned long active_h_start : 10;
- unsigned long : 6;
- unsigned long active_h_end : 10;
- unsigned long : 6;
+ u32 active_h_start : 10;
+ u32 : 6;
+ u32 active_h_end : 10;
+ u32 : 6;
} __attribute__((packed));
union active_h_disp_u {
- unsigned long val : 32;
+ u32 val : 32;
struct active_h_disp_t f;
} __attribute__((packed));
struct active_v_disp_t {
- unsigned long active_v_start : 10;
- unsigned long : 6;
- unsigned long active_v_end : 10;
- unsigned long : 6;
+ u32 active_v_start : 10;
+ u32 : 6;
+ u32 active_v_end : 10;
+ u32 : 6;
} __attribute__((packed));
union active_v_disp_u {
- unsigned long val : 32;
+ u32 val : 32;
struct active_v_disp_t f;
} __attribute__((packed));
struct graphic_h_disp_t {
- unsigned long graphic_h_start : 10;
- unsigned long : 6;
- unsigned long graphic_h_end : 10;
- unsigned long : 6;
+ u32 graphic_h_start : 10;
+ u32 : 6;
+ u32 graphic_h_end : 10;
+ u32 : 6;
} __attribute__((packed));
union graphic_h_disp_u {
- unsigned long val : 32;
+ u32 val : 32;
struct graphic_h_disp_t f;
} __attribute__((packed));
struct graphic_v_disp_t {
- unsigned long graphic_v_start : 10;
- unsigned long : 6;
- unsigned long graphic_v_end : 10;
- unsigned long : 6;
+ u32 graphic_v_start : 10;
+ u32 : 6;
+ u32 graphic_v_end : 10;
+ u32 : 6;
} __attribute__((packed));
union graphic_v_disp_u{
- unsigned long val : 32;
+ u32 val : 32;
struct graphic_v_disp_t f;
} __attribute__((packed));
struct graphic_ctrl_t_w100 {
- unsigned long color_depth : 3;
- unsigned long portrait_mode : 2;
- unsigned long low_power_on : 1;
- unsigned long req_freq : 4;
- unsigned long en_crtc : 1;
- unsigned long en_graphic_req : 1;
- unsigned long en_graphic_crtc : 1;
- unsigned long total_req_graphic : 9;
- unsigned long lcd_pclk_on : 1;
- unsigned long lcd_sclk_on : 1;
- unsigned long pclk_running : 1;
- unsigned long sclk_running : 1;
- unsigned long : 6;
+ u32 color_depth : 3;
+ u32 portrait_mode : 2;
+ u32 low_power_on : 1;
+ u32 req_freq : 4;
+ u32 en_crtc : 1;
+ u32 en_graphic_req : 1;
+ u32 en_graphic_crtc : 1;
+ u32 total_req_graphic : 9;
+ u32 lcd_pclk_on : 1;
+ u32 lcd_sclk_on : 1;
+ u32 pclk_running : 1;
+ u32 sclk_running : 1;
+ u32 : 6;
} __attribute__((packed));
struct graphic_ctrl_t_w32xx {
- unsigned long color_depth : 3;
- unsigned long portrait_mode : 2;
- unsigned long low_power_on : 1;
- unsigned long req_freq : 4;
- unsigned long en_crtc : 1;
- unsigned long en_graphic_req : 1;
- unsigned long en_graphic_crtc : 1;
- unsigned long total_req_graphic : 10;
- unsigned long lcd_pclk_on : 1;
- unsigned long lcd_sclk_on : 1;
- unsigned long pclk_running : 1;
- unsigned long sclk_running : 1;
- unsigned long : 5;
+ u32 color_depth : 3;
+ u32 portrait_mode : 2;
+ u32 low_power_on : 1;
+ u32 req_freq : 4;
+ u32 en_crtc : 1;
+ u32 en_graphic_req : 1;
+ u32 en_graphic_crtc : 1;
+ u32 total_req_graphic : 10;
+ u32 lcd_pclk_on : 1;
+ u32 lcd_sclk_on : 1;
+ u32 pclk_running : 1;
+ u32 sclk_running : 1;
+ u32 : 5;
} __attribute__((packed));
union graphic_ctrl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct graphic_ctrl_t_w100 f_w100;
struct graphic_ctrl_t_w32xx f_w32xx;
} __attribute__((packed));
struct video_ctrl_t {
- unsigned long video_mode : 1;
- unsigned long keyer_en : 1;
- unsigned long en_video_req : 1;
- unsigned long en_graphic_req_video : 1;
- unsigned long en_video_crtc : 1;
- unsigned long video_hor_exp : 2;
- unsigned long video_ver_exp : 2;
- unsigned long uv_combine : 1;
- unsigned long total_req_video : 9;
- unsigned long video_ch_sel : 1;
- unsigned long video_portrait : 2;
- unsigned long yuv2rgb_en : 1;
- unsigned long yuv2rgb_option : 1;
- unsigned long video_inv_hor : 1;
- unsigned long video_inv_ver : 1;
- unsigned long gamma_sel : 2;
- unsigned long dis_limit : 1;
- unsigned long en_uv_hblend : 1;
- unsigned long rgb_gamma_sel : 2;
+ u32 video_mode : 1;
+ u32 keyer_en : 1;
+ u32 en_video_req : 1;
+ u32 en_graphic_req_video : 1;
+ u32 en_video_crtc : 1;
+ u32 video_hor_exp : 2;
+ u32 video_ver_exp : 2;
+ u32 uv_combine : 1;
+ u32 total_req_video : 9;
+ u32 video_ch_sel : 1;
+ u32 video_portrait : 2;
+ u32 yuv2rgb_en : 1;
+ u32 yuv2rgb_option : 1;
+ u32 video_inv_hor : 1;
+ u32 video_inv_ver : 1;
+ u32 gamma_sel : 2;
+ u32 dis_limit : 1;
+ u32 en_uv_hblend : 1;
+ u32 rgb_gamma_sel : 2;
} __attribute__((packed));
union video_ctrl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct video_ctrl_t f;
} __attribute__((packed));
struct disp_db_buf_cntl_rd_t {
- unsigned long en_db_buf : 1;
- unsigned long update_db_buf_done : 1;
- unsigned long db_buf_cntl : 6;
- unsigned long : 24;
+ u32 en_db_buf : 1;
+ u32 update_db_buf_done : 1;
+ u32 db_buf_cntl : 6;
+ u32 : 24;
} __attribute__((packed));
union disp_db_buf_cntl_rd_u {
- unsigned long val : 32;
+ u32 val : 32;
struct disp_db_buf_cntl_rd_t f;
} __attribute__((packed));
struct disp_db_buf_cntl_wr_t {
- unsigned long en_db_buf : 1;
- unsigned long update_db_buf : 1;
- unsigned long db_buf_cntl : 6;
- unsigned long : 24;
+ u32 en_db_buf : 1;
+ u32 update_db_buf : 1;
+ u32 db_buf_cntl : 6;
+ u32 : 24;
} __attribute__((packed));
union disp_db_buf_cntl_wr_u {
- unsigned long val : 32;
+ u32 val : 32;
struct disp_db_buf_cntl_wr_t f;
} __attribute__((packed));
struct gamma_value1_t {
- unsigned long gamma1 : 8;
- unsigned long gamma2 : 8;
- unsigned long gamma3 : 8;
- unsigned long gamma4 : 8;
+ u32 gamma1 : 8;
+ u32 gamma2 : 8;
+ u32 gamma3 : 8;
+ u32 gamma4 : 8;
} __attribute__((packed));
union gamma_value1_u {
- unsigned long val : 32;
+ u32 val : 32;
struct gamma_value1_t f;
} __attribute__((packed));
struct gamma_value2_t {
- unsigned long gamma5 : 8;
- unsigned long gamma6 : 8;
- unsigned long gamma7 : 8;
- unsigned long gamma8 : 8;
+ u32 gamma5 : 8;
+ u32 gamma6 : 8;
+ u32 gamma7 : 8;
+ u32 gamma8 : 8;
} __attribute__((packed));
union gamma_value2_u {
- unsigned long val : 32;
+ u32 val : 32;
struct gamma_value2_t f;
} __attribute__((packed));
struct gamma_slope_t {
- unsigned long slope1 : 3;
- unsigned long slope2 : 3;
- unsigned long slope3 : 3;
- unsigned long slope4 : 3;
- unsigned long slope5 : 3;
- unsigned long slope6 : 3;
- unsigned long slope7 : 3;
- unsigned long slope8 : 3;
- unsigned long : 8;
+ u32 slope1 : 3;
+ u32 slope2 : 3;
+ u32 slope3 : 3;
+ u32 slope4 : 3;
+ u32 slope5 : 3;
+ u32 slope6 : 3;
+ u32 slope7 : 3;
+ u32 slope8 : 3;
+ u32 : 8;
} __attribute__((packed));
union gamma_slope_u {
- unsigned long val : 32;
+ u32 val : 32;
struct gamma_slope_t f;
} __attribute__((packed));
struct mc_ext_mem_location_t {
- unsigned long mc_ext_mem_start : 16;
- unsigned long mc_ext_mem_top : 16;
+ u32 mc_ext_mem_start : 16;
+ u32 mc_ext_mem_top : 16;
} __attribute__((packed));
union mc_ext_mem_location_u {
- unsigned long val : 32;
+ u32 val : 32;
struct mc_ext_mem_location_t f;
} __attribute__((packed));
struct mc_fb_location_t {
- unsigned long mc_fb_start : 16;
- unsigned long mc_fb_top : 16;
+ u32 mc_fb_start : 16;
+ u32 mc_fb_top : 16;
} __attribute__((packed));
union mc_fb_location_u {
- unsigned long val : 32;
+ u32 val : 32;
struct mc_fb_location_t f;
} __attribute__((packed));
struct clk_pin_cntl_t {
- unsigned long osc_en : 1;
- unsigned long osc_gain : 5;
- unsigned long dont_use_xtalin : 1;
- unsigned long xtalin_pm_en : 1;
- unsigned long xtalin_dbl_en : 1;
- unsigned long : 7;
- unsigned long cg_debug : 16;
+ u32 osc_en : 1;
+ u32 osc_gain : 5;
+ u32 dont_use_xtalin : 1;
+ u32 xtalin_pm_en : 1;
+ u32 xtalin_dbl_en : 1;
+ u32 : 7;
+ u32 cg_debug : 16;
} __attribute__((packed));
union clk_pin_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct clk_pin_cntl_t f;
} __attribute__((packed));
struct pll_ref_fb_div_t {
- unsigned long pll_ref_div : 4;
- unsigned long : 4;
- unsigned long pll_fb_div_int : 6;
- unsigned long : 2;
- unsigned long pll_fb_div_frac : 3;
- unsigned long : 1;
- unsigned long pll_reset_time : 4;
- unsigned long pll_lock_time : 8;
+ u32 pll_ref_div : 4;
+ u32 : 4;
+ u32 pll_fb_div_int : 6;
+ u32 : 2;
+ u32 pll_fb_div_frac : 3;
+ u32 : 1;
+ u32 pll_reset_time : 4;
+ u32 pll_lock_time : 8;
} __attribute__((packed));
union pll_ref_fb_div_u {
- unsigned long val : 32;
+ u32 val : 32;
struct pll_ref_fb_div_t f;
} __attribute__((packed));
struct pll_cntl_t {
- unsigned long pll_pwdn : 1;
- unsigned long pll_reset : 1;
- unsigned long pll_pm_en : 1;
- unsigned long pll_mode : 1;
- unsigned long pll_refclk_sel : 1;
- unsigned long pll_fbclk_sel : 1;
- unsigned long pll_tcpoff : 1;
- unsigned long pll_pcp : 3;
- unsigned long pll_pvg : 3;
- unsigned long pll_vcofr : 1;
- unsigned long pll_ioffset : 2;
- unsigned long pll_pecc_mode : 2;
- unsigned long pll_pecc_scon : 2;
- unsigned long pll_dactal : 4;
- unsigned long pll_cp_clip : 2;
- unsigned long pll_conf : 3;
- unsigned long pll_mbctrl : 2;
- unsigned long pll_ring_off : 1;
+ u32 pll_pwdn : 1;
+ u32 pll_reset : 1;
+ u32 pll_pm_en : 1;
+ u32 pll_mode : 1;
+ u32 pll_refclk_sel : 1;
+ u32 pll_fbclk_sel : 1;
+ u32 pll_tcpoff : 1;
+ u32 pll_pcp : 3;
+ u32 pll_pvg : 3;
+ u32 pll_vcofr : 1;
+ u32 pll_ioffset : 2;
+ u32 pll_pecc_mode : 2;
+ u32 pll_pecc_scon : 2;
+ u32 pll_dactal : 4;
+ u32 pll_cp_clip : 2;
+ u32 pll_conf : 3;
+ u32 pll_mbctrl : 2;
+ u32 pll_ring_off : 1;
} __attribute__((packed));
union pll_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct pll_cntl_t f;
} __attribute__((packed));
struct sclk_cntl_t {
- unsigned long sclk_src_sel : 2;
- unsigned long : 2;
- unsigned long sclk_post_div_fast : 4;
- unsigned long sclk_clkon_hys : 3;
- unsigned long sclk_post_div_slow : 4;
- unsigned long disp_cg_ok2switch_en : 1;
- unsigned long sclk_force_reg : 1;
- unsigned long sclk_force_disp : 1;
- unsigned long sclk_force_mc : 1;
- unsigned long sclk_force_extmc : 1;
- unsigned long sclk_force_cp : 1;
- unsigned long sclk_force_e2 : 1;
- unsigned long sclk_force_e3 : 1;
- unsigned long sclk_force_idct : 1;
- unsigned long sclk_force_bist : 1;
- unsigned long busy_extend_cp : 1;
- unsigned long busy_extend_e2 : 1;
- unsigned long busy_extend_e3 : 1;
- unsigned long busy_extend_idct : 1;
- unsigned long : 3;
+ u32 sclk_src_sel : 2;
+ u32 : 2;
+ u32 sclk_post_div_fast : 4;
+ u32 sclk_clkon_hys : 3;
+ u32 sclk_post_div_slow : 4;
+ u32 disp_cg_ok2switch_en : 1;
+ u32 sclk_force_reg : 1;
+ u32 sclk_force_disp : 1;
+ u32 sclk_force_mc : 1;
+ u32 sclk_force_extmc : 1;
+ u32 sclk_force_cp : 1;
+ u32 sclk_force_e2 : 1;
+ u32 sclk_force_e3 : 1;
+ u32 sclk_force_idct : 1;
+ u32 sclk_force_bist : 1;
+ u32 busy_extend_cp : 1;
+ u32 busy_extend_e2 : 1;
+ u32 busy_extend_e3 : 1;
+ u32 busy_extend_idct : 1;
+ u32 : 3;
} __attribute__((packed));
union sclk_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct sclk_cntl_t f;
} __attribute__((packed));
struct pclk_cntl_t {
- unsigned long pclk_src_sel : 2;
- unsigned long : 2;
- unsigned long pclk_post_div : 4;
- unsigned long : 8;
- unsigned long pclk_force_disp : 1;
- unsigned long : 15;
+ u32 pclk_src_sel : 2;
+ u32 : 2;
+ u32 pclk_post_div : 4;
+ u32 : 8;
+ u32 pclk_force_disp : 1;
+ u32 : 15;
} __attribute__((packed));
union pclk_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct pclk_cntl_t f;
} __attribute__((packed));
@@ -735,36 +753,176 @@ union pclk_cntl_u {
#define TESTCLK_SRC_XTAL 0x06
struct clk_test_cntl_t {
- unsigned long testclk_sel : 4;
- unsigned long : 3;
- unsigned long start_check_freq : 1;
- unsigned long tstcount_rst : 1;
- unsigned long : 15;
- unsigned long test_count : 8;
+ u32 testclk_sel : 4;
+ u32 : 3;
+ u32 start_check_freq : 1;
+ u32 tstcount_rst : 1;
+ u32 : 15;
+ u32 test_count : 8;
} __attribute__((packed));
union clk_test_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct clk_test_cntl_t f;
} __attribute__((packed));
struct pwrmgt_cntl_t {
- unsigned long pwm_enable : 1;
- unsigned long : 1;
- unsigned long pwm_mode_req : 2;
- unsigned long pwm_wakeup_cond : 2;
- unsigned long pwm_fast_noml_hw_en : 1;
- unsigned long pwm_noml_fast_hw_en : 1;
- unsigned long pwm_fast_noml_cond : 4;
- unsigned long pwm_noml_fast_cond : 4;
- unsigned long pwm_idle_timer : 8;
- unsigned long pwm_busy_timer : 8;
+ u32 pwm_enable : 1;
+ u32 : 1;
+ u32 pwm_mode_req : 2;
+ u32 pwm_wakeup_cond : 2;
+ u32 pwm_fast_noml_hw_en : 1;
+ u32 pwm_noml_fast_hw_en : 1;
+ u32 pwm_fast_noml_cond : 4;
+ u32 pwm_noml_fast_cond : 4;
+ u32 pwm_idle_timer : 8;
+ u32 pwm_busy_timer : 8;
} __attribute__((packed));
union pwrmgt_cntl_u {
- unsigned long val : 32;
+ u32 val : 32;
struct pwrmgt_cntl_t f;
} __attribute__((packed));
+#define SRC_DATATYPE_EQU_DST 3
+
+#define ROP3_SRCCOPY 0xcc
+#define ROP3_PATCOPY 0xf0
+
+#define GMC_BRUSH_SOLID_COLOR 13
+#define GMC_BRUSH_NONE 15
+
+#define DP_SRC_MEM_RECTANGULAR 2
+
+#define DP_OP_ROP 0
+
+struct dp_gui_master_cntl_t {
+ u32 gmc_src_pitch_offset_cntl : 1;
+ u32 gmc_dst_pitch_offset_cntl : 1;
+ u32 gmc_src_clipping : 1;
+ u32 gmc_dst_clipping : 1;
+ u32 gmc_brush_datatype : 4;
+ u32 gmc_dst_datatype : 4;
+ u32 gmc_src_datatype : 3;
+ u32 gmc_byte_pix_order : 1;
+ u32 gmc_default_sel : 1;
+ u32 gmc_rop3 : 8;
+ u32 gmc_dp_src_source : 3;
+ u32 gmc_clr_cmp_fcn_dis : 1;
+ u32 : 1;
+ u32 gmc_wr_msk_dis : 1;
+ u32 gmc_dp_op : 1;
+} __attribute__((packed));
+
+union dp_gui_master_cntl_u {
+ u32 val : 32;
+ struct dp_gui_master_cntl_t f;
+} __attribute__((packed));
+
+struct rbbm_status_t {
+ u32 cmdfifo_avail : 7;
+ u32 : 1;
+ u32 hirq_on_rbb : 1;
+ u32 cprq_on_rbb : 1;
+ u32 cfrq_on_rbb : 1;
+ u32 hirq_in_rtbuf : 1;
+ u32 cprq_in_rtbuf : 1;
+ u32 cfrq_in_rtbuf : 1;
+ u32 cf_pipe_busy : 1;
+ u32 eng_ev_busy : 1;
+ u32 cp_cmdstrm_busy : 1;
+ u32 e2_busy : 1;
+ u32 rb2d_busy : 1;
+ u32 rb3d_busy : 1;
+ u32 se_busy : 1;
+ u32 re_busy : 1;
+ u32 tam_busy : 1;
+ u32 tdm_busy : 1;
+ u32 pb_busy : 1;
+ u32 : 6;
+ u32 gui_active : 1;
+} __attribute__((packed));
+
+union rbbm_status_u {
+ u32 val : 32;
+ struct rbbm_status_t f;
+} __attribute__((packed));
+
+struct dp_datatype_t {
+ u32 dp_dst_datatype : 4;
+ u32 : 4;
+ u32 dp_brush_datatype : 4;
+ u32 dp_src2_type : 1;
+ u32 dp_src2_datatype : 3;
+ u32 dp_src_datatype : 3;
+ u32 : 11;
+ u32 dp_byte_pix_order : 1;
+ u32 : 1;
+} __attribute__((packed));
+
+union dp_datatype_u {
+ u32 val : 32;
+ struct dp_datatype_t f;
+} __attribute__((packed));
+
+struct dp_mix_t {
+ u32 : 8;
+ u32 dp_src_source : 3;
+ u32 dp_src2_source : 3;
+ u32 : 2;
+ u32 dp_rop3 : 8;
+ u32 dp_op : 1;
+ u32 : 7;
+} __attribute__((packed));
+
+union dp_mix_u {
+ u32 val : 32;
+ struct dp_mix_t f;
+} __attribute__((packed));
+
+struct eng_cntl_t {
+ u32 erc_reg_rd_ws : 1;
+ u32 erc_reg_wr_ws : 1;
+ u32 erc_idle_reg_wr : 1;
+ u32 dis_engine_triggers : 1;
+ u32 dis_rop_src_uses_dst_w_h : 1;
+ u32 dis_src_uses_dst_dirmaj : 1;
+ u32 : 6;
+ u32 force_3dclk_when_2dclk : 1;
+ u32 : 19;
+} __attribute__((packed));
+
+union eng_cntl_u {
+ u32 val : 32;
+ struct eng_cntl_t f;
+} __attribute__((packed));
+
+struct dp_cntl_t {
+ u32 dst_x_dir : 1;
+ u32 dst_y_dir : 1;
+ u32 src_x_dir : 1;
+ u32 src_y_dir : 1;
+ u32 dst_major_x : 1;
+ u32 src_major_x : 1;
+ u32 : 26;
+} __attribute__((packed));
+
+union dp_cntl_u {
+ u32 val : 32;
+ struct dp_cntl_t f;
+} __attribute__((packed));
+
+struct dp_cntl_dst_dir_t {
+ u32 : 15;
+ u32 dst_y_dir : 1;
+ u32 : 15;
+ u32 dst_x_dir : 1;
+} __attribute__((packed));
+
+union dp_cntl_dst_dir_u {
+ u32 val : 32;
+ struct dp_cntl_dst_dir_t f;
+} __attribute__((packed));
+
#endif