LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [GIT PATCH] STAGING patches for 2.6.28
@ 2008-10-10 22:41 Greg KH
  2008-10-10 22:42 ` [PATCH 01/23] Staging: add TAINT_CRAP for all drivers/staging code Greg KH
                   ` (19 more replies)
  0 siblings, 20 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:41 UTC (permalink / raw)
  To: Linus Torvalds, Andrew Morton; +Cc: linux-kernel

Here is a tree of patches against your 2.6 git tree that adds the
drivers/staging/ directory and related infrastructure.

The infrastructure is:
  - add TAINT_CRAP to the kernel and the module build tools
  - add MAINTAINERS file entry for this pile of crap

The drivers we have for the staging/ directory in this patch series are:
  - USB/IP drivers
  - echo cancellation driver (used by mISDN and Zaptel)
  - eth131x network driver
  - slicoss network driver
  - sxg network driver
  - me4000 data collection driver
  - go7007 video capture driver
  - wlan-ng prism2 usb driver
  - w35und wifi driver

There are also follow-on patches by individuals cleaning up some of the
problems in these drivers that have kept them out of the main kernel
tree so far.

All of these drivers are self-contained in their respective
subdirectories, touching no other kernel code at all.  There are TODO
files in the directories detailing what is needed to be done to clean up
these drivers to get them merged into the main tree.

Please pull from:
	master.kernel.org:/pub/scm/linux/kernel/git/gregkh/staging-2.6.git/

Patches will be sent as a follow-on to this message to lkml for people
to see.

thanks,

greg k-h


 Documentation/sysctl/kernel.txt             |    1 +
 MAINTAINERS                                 |    7 +
 drivers/Kconfig                             |    2 +
 drivers/Makefile                            |    1 +
 drivers/staging/Kconfig                     |   44 +
 drivers/staging/Makefile                    |   11 +
 drivers/staging/echo/Kconfig                |    9 +
 drivers/staging/echo/Makefile               |    1 +
 drivers/staging/echo/TODO                   |   10 +
 drivers/staging/echo/bit_operations.h       |  253 +
 drivers/staging/echo/echo.c                 |  632 ++
 drivers/staging/echo/echo.h                 |  220 +
 drivers/staging/echo/fir.h                  |  369 +
 drivers/staging/echo/mmx.h                  |  288 +
 drivers/staging/et131x/Kconfig              |   18 +
 drivers/staging/et131x/Makefile             |   18 +
 drivers/staging/et131x/README               |   25 +
 drivers/staging/et131x/et1310_address_map.h | 2399 +++++++
 drivers/staging/et131x/et1310_eeprom.c      |  480 ++
 drivers/staging/et131x/et1310_eeprom.h      |   89 +
 drivers/staging/et131x/et1310_jagcore.c     |  220 +
 drivers/staging/et131x/et1310_jagcore.h     |  112 +
 drivers/staging/et131x/et1310_mac.c         |  792 +++
 drivers/staging/et131x/et1310_mac.h         |   93 +
 drivers/staging/et131x/et1310_phy.c         | 1281 ++++
 drivers/staging/et131x/et1310_phy.h         |  910 +++
 drivers/staging/et131x/et1310_pm.c          |  207 +
 drivers/staging/et131x/et1310_pm.h          |  125 +
 drivers/staging/et131x/et1310_rx.c          | 1391 ++++
 drivers/staging/et131x/et1310_rx.h          |  373 +
 drivers/staging/et131x/et1310_tx.c          | 1525 ++++
 drivers/staging/et131x/et1310_tx.h          |  242 +
 drivers/staging/et131x/et131x_adapter.h     |  347 +
 drivers/staging/et131x/et131x_config.c      |  325 +
 drivers/staging/et131x/et131x_config.h      |   67 +
 drivers/staging/et131x/et131x_debug.c       |  218 +
 drivers/staging/et131x/et131x_debug.h       |  201 +
 drivers/staging/et131x/et131x_defs.h        |  128 +
 drivers/staging/et131x/et131x_initpci.c     | 1046 +++
 drivers/staging/et131x/et131x_initpci.h     |   73 +
 drivers/staging/et131x/et131x_isr.c         |  488 ++
 drivers/staging/et131x/et131x_isr.h         |   65 +
 drivers/staging/et131x/et131x_netdev.c      |  856 +++
 drivers/staging/et131x/et131x_netdev.h      |   64 +
 drivers/staging/et131x/et131x_version.h     |   81 +
 drivers/staging/go7007/Kconfig              |   25 +
 drivers/staging/go7007/Makefile             |   18 +
 drivers/staging/go7007/README               |   11 +
 drivers/staging/go7007/go7007-driver.c      |  688 ++
 drivers/staging/go7007/go7007-fw.c          | 1639 +++++
 drivers/staging/go7007/go7007-i2c.c         |  309 +
 drivers/staging/go7007/go7007-priv.h        |  279 +
 drivers/staging/go7007/go7007-usb.c         | 1229 ++++
 drivers/staging/go7007/go7007-v4l2.c        | 1499 ++++
 drivers/staging/go7007/go7007.h             |  114 +
 drivers/staging/go7007/saa7134-go7007.c     |  484 ++
 drivers/staging/go7007/snd-go7007.c         |  305 +
 drivers/staging/go7007/wis-i2c.h            |   55 +
 drivers/staging/go7007/wis-ov7640.c         |  130 +
 drivers/staging/go7007/wis-saa7113.c        |  358 +
 drivers/staging/go7007/wis-saa7115.c        |  491 ++
 drivers/staging/go7007/wis-sony-tuner.c     |  742 ++
 drivers/staging/go7007/wis-tw2804.c         |  380 +
 drivers/staging/go7007/wis-tw9903.c         |  362 +
 drivers/staging/go7007/wis-uda1342.c        |  136 +
 drivers/staging/me4000/Kconfig              |   10 +
 drivers/staging/me4000/Makefile             |    1 +
 drivers/staging/me4000/README               |   13 +
 drivers/staging/me4000/me4000.c             | 6133 ++++++++++++++++
 drivers/staging/me4000/me4000.h             |  954 +++
 drivers/staging/me4000/me4000_firmware.h    |10033 +++++++++++++++++++++++++++
 drivers/staging/me4000/me4610_firmware.h    | 5409 +++++++++++++++
 drivers/staging/slicoss/Kconfig             |   14 +
 drivers/staging/slicoss/Makefile            |    1 +
 drivers/staging/slicoss/README              |   19 +
 drivers/staging/slicoss/gbdownload.h        | 8215 ++++++++++++++++++++++
 drivers/staging/slicoss/gbrcvucode.h        |  238 +
 drivers/staging/slicoss/oasisdbgdownload.h  | 6850 ++++++++++++++++++
 drivers/staging/slicoss/oasisdownload.h     | 6848 ++++++++++++++++++
 drivers/staging/slicoss/oasisrcvucode.h     |  205 +
 drivers/staging/slicoss/slic.h              |  598 ++
 drivers/staging/slicoss/slic_os.h           |   84 +
 drivers/staging/slicoss/slicbuild.h         |   96 +
 drivers/staging/slicoss/slicdbg.h           |  100 +
 drivers/staging/slicoss/slicdump.h          |  278 +
 drivers/staging/slicoss/slichw.h            |  845 +++
 drivers/staging/slicoss/slicinc.h           |  185 +
 drivers/staging/slicoss/slicoss.c           | 5936 ++++++++++++++++
 drivers/staging/sxg/Kconfig                 |   10 +
 drivers/staging/sxg/Makefile                |    1 +
 drivers/staging/sxg/README                  |   13 +
 drivers/staging/sxg/saharadbgdownload.h     | 4854 +++++++++++++
 drivers/staging/sxg/sxg.c                   | 3624 ++++++++++
 drivers/staging/sxg/sxg.h                   |  773 +++
 drivers/staging/sxg/sxg_os.h                |  154 +
 drivers/staging/sxg/sxgdbg.h                |  190 +
 drivers/staging/sxg/sxghif.h                |  861 +++
 drivers/staging/sxg/sxghw.h                 |  734 ++
 drivers/staging/sxg/sxgphycode.h            |  349 +
 drivers/staging/usbip/Kconfig               |   36 +
 drivers/staging/usbip/Makefile              |   12 +
 drivers/staging/usbip/README                |    6 +
 drivers/staging/usbip/stub.h                |   95 +
 drivers/staging/usbip/stub_dev.c            |  483 ++
 drivers/staging/usbip/stub_main.c           |  300 +
 drivers/staging/usbip/stub_rx.c             |  615 ++
 drivers/staging/usbip/stub_tx.c             |  371 +
 drivers/staging/usbip/usbip_common.c        |  997 +++
 drivers/staging/usbip/usbip_common.h        |  406 ++
 drivers/staging/usbip/usbip_event.c         |  141 +
 drivers/staging/usbip/vhci.h                |  142 +
 drivers/staging/usbip/vhci_hcd.c            | 1275 ++++
 drivers/staging/usbip/vhci_rx.c             |  251 +
 drivers/staging/usbip/vhci_sysfs.c          |  250 +
 drivers/staging/usbip/vhci_tx.c             |  239 +
 drivers/staging/winbond/Kconfig             |    7 +
 drivers/staging/winbond/Makefile            |   18 +
 drivers/staging/winbond/README              |   10 +
 drivers/staging/winbond/adapter.h           |   23 +
 drivers/staging/winbond/bss_f.h             |   59 +
 drivers/staging/winbond/bssdscpt.h          |  156 +
 drivers/staging/winbond/ds_tkip.h           |   33 +
 drivers/staging/winbond/gl_80211.h          |  125 +
 drivers/staging/winbond/ioctls.h            |  678 ++
 drivers/staging/winbond/linux/common.h      |  143 +
 drivers/staging/winbond/linux/sysdef.h      |   73 +
 drivers/staging/winbond/linux/wb35reg.c     |  747 ++
 drivers/staging/winbond/linux/wb35reg_f.h   |   56 +
 drivers/staging/winbond/linux/wb35reg_s.h   |  170 +
 drivers/staging/winbond/linux/wb35rx.c      |  337 +
 drivers/staging/winbond/linux/wb35rx_f.h    |   17 +
 drivers/staging/winbond/linux/wb35rx_s.h    |   48 +
 drivers/staging/winbond/linux/wb35tx.c      |  313 +
 drivers/staging/winbond/linux/wb35tx_f.h    |   20 +
 drivers/staging/winbond/linux/wb35tx_s.h    |   47 +
 drivers/staging/winbond/linux/wbusb.c       |  404 ++
 drivers/staging/winbond/linux/wbusb_f.h     |   34 +
 drivers/staging/winbond/linux/wbusb_s.h     |   42 +
 drivers/staging/winbond/localpara.h         |  275 +
 drivers/staging/winbond/mac_structures.h    |  670 ++
 drivers/staging/winbond/mds.c               |  630 ++
 drivers/staging/winbond/mds_f.h             |   33 +
 drivers/staging/winbond/mds_s.h             |  183 +
 drivers/staging/winbond/mlme_mib.h          |   84 +
 drivers/staging/winbond/mlme_s.h            |  195 +
 drivers/staging/winbond/mlmetxrx.c          |  150 +
 drivers/staging/winbond/mlmetxrx_f.h        |   52 +
 drivers/staging/winbond/mto.c               | 1229 ++++
 drivers/staging/winbond/mto.h               |  265 +
 drivers/staging/winbond/mto_f.h             |    7 +
 drivers/staging/winbond/os_common.h         |    2 +
 drivers/staging/winbond/phy_calibration.c   | 1759 +++++
 drivers/staging/winbond/phy_calibration.h   |  101 +
 drivers/staging/winbond/reg.c               | 2683 +++++++
 drivers/staging/winbond/rxisr.c             |   30 +
 drivers/staging/winbond/scan_s.h            |  115 +
 drivers/staging/winbond/sme_api.c           |   13 +
 drivers/staging/winbond/sme_api.h           |  265 +
 drivers/staging/winbond/sme_s.h             |  228 +
 drivers/staging/winbond/wb35_ver.h          |   30 +
 drivers/staging/winbond/wbhal.c             |  878 +++
 drivers/staging/winbond/wbhal_f.h           |  122 +
 drivers/staging/winbond/wbhal_s.h           |  615 ++
 drivers/staging/winbond/wblinux.c           |  277 +
 drivers/staging/winbond/wblinux_f.h         |   23 +
 drivers/staging/winbond/wblinux_s.h         |   45 +
 drivers/staging/wlan-ng/Kconfig             |   10 +
 drivers/staging/wlan-ng/Makefile            |    9 +
 drivers/staging/wlan-ng/README              |    8 +
 drivers/staging/wlan-ng/hfa384x.c           | 4018 +++++++++++
 drivers/staging/wlan-ng/hfa384x.h           | 3067 ++++++++
 drivers/staging/wlan-ng/hfa384x_usb.c       | 5027 ++++++++++++++
 drivers/staging/wlan-ng/p80211conv.c        |  683 ++
 drivers/staging/wlan-ng/p80211conv.h        |  186 +
 drivers/staging/wlan-ng/p80211hdr.h         |  299 +
 drivers/staging/wlan-ng/p80211ioctl.h       |  123 +
 drivers/staging/wlan-ng/p80211meta.h        |  169 +
 drivers/staging/wlan-ng/p80211metadef.h     | 2524 +++++++
 drivers/staging/wlan-ng/p80211metamib.h     |  105 +
 drivers/staging/wlan-ng/p80211metamsg.h     |  105 +
 drivers/staging/wlan-ng/p80211metastruct.h  |  644 ++
 drivers/staging/wlan-ng/p80211mgmt.h        |  575 ++
 drivers/staging/wlan-ng/p80211mod.c         |  216 +
 drivers/staging/wlan-ng/p80211msg.h         |  102 +
 drivers/staging/wlan-ng/p80211netdev.c      | 1502 ++++
 drivers/staging/wlan-ng/p80211netdev.h      |  336 +
 drivers/staging/wlan-ng/p80211req.c         |  329 +
 drivers/staging/wlan-ng/p80211req.h         |   68 +
 drivers/staging/wlan-ng/p80211types.h       |  675 ++
 drivers/staging/wlan-ng/p80211wep.c         |  317 +
 drivers/staging/wlan-ng/p80211wext.c        | 2048 ++++++
 drivers/staging/wlan-ng/prism2_cs.c         | 1487 ++++
 drivers/staging/wlan-ng/prism2_pci.c        |  332 +
 drivers/staging/wlan-ng/prism2_plx.c        |  472 ++
 drivers/staging/wlan-ng/prism2_usb.c        |  361 +
 drivers/staging/wlan-ng/prism2mgmt.c        | 2956 ++++++++
 drivers/staging/wlan-ng/prism2mgmt.h        |  182 +
 drivers/staging/wlan-ng/prism2mib.c         | 3799 ++++++++++
 drivers/staging/wlan-ng/prism2sta.c         | 2502 +++++++
 drivers/staging/wlan-ng/version.h           |   64 +
 drivers/staging/wlan-ng/wlan_compat.h       |  757 ++
 include/linux/kernel.h                      |    1 +
 kernel/module.c                             |   11 +
 kernel/panic.c                              |    6 +-
 scripts/mod/modpost.c                       |    9 +
 205 files changed, 146209 insertions(+), 2 deletions(-)
 create mode 100644 drivers/staging/Kconfig
 create mode 100644 drivers/staging/Makefile
 create mode 100644 drivers/staging/echo/Kconfig
 create mode 100644 drivers/staging/echo/Makefile
 create mode 100644 drivers/staging/echo/TODO
 create mode 100644 drivers/staging/echo/bit_operations.h
 create mode 100644 drivers/staging/echo/echo.c
 create mode 100644 drivers/staging/echo/echo.h
 create mode 100644 drivers/staging/echo/fir.h
 create mode 100644 drivers/staging/echo/mmx.h
 create mode 100644 drivers/staging/et131x/Kconfig
 create mode 100644 drivers/staging/et131x/Makefile
 create mode 100644 drivers/staging/et131x/README
 create mode 100644 drivers/staging/et131x/et1310_address_map.h
 create mode 100644 drivers/staging/et131x/et1310_eeprom.c
 create mode 100644 drivers/staging/et131x/et1310_eeprom.h
 create mode 100644 drivers/staging/et131x/et1310_jagcore.c
 create mode 100644 drivers/staging/et131x/et1310_jagcore.h
 create mode 100644 drivers/staging/et131x/et1310_mac.c
 create mode 100644 drivers/staging/et131x/et1310_mac.h
 create mode 100644 drivers/staging/et131x/et1310_phy.c
 create mode 100644 drivers/staging/et131x/et1310_phy.h
 create mode 100644 drivers/staging/et131x/et1310_pm.c
 create mode 100644 drivers/staging/et131x/et1310_pm.h
 create mode 100644 drivers/staging/et131x/et1310_rx.c
 create mode 100644 drivers/staging/et131x/et1310_rx.h
 create mode 100644 drivers/staging/et131x/et1310_tx.c
 create mode 100644 drivers/staging/et131x/et1310_tx.h
 create mode 100644 drivers/staging/et131x/et131x_adapter.h
 create mode 100644 drivers/staging/et131x/et131x_config.c
 create mode 100644 drivers/staging/et131x/et131x_config.h
 create mode 100644 drivers/staging/et131x/et131x_debug.c
 create mode 100644 drivers/staging/et131x/et131x_debug.h
 create mode 100644 drivers/staging/et131x/et131x_defs.h
 create mode 100644 drivers/staging/et131x/et131x_initpci.c
 create mode 100644 drivers/staging/et131x/et131x_initpci.h
 create mode 100644 drivers/staging/et131x/et131x_isr.c
 create mode 100644 drivers/staging/et131x/et131x_isr.h
 create mode 100644 drivers/staging/et131x/et131x_netdev.c
 create mode 100644 drivers/staging/et131x/et131x_netdev.h
 create mode 100644 drivers/staging/et131x/et131x_version.h
 create mode 100644 drivers/staging/go7007/Kconfig
 create mode 100644 drivers/staging/go7007/Makefile
 create mode 100644 drivers/staging/go7007/README
 create mode 100644 drivers/staging/go7007/go7007-driver.c
 create mode 100644 drivers/staging/go7007/go7007-fw.c
 create mode 100644 drivers/staging/go7007/go7007-i2c.c
 create mode 100644 drivers/staging/go7007/go7007-priv.h
 create mode 100644 drivers/staging/go7007/go7007-usb.c
 create mode 100644 drivers/staging/go7007/go7007-v4l2.c
 create mode 100644 drivers/staging/go7007/go7007.h
 create mode 100644 drivers/staging/go7007/saa7134-go7007.c
 create mode 100644 drivers/staging/go7007/snd-go7007.c
 create mode 100644 drivers/staging/go7007/wis-i2c.h
 create mode 100644 drivers/staging/go7007/wis-ov7640.c
 create mode 100644 drivers/staging/go7007/wis-saa7113.c
 create mode 100644 drivers/staging/go7007/wis-saa7115.c
 create mode 100644 drivers/staging/go7007/wis-sony-tuner.c
 create mode 100644 drivers/staging/go7007/wis-tw2804.c
 create mode 100644 drivers/staging/go7007/wis-tw9903.c
 create mode 100644 drivers/staging/go7007/wis-uda1342.c
 create mode 100644 drivers/staging/me4000/Kconfig
 create mode 100644 drivers/staging/me4000/Makefile
 create mode 100644 drivers/staging/me4000/README
 create mode 100644 drivers/staging/me4000/me4000.c
 create mode 100644 drivers/staging/me4000/me4000.h
 create mode 100644 drivers/staging/me4000/me4000_firmware.h
 create mode 100644 drivers/staging/me4000/me4610_firmware.h
 create mode 100644 drivers/staging/slicoss/Kconfig
 create mode 100644 drivers/staging/slicoss/Makefile
 create mode 100644 drivers/staging/slicoss/README
 create mode 100644 drivers/staging/slicoss/gbdownload.h
 create mode 100644 drivers/staging/slicoss/gbrcvucode.h
 create mode 100644 drivers/staging/slicoss/oasisdbgdownload.h
 create mode 100644 drivers/staging/slicoss/oasisdownload.h
 create mode 100644 drivers/staging/slicoss/oasisrcvucode.h
 create mode 100644 drivers/staging/slicoss/slic.h
 create mode 100644 drivers/staging/slicoss/slic_os.h
 create mode 100644 drivers/staging/slicoss/slicbuild.h
 create mode 100644 drivers/staging/slicoss/slicdbg.h
 create mode 100644 drivers/staging/slicoss/slicdump.h
 create mode 100644 drivers/staging/slicoss/slichw.h
 create mode 100644 drivers/staging/slicoss/slicinc.h
 create mode 100644 drivers/staging/slicoss/slicoss.c
 create mode 100644 drivers/staging/sxg/Kconfig
 create mode 100644 drivers/staging/sxg/Makefile
 create mode 100644 drivers/staging/sxg/README
 create mode 100644 drivers/staging/sxg/saharadbgdownload.h
 create mode 100644 drivers/staging/sxg/sxg.c
 create mode 100644 drivers/staging/sxg/sxg.h
 create mode 100644 drivers/staging/sxg/sxg_os.h
 create mode 100644 drivers/staging/sxg/sxgdbg.h
 create mode 100644 drivers/staging/sxg/sxghif.h
 create mode 100644 drivers/staging/sxg/sxghw.h
 create mode 100644 drivers/staging/sxg/sxgphycode.h
 create mode 100644 drivers/staging/usbip/Kconfig
 create mode 100644 drivers/staging/usbip/Makefile
 create mode 100644 drivers/staging/usbip/README
 create mode 100644 drivers/staging/usbip/stub.h
 create mode 100644 drivers/staging/usbip/stub_dev.c
 create mode 100644 drivers/staging/usbip/stub_main.c
 create mode 100644 drivers/staging/usbip/stub_rx.c
 create mode 100644 drivers/staging/usbip/stub_tx.c
 create mode 100644 drivers/staging/usbip/usbip_common.c
 create mode 100644 drivers/staging/usbip/usbip_common.h
 create mode 100644 drivers/staging/usbip/usbip_event.c
 create mode 100644 drivers/staging/usbip/vhci.h
 create mode 100644 drivers/staging/usbip/vhci_hcd.c
 create mode 100644 drivers/staging/usbip/vhci_rx.c
 create mode 100644 drivers/staging/usbip/vhci_sysfs.c
 create mode 100644 drivers/staging/usbip/vhci_tx.c
 create mode 100644 drivers/staging/winbond/Kconfig
 create mode 100644 drivers/staging/winbond/Makefile
 create mode 100644 drivers/staging/winbond/README
 create mode 100644 drivers/staging/winbond/adapter.h
 create mode 100644 drivers/staging/winbond/bss_f.h
 create mode 100644 drivers/staging/winbond/bssdscpt.h
 create mode 100644 drivers/staging/winbond/ds_tkip.h
 create mode 100644 drivers/staging/winbond/gl_80211.h
 create mode 100644 drivers/staging/winbond/ioctls.h
 create mode 100644 drivers/staging/winbond/linux/common.h
 create mode 100644 drivers/staging/winbond/linux/sysdef.h
 create mode 100644 drivers/staging/winbond/linux/wb35reg.c
 create mode 100644 drivers/staging/winbond/linux/wb35reg_f.h
 create mode 100644 drivers/staging/winbond/linux/wb35reg_s.h
 create mode 100644 drivers/staging/winbond/linux/wb35rx.c
 create mode 100644 drivers/staging/winbond/linux/wb35rx_f.h
 create mode 100644 drivers/staging/winbond/linux/wb35rx_s.h
 create mode 100644 drivers/staging/winbond/linux/wb35tx.c
 create mode 100644 drivers/staging/winbond/linux/wb35tx_f.h
 create mode 100644 drivers/staging/winbond/linux/wb35tx_s.h
 create mode 100644 drivers/staging/winbond/linux/wbusb.c
 create mode 100644 drivers/staging/winbond/linux/wbusb_f.h
 create mode 100644 drivers/staging/winbond/linux/wbusb_s.h
 create mode 100644 drivers/staging/winbond/localpara.h
 create mode 100644 drivers/staging/winbond/mac_structures.h
 create mode 100644 drivers/staging/winbond/mds.c
 create mode 100644 drivers/staging/winbond/mds_f.h
 create mode 100644 drivers/staging/winbond/mds_s.h
 create mode 100644 drivers/staging/winbond/mlme_mib.h
 create mode 100644 drivers/staging/winbond/mlme_s.h
 create mode 100644 drivers/staging/winbond/mlmetxrx.c
 create mode 100644 drivers/staging/winbond/mlmetxrx_f.h
 create mode 100644 drivers/staging/winbond/mto.c
 create mode 100644 drivers/staging/winbond/mto.h
 create mode 100644 drivers/staging/winbond/mto_f.h
 create mode 100644 drivers/staging/winbond/os_common.h
 create mode 100644 drivers/staging/winbond/phy_calibration.c
 create mode 100644 drivers/staging/winbond/phy_calibration.h
 create mode 100644 drivers/staging/winbond/reg.c
 create mode 100644 drivers/staging/winbond/rxisr.c
 create mode 100644 drivers/staging/winbond/scan_s.h
 create mode 100644 drivers/staging/winbond/sme_api.c
 create mode 100644 drivers/staging/winbond/sme_api.h
 create mode 100644 drivers/staging/winbond/sme_s.h
 create mode 100644 drivers/staging/winbond/wb35_ver.h
 create mode 100644 drivers/staging/winbond/wbhal.c
 create mode 100644 drivers/staging/winbond/wbhal_f.h
 create mode 100644 drivers/staging/winbond/wbhal_s.h
 create mode 100644 drivers/staging/winbond/wblinux.c
 create mode 100644 drivers/staging/winbond/wblinux_f.h
 create mode 100644 drivers/staging/winbond/wblinux_s.h
 create mode 100644 drivers/staging/wlan-ng/Kconfig
 create mode 100644 drivers/staging/wlan-ng/Makefile
 create mode 100644 drivers/staging/wlan-ng/README
 create mode 100644 drivers/staging/wlan-ng/hfa384x.c
 create mode 100644 drivers/staging/wlan-ng/hfa384x.h
 create mode 100644 drivers/staging/wlan-ng/hfa384x_usb.c
 create mode 100644 drivers/staging/wlan-ng/p80211conv.c
 create mode 100644 drivers/staging/wlan-ng/p80211conv.h
 create mode 100644 drivers/staging/wlan-ng/p80211hdr.h
 create mode 100644 drivers/staging/wlan-ng/p80211ioctl.h
 create mode 100644 drivers/staging/wlan-ng/p80211meta.h
 create mode 100644 drivers/staging/wlan-ng/p80211metadef.h
 create mode 100644 drivers/staging/wlan-ng/p80211metamib.h
 create mode 100644 drivers/staging/wlan-ng/p80211metamsg.h
 create mode 100644 drivers/staging/wlan-ng/p80211metastruct.h
 create mode 100644 drivers/staging/wlan-ng/p80211mgmt.h
 create mode 100644 drivers/staging/wlan-ng/p80211mod.c
 create mode 100644 drivers/staging/wlan-ng/p80211msg.h
 create mode 100644 drivers/staging/wlan-ng/p80211netdev.c
 create mode 100644 drivers/staging/wlan-ng/p80211netdev.h
 create mode 100644 drivers/staging/wlan-ng/p80211req.c
 create mode 100644 drivers/staging/wlan-ng/p80211req.h
 create mode 100644 drivers/staging/wlan-ng/p80211types.h
 create mode 100644 drivers/staging/wlan-ng/p80211wep.c
 create mode 100644 drivers/staging/wlan-ng/p80211wext.c
 create mode 100644 drivers/staging/wlan-ng/prism2_cs.c
 create mode 100644 drivers/staging/wlan-ng/prism2_pci.c
 create mode 100644 drivers/staging/wlan-ng/prism2_plx.c
 create mode 100644 drivers/staging/wlan-ng/prism2_usb.c
 create mode 100644 drivers/staging/wlan-ng/prism2mgmt.c
 create mode 100644 drivers/staging/wlan-ng/prism2mgmt.h
 create mode 100644 drivers/staging/wlan-ng/prism2mib.c
 create mode 100644 drivers/staging/wlan-ng/prism2sta.c
 create mode 100644 drivers/staging/wlan-ng/version.h
 create mode 100644 drivers/staging/wlan-ng/wlan_compat.h

---------------

David Rowe (1):
      Staging: add echo cancelation module

Greg Kroah-Hartman (11):
      Staging: add TAINT_CRAP for all drivers/staging code
      Staging: add TAINT_CRAP flag to drivers/staging modules
      Staging: add Kconfig entries and Makefile infrastructure
      Staging: add MAINTAINERS entry
      Staging: add et131x network driver
      Staging: add Alacritech slicoss network driver
      Staging: add sxg network driver
      Staging: add me4000 firmware files
      Staging: add me4000 pci data collection driver
      Staging: add the go7007 video driver
      Staging: add wlan-ng prism2 usb driver

J.R. Mauro (2):
      Staging: Fix gcc warnings in sxg
      Staging: Lindent sxg.c

Lior Dotan (4):
      Staging: SLICOSS: lots of checkpatch fixes
      Staging: SLICOSS: Fix warnings due to static usage
      Staging: SLICOSS: Fix remaining type names
      Staging: SLICOSS: Call pci_release_regions at driver exit

Pavel Machek (1):
      Staging: add w35und wifi driver

Ross Cohen (1):
      Staging: go7007 v4l fixes

Takahiro Hirofuchi (3):
      Staging: USB/IP: add common functions needed
      Staging: USB/IP: add client driver
      Staging: USB/IP: add host driver


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 01/23] Staging: add TAINT_CRAP for all drivers/staging code
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-10 22:42 ` [PATCH 02/23] Staging: add TAINT_CRAP flag to drivers/staging modules Greg KH
                   ` (18 subsequent siblings)
  19 siblings, 0 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: Greg Kroah-Hartman, Andreas Gruenbacher, Jeff Mahoney

From: Greg Kroah-Hartman <gregkh@suse.de>

We need to add a flag for all code that is in the drivers/staging/
directory to prevent all other kernel developers from worrying about
issues here, and to notify users that the drivers might not be as good
as they are normally used to.

Based on code from Andreas Gruenbacher and Jeff Mahoney to provide a
TAINT flag for the support level of a kernel module in the Novell
enterprise kernel release.

This is the kernel portion of this feature, the ability for the flag to
be set needs to be done in the build process and will happen in a
follow-up patch.

Cc: Andreas Gruenbacher <agruen@suse.de>
Cc: Jeff Mahoney <jeffm@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 Documentation/sysctl/kernel.txt |    1 +
 include/linux/kernel.h          |    1 +
 kernel/module.c                 |   11 +++++++++++
 kernel/panic.c                  |    6 ++++--
 4 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/Documentation/sysctl/kernel.txt b/Documentation/sysctl/kernel.txt
index e1ff0d9..bde799e 100644
--- a/Documentation/sysctl/kernel.txt
+++ b/Documentation/sysctl/kernel.txt
@@ -369,4 +369,5 @@ can be ORed together:
   2 - A module was force loaded by insmod -f.
       Set by modutils >= 2.4.9 and module-init-tools.
   4 - Unsafe SMP processors: SMP with CPUs not designed for SMP.
+ 64 - A module from drivers/staging was loaded.
 
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 2651f80..b36805c 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -260,6 +260,7 @@ extern enum system_states {
 #define TAINT_DIE			(1<<7)
 #define TAINT_OVERRIDDEN_ACPI_TABLE	(1<<8)
 #define TAINT_WARN			(1<<9)
+#define TAINT_CRAP			(1<<10)
 
 extern void dump_stack(void) __cold;
 
diff --git a/kernel/module.c b/kernel/module.c
index 9db1191..152b165 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -1806,6 +1806,7 @@ static noinline struct module *load_module(void __user *umod,
 	Elf_Ehdr *hdr;
 	Elf_Shdr *sechdrs;
 	char *secstrings, *args, *modmagic, *strtab = NULL;
+	char *staging;
 	unsigned int i;
 	unsigned int symindex = 0;
 	unsigned int strindex = 0;
@@ -1960,6 +1961,14 @@ static noinline struct module *load_module(void __user *umod,
 		goto free_hdr;
 	}
 
+	staging = get_modinfo(sechdrs, infoindex, "staging");
+	if (staging) {
+		add_taint_module(mod, TAINT_CRAP);
+		printk(KERN_WARNING "%s: module is from the staging directory,"
+		       " the quality is unknown, you have been warned.\n",
+		       mod->name);
+	}
+
 	/* Now copy in args */
 	args = strndup_user(uargs, ~0UL >> 1);
 	if (IS_ERR(args)) {
@@ -2556,6 +2565,8 @@ static char *module_flags(struct module *mod, char *buf)
 			buf[bx++] = 'P';
 		if (mod->taints & TAINT_FORCED_MODULE)
 			buf[bx++] = 'F';
+		if (mod->taints & TAINT_CRAP)
+			buf[bx++] = 'C';
 		/*
 		 * TAINT_FORCED_RMMOD: could be added.
 		 * TAINT_UNSAFE_SMP, TAINT_MACHINE_CHECK, TAINT_BAD_PAGE don't
diff --git a/kernel/panic.c b/kernel/panic.c
index 12c5a0a..98e2047 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -155,6 +155,7 @@ EXPORT_SYMBOL(panic);
  *  'U' - Userspace-defined naughtiness.
  *  'A' - ACPI table overridden.
  *  'W' - Taint on warning.
+ *  'C' - modules from drivers/staging are loaded.
  *
  *	The string is overwritten by the next call to print_taint().
  */
@@ -163,7 +164,7 @@ const char *print_tainted(void)
 {
 	static char buf[20];
 	if (tainted) {
-		snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c%c%c%c",
+		snprintf(buf, sizeof(buf), "Tainted: %c%c%c%c%c%c%c%c%c%c%c",
 			tainted & TAINT_PROPRIETARY_MODULE ? 'P' : 'G',
 			tainted & TAINT_FORCED_MODULE ? 'F' : ' ',
 			tainted & TAINT_UNSAFE_SMP ? 'S' : ' ',
@@ -173,7 +174,8 @@ const char *print_tainted(void)
 			tainted & TAINT_USER ? 'U' : ' ',
 			tainted & TAINT_DIE ? 'D' : ' ',
 			tainted & TAINT_OVERRIDDEN_ACPI_TABLE ? 'A' : ' ',
-			tainted & TAINT_WARN ? 'W' : ' ');
+			tainted & TAINT_WARN ? 'W' : ' ',
+			tainted & TAINT_CRAP ? 'C' : ' ');
 	}
 	else
 		snprintf(buf, sizeof(buf), "Not tainted");
-- 
1.6.0.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 02/23] Staging: add TAINT_CRAP flag to drivers/staging modules
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
  2008-10-10 22:42 ` [PATCH 01/23] Staging: add TAINT_CRAP for all drivers/staging code Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-10 22:42 ` [PATCH 03/23] Staging: add Kconfig entries and Makefile infrastructure Greg KH
                   ` (17 subsequent siblings)
  19 siblings, 0 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: Greg Kroah-Hartman, Andreas Gruenbacher, Jeff Mahoney

From: Greg Kroah-Hartman <gregkh@suse.de>

We need to add a flag for all code that is in the drivers/staging/
directory to prevent all other kernel developers from worrying about
issues here, and to notify users that the drivers might not be as good
as they are normally used to.

Based on code from Andreas Gruenbacher and Jeff Mahoney to provide a
TAINT flag for the support level of a kernel module in the Novell
enterprise kernel release.

This is the code that actually modifies the modules, adding the flag to
any files in the drivers/staging directory.

Cc: Andreas Gruenbacher <agruen@suse.de>
Cc: Jeff Mahoney <jeffm@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 scripts/mod/modpost.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index 8e0de6a..8892161 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -1726,6 +1726,14 @@ static void add_header(struct buffer *b, struct module *mod)
 	buf_printf(b, "};\n");
 }
 
+void add_staging_flag(struct buffer *b, const char *name)
+{
+	static const char *staging_dir = "drivers/staging";
+
+	if (strncmp(staging_dir, name, strlen(staging_dir)) == 0)
+		buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
+}
+
 /**
  * Record CRCs for unresolved symbols
  **/
@@ -2135,6 +2143,7 @@ int main(int argc, char **argv)
 		buf.pos = 0;
 
 		add_header(&buf, mod);
+		add_staging_flag(&buf, mod->name);
 		err |= add_versions(&buf, mod);
 		add_depends(&buf, mod, modules);
 		add_moddevtable(&buf, mod);
-- 
1.6.0.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 03/23] Staging: add Kconfig entries and Makefile infrastructure
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
  2008-10-10 22:42 ` [PATCH 01/23] Staging: add TAINT_CRAP for all drivers/staging code Greg KH
  2008-10-10 22:42 ` [PATCH 02/23] Staging: add TAINT_CRAP flag to drivers/staging modules Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-10 22:42 ` [PATCH 04/23] Staging: add MAINTAINERS entry Greg KH
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: Greg Kroah-Hartman

From: Greg Kroah-Hartman <gregkh@suse.de>

This hooks up the drivers/staging directory to the build system

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/Kconfig          |    2 ++
 drivers/Makefile         |    1 +
 drivers/staging/Kconfig  |   27 +++++++++++++++++++++++++++
 drivers/staging/Makefile |    2 ++
 4 files changed, 32 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/Kconfig
 create mode 100644 drivers/staging/Makefile

diff --git a/drivers/Kconfig b/drivers/Kconfig
index 59f33fa..d19b6f5 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -101,4 +101,6 @@ source "drivers/auxdisplay/Kconfig"
 source "drivers/uio/Kconfig"
 
 source "drivers/xen/Kconfig"
+
+source "drivers/staging/Kconfig"
 endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 2735bde..46c8681 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -99,3 +99,4 @@ obj-$(CONFIG_OF)		+= of/
 obj-$(CONFIG_SSB)		+= ssb/
 obj-$(CONFIG_VIRTIO)		+= virtio/
 obj-$(CONFIG_REGULATOR)		+= regulator/
+obj-$(CONFIG_STAGING)		+= staging/
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
new file mode 100644
index 0000000..84832fe
--- /dev/null
+++ b/drivers/staging/Kconfig
@@ -0,0 +1,27 @@
+menuconfig STAGING
+	bool "Staging drivers"
+	default n
+	---help---
+	  This option allows you to select a number of drivers that are
+	  not of the "normal" Linux kernel quality level.  These drivers
+	  are placed here in order to get a wider audience for use of
+	  them.  Please note that these drivers are under heavy
+	  development, may or may not work, and may contain userspace
+	  interfaces that most likely will be changed in the near
+	  future.
+
+	  Using any of these drivers will taint your kernel which might
+	  affect support options from both the community, and various
+	  commercial support orginizations.
+
+	  If you wish to work on these drivers, to help improve them, or
+	  to report problems you have with them, please see the
+	  driver_name.README file in the drivers/staging/ directory to
+	  see what needs to be worked on, and who to contact.
+
+	  If in doubt, say N here.
+
+if STAGING
+
+
+endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
new file mode 100644
index 0000000..ceb0328
--- /dev/null
+++ b/drivers/staging/Makefile
@@ -0,0 +1,2 @@
+# Makefile for staging directory
+
-- 
1.6.0.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 04/23] Staging: add MAINTAINERS entry
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
                   ` (2 preceding siblings ...)
  2008-10-10 22:42 ` [PATCH 03/23] Staging: add Kconfig entries and Makefile infrastructure Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-10 22:42 ` [PATCH 05/23] Staging: add et131x network driver Greg KH
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: Greg Kroah-Hartman

From: Greg Kroah-Hartman <gregkh@suse.de>

Someone has to claim this mess, might as well let everyone know who to
blame.

Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 MAINTAINERS |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a03bd5..ea6b478 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3863,6 +3863,13 @@ M:	chrisw@sous-sol.org
 L:	stable@kernel.org
 S:	Maintained
 
+STAGING SUBSYSTEM:
+P:	Greg Kroah-Hartman
+M:	gregkh@suse.de
+L:	linux-kernel@vger.kernel.org
+T:	quilt kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
+S:	Maintained
+
 SHARP LH SUPPORT (LH7952X & LH7A40X)
 P:	Marc Singer
 M:	elf@buici.com
-- 
1.6.0.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 05/23] Staging: add et131x network driver
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
                   ` (3 preceding siblings ...)
  2008-10-10 22:42 ` [PATCH 04/23] Staging: add MAINTAINERS entry Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-10 22:42 ` [PATCH 09/23] Staging: add me4000 pci data collection driver Greg KH
                   ` (14 subsequent siblings)
  19 siblings, 0 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel
  Cc: Greg Kroah-Hartman, Olaf Hartmann, Christoph Hellwig, Dean Adams,
	Victor Soriano, Andre-Sebastian Liebe

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 451124 bytes --]

From: Greg Kroah-Hartman <gregkh@suse.de>

This is a driver for the ET1310 network device.

Based on the driver found at https://sourceforge.net/projects/et131x/

Cleaned up immensely by Olaf Hartman <o.hartmann@telovital.com> and Christoph
Hellwig <hch@infradead.org>

Note, the powermanagement options were removed from the vendor provided
driver as they did not build properly at the time.

TODO:
	- kernel coding style cleanups
	- forward port for latest network driver changes
	- kill useless typecasts (e.g. in et1310_phy.c)
	- alloc_etherdev is initializing memory with zero?!?
	- add_timer call in et131x_netdev.c is correct?
	- Add power saving functionality (suspend, sleep, resume)
	- Implement a few more kernel Parameter (set mac )

Cc: Olaf Hartmann <o.hartmann@telovital.com>
Cc: Christoph Hellwig <hch@infradead.org>
Cc: Dean Adams <dadams1969@gmail.com>
Cc: Victor Soriano <vjsoriano@agere.com>
Cc: Andre-Sebastian Liebe <andre@lianse.eu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/staging/Kconfig                     |    1 +
 drivers/staging/Makefile                    |    1 +
 drivers/staging/et131x/Kconfig              |   18 +
 drivers/staging/et131x/Makefile             |   18 +
 drivers/staging/et131x/README               |   25 +
 drivers/staging/et131x/et1310_address_map.h | 2399 +++++++++++++++++++++++++++
 drivers/staging/et131x/et1310_eeprom.c      |  480 ++++++
 drivers/staging/et131x/et1310_eeprom.h      |   89 +
 drivers/staging/et131x/et1310_jagcore.c     |  220 +++
 drivers/staging/et131x/et1310_jagcore.h     |  112 ++
 drivers/staging/et131x/et1310_mac.c         |  792 +++++++++
 drivers/staging/et131x/et1310_mac.h         |   93 +
 drivers/staging/et131x/et1310_phy.c         | 1281 ++++++++++++++
 drivers/staging/et131x/et1310_phy.h         |  910 ++++++++++
 drivers/staging/et131x/et1310_pm.c          |  207 +++
 drivers/staging/et131x/et1310_pm.h          |  125 ++
 drivers/staging/et131x/et1310_rx.c          | 1391 ++++++++++++++++
 drivers/staging/et131x/et1310_rx.h          |  373 +++++
 drivers/staging/et131x/et1310_tx.c          | 1525 +++++++++++++++++
 drivers/staging/et131x/et1310_tx.h          |  242 +++
 drivers/staging/et131x/et131x_adapter.h     |  347 ++++
 drivers/staging/et131x/et131x_config.c      |  325 ++++
 drivers/staging/et131x/et131x_config.h      |   67 +
 drivers/staging/et131x/et131x_debug.c       |  218 +++
 drivers/staging/et131x/et131x_debug.h       |  201 +++
 drivers/staging/et131x/et131x_defs.h        |  128 ++
 drivers/staging/et131x/et131x_initpci.c     | 1046 ++++++++++++
 drivers/staging/et131x/et131x_initpci.h     |   73 +
 drivers/staging/et131x/et131x_isr.c         |  488 ++++++
 drivers/staging/et131x/et131x_isr.h         |   65 +
 drivers/staging/et131x/et131x_netdev.c      |  856 ++++++++++
 drivers/staging/et131x/et131x_netdev.h      |   64 +
 drivers/staging/et131x/et131x_version.h     |   81 +
 33 files changed, 14261 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/et131x/Kconfig
 create mode 100644 drivers/staging/et131x/Makefile
 create mode 100644 drivers/staging/et131x/README
 create mode 100644 drivers/staging/et131x/et1310_address_map.h
 create mode 100644 drivers/staging/et131x/et1310_eeprom.c
 create mode 100644 drivers/staging/et131x/et1310_eeprom.h
 create mode 100644 drivers/staging/et131x/et1310_jagcore.c
 create mode 100644 drivers/staging/et131x/et1310_jagcore.h
 create mode 100644 drivers/staging/et131x/et1310_mac.c
 create mode 100644 drivers/staging/et131x/et1310_mac.h
 create mode 100644 drivers/staging/et131x/et1310_phy.c
 create mode 100644 drivers/staging/et131x/et1310_phy.h
 create mode 100644 drivers/staging/et131x/et1310_pm.c
 create mode 100644 drivers/staging/et131x/et1310_pm.h
 create mode 100644 drivers/staging/et131x/et1310_rx.c
 create mode 100644 drivers/staging/et131x/et1310_rx.h
 create mode 100644 drivers/staging/et131x/et1310_tx.c
 create mode 100644 drivers/staging/et131x/et1310_tx.h
 create mode 100644 drivers/staging/et131x/et131x_adapter.h
 create mode 100644 drivers/staging/et131x/et131x_config.c
 create mode 100644 drivers/staging/et131x/et131x_config.h
 create mode 100644 drivers/staging/et131x/et131x_debug.c
 create mode 100644 drivers/staging/et131x/et131x_debug.h
 create mode 100644 drivers/staging/et131x/et131x_defs.h
 create mode 100644 drivers/staging/et131x/et131x_initpci.c
 create mode 100644 drivers/staging/et131x/et131x_initpci.h
 create mode 100644 drivers/staging/et131x/et131x_isr.c
 create mode 100644 drivers/staging/et131x/et131x_isr.h
 create mode 100644 drivers/staging/et131x/et131x_netdev.c
 create mode 100644 drivers/staging/et131x/et131x_netdev.h
 create mode 100644 drivers/staging/et131x/et131x_version.h

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 84832fe..4c3789d 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -23,5 +23,6 @@ menuconfig STAGING
 
 if STAGING
 
+source "drivers/staging/et131x/Kconfig"
 
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index ceb0328..933b984 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -1,2 +1,3 @@
 # Makefile for staging directory
 
+obj-$(CONFIG_ET131X)		+= et131x/
diff --git a/drivers/staging/et131x/Kconfig b/drivers/staging/et131x/Kconfig
new file mode 100644
index 0000000..e11cf34
--- /dev/null
+++ b/drivers/staging/et131x/Kconfig
@@ -0,0 +1,18 @@
+config ET131X
+	tristate "Agere ET-1310 Gigabit Ethernet support"
+	depends on NETDEV_1000 && PCI
+	default n
+	---help---
+	  This driver supports Agere ET-1310 ethernet adapters.
+
+	  To compile this driver as a module, choose M here. The module
+	  will be called et131x.
+
+config ET131X_DEBUG
+	bool "Enable et131x debugging"
+	depends on ET131X
+	default n
+	---help---
+	  Say Y for detailed debug information.
+
+	  If in doubt, say N.
diff --git a/drivers/staging/et131x/Makefile b/drivers/staging/et131x/Makefile
new file mode 100644
index 0000000..3ad571d
--- /dev/null
+++ b/drivers/staging/et131x/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for the Agere ET-131x ethernet driver
+#
+
+obj-$(CONFIG_ET131X) += et131x.o
+
+et131x-objs :=	et1310_eeprom.o \
+		et1310_jagcore.o \
+		et1310_mac.o \
+		et1310_phy.o \
+		et1310_pm.o \
+		et1310_rx.o \
+		et1310_tx.o \
+		et131x_config.o \
+		et131x_debug.o \
+		et131x_initpci.o \
+		et131x_isr.o \
+		et131x_netdev.o
diff --git a/drivers/staging/et131x/README b/drivers/staging/et131x/README
new file mode 100644
index 0000000..28752a5
--- /dev/null
+++ b/drivers/staging/et131x/README
@@ -0,0 +1,25 @@
+This is a driver for the ET1310 network device.
+
+Based on the driver found at https://sourceforge.net/projects/et131x/
+
+Cleaned up immensely by Olaf Hartman <o.hartmann@telovital.com> and Christoph
+Hellwig <hch@infradead.org>
+
+Note, the powermanagement options were removed from the vendor provided
+driver as they did not build properly at the time.
+
+TODO:
+	- kernel coding style cleanups
+	- forward port for latest network driver changes
+	- kill useless typecasts (e.g. in et1310_phy.c)
+	- alloc_etherdev is initializing memory with zero?!?
+	- add_timer call in et131x_netdev.c is correct?
+	- Add power saving functionality (suspend, sleep, resume)
+	- Implement a few more kernel Parameter (set mac )
+
+Please send patches to:
+	Greg Kroah-Hartman <gregkh@suse.de>
+
+And Cc: Olaf Hartmann <o.hartmann@telovital.com> as he has this device and can
+test any changes.
+
diff --git a/drivers/staging/et131x/et1310_address_map.h b/drivers/staging/et131x/et1310_address_map.h
new file mode 100644
index 0000000..3c85999
--- /dev/null
+++ b/drivers/staging/et131x/et1310_address_map.h
@@ -0,0 +1,2399 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_address_map.h - Contains the register mapping for the ET1310
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef _ET1310_ADDRESS_MAP_H_
+#define _ET1310_ADDRESS_MAP_H_
+
+
+/* START OF GLOBAL REGISTER ADDRESS MAP */
+
+typedef union _Q_ADDR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:22;	// bits 10-31
+		u32 addr:10;	// bits 0-9
+#else
+		u32 addr:10;	// bits 0-9
+		u32 unused:22;	// bits 10-31
+#endif
+	} bits;
+} Q_ADDR_t, *PQ_ADDR_t;
+
+/*
+ * structure for tx queue start address reg in global address map
+ * located at address 0x0000
+ * Defined earlier (Q_ADDR_t)
+ */
+
+/*
+ * structure for tx queue end address reg in global address map
+ * located at address 0x0004
+ * Defined earlier (Q_ADDR_t)
+ */
+
+/*
+ * structure for rx queue start address reg in global address map
+ * located at address 0x0008
+ * Defined earlier (Q_ADDR_t)
+ */
+
+/*
+ * structure for rx queue end address reg in global address map
+ * located at address 0x000C
+ * Defined earlier (Q_ADDR_t)
+ */
+
+/*
+ * structure for power management control status reg in global address map
+ * located at address 0x0010
+ */
+typedef union _PM_CSR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:22;		// bits 10-31
+		u32 pm_jagcore_rx_rdy:1;	// bit 9
+		u32 pm_jagcore_tx_rdy:1;	// bit 8
+		u32 pm_phy_lped_en:1;	// bit 7
+		u32 pm_phy_sw_coma:1;	// bit 6
+		u32 pm_rxclk_gate:1;	// bit 5
+		u32 pm_txclk_gate:1;	// bit 4
+		u32 pm_sysclk_gate:1;	// bit 3
+		u32 pm_jagcore_rx_en:1;	// bit 2
+		u32 pm_jagcore_tx_en:1;	// bit 1
+		u32 pm_gigephy_en:1;	// bit 0
+#else
+		u32 pm_gigephy_en:1;	// bit 0
+		u32 pm_jagcore_tx_en:1;	// bit 1
+		u32 pm_jagcore_rx_en:1;	// bit 2
+		u32 pm_sysclk_gate:1;	// bit 3
+		u32 pm_txclk_gate:1;	// bit 4
+		u32 pm_rxclk_gate:1;	// bit 5
+		u32 pm_phy_sw_coma:1;	// bit 6
+		u32 pm_phy_lped_en:1;	// bit 7
+		u32 pm_jagcore_tx_rdy:1;	// bit 8
+		u32 pm_jagcore_rx_rdy:1;	// bit 9
+		u32 unused:22;		// bits 10-31
+#endif
+	} bits;
+} PM_CSR_t, *PPM_CSR_t;
+
+/*
+ * structure for interrupt status reg in global address map
+ * located at address 0x0018
+ */
+typedef union _INTERRUPT_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused5:11;			// bits 21-31
+		u32 slv_timeout:1;			// bit 20
+		u32 mac_stat_interrupt:1;		// bit 19
+		u32 rxmac_interrupt:1;		// bit 18
+		u32 txmac_interrupt:1;		// bit 17
+		u32 phy_interrupt:1;		// bit 16
+		u32 wake_on_lan:1;			// bit 15
+		u32 watchdog_interrupt:1;		// bit 14
+		u32 unused4:4;			// bits 10-13
+		u32 rxdma_err:1;			// bit 9
+		u32 rxdma_pkt_stat_ring_low:1;	// bit 8
+		u32 rxdma_fb_ring1_low:1;		// bit 7
+		u32 rxdma_fb_ring0_low:1;		// bit 6
+		u32 rxdma_xfr_done:1;		// bit 5
+		u32 txdma_err:1;			// bit 4
+		u32 txdma_isr:1;			// bit 3
+		u32 unused3:1;			// bit 2
+		u32 unused2:1;			// bit 1
+		u32 unused1:1;			// bit 0
+#else
+		u32 unused1:1;			// bit 0
+		u32 unused2:1;			// bit 1
+		u32 unused3:1;			// bit 2
+		u32 txdma_isr:1;			// bit 3
+		u32 txdma_err:1;			// bit 4
+		u32 rxdma_xfr_done:1;		// bit 5
+		u32 rxdma_fb_ring0_low:1;		// bit 6
+		u32 rxdma_fb_ring1_low:1;		// bit 7
+		u32 rxdma_pkt_stat_ring_low:1;	// bit 8
+		u32 rxdma_err:1;			// bit 9
+		u32 unused4:4;			// bits 10-13
+		u32 watchdog_interrupt:1;		// bit 14
+		u32 wake_on_lan:1;			// bit 15
+		u32 phy_interrupt:1;		// bit 16
+		u32 txmac_interrupt:1;		// bit 17
+		u32 rxmac_interrupt:1;		// bit 18
+		u32 mac_stat_interrupt:1;		// bit 19
+		u32 slv_timeout:1;			// bit 20
+		u32 unused5:11;			// bits 21-31
+#endif
+	} bits;
+} INTERRUPT_t, *PINTERRUPT_t;
+
+/*
+ * structure for interrupt mask reg in global address map
+ * located at address 0x001C
+ * Defined earlier (INTERRUPT_t), but 'watchdog_interrupt' is not used.
+ */
+
+/*
+ * structure for interrupt alias clear mask reg in global address map
+ * located at address 0x0020
+ * Defined earlier (INTERRUPT_t)
+ */
+
+/*
+ * structure for interrupt status alias reg in global address map
+ * located at address 0x0024
+ * Defined earlier (INTERRUPT_t)
+ */
+
+/*
+ * structure for software reset reg in global address map
+ * located at address 0x0028
+ */
+typedef union _SW_RESET_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 selfclr_disable:1;	// bit 31
+		u32 unused:24;		// bits 7-30
+		u32 mmc_sw_reset:1;	// bit 6
+		u32 mac_stat_sw_reset:1;	// bit 5
+		u32 mac_sw_reset:1;	// bit 4
+		u32 rxmac_sw_reset:1;	// bit 3
+		u32 txmac_sw_reset:1;	// bit 2
+		u32 rxdma_sw_reset:1;	// bit 1
+		u32 txdma_sw_reset:1;	// bit 0
+#else
+		u32 txdma_sw_reset:1;	// bit 0
+		u32 rxdma_sw_reset:1;	// bit 1
+		u32 txmac_sw_reset:1;	// bit 2
+		u32 rxmac_sw_reset:1;	// bit 3
+		u32 mac_sw_reset:1;	// bit 4
+		u32 mac_stat_sw_reset:1;	// bit 5
+		u32 mmc_sw_reset:1;	// bit 6
+		u32 unused:24;		// bits 7-30
+		u32 selfclr_disable:1;	// bit 31
+#endif
+	} bits;
+} SW_RESET_t, *PSW_RESET_t;
+
+/*
+ * structure for SLV Timer reg in global address map
+ * located at address 0x002C
+ */
+typedef union _SLV_TIMER_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:8;	// bits 24-31
+		u32 timer_ini:24;	// bits 0-23
+#else
+		u32 timer_ini:24;	// bits 0-23
+		u32 unused:8;	// bits 24-31
+#endif
+	} bits;
+} SLV_TIMER_t, *PSLV_TIMER_t;
+
+/*
+ * structure for MSI Configuration reg in global address map
+ * located at address 0x0030
+ */
+typedef union _MSI_CONFIG_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused1:13;	// bits 19-31
+		u32 msi_tc:3;	// bits 16-18
+		u32 unused2:11;	// bits 5-15
+		u32 msi_vector:5;	// bits 0-4
+#else
+		u32 msi_vector:5;	// bits 0-4
+		u32 unused2:11;	// bits 5-15
+		u32 msi_tc:3;	// bits 16-18
+		u32 unused1:13;	// bits 19-31
+#endif
+	} bits;
+} MSI_CONFIG_t, *PMSI_CONFIG_t;
+
+/*
+ * structure for Loopback reg in global address map
+ * located at address 0x0034
+ */
+typedef union _LOOPBACK_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:30;		// bits 2-31
+		u32 dma_loopback:1;	// bit 1
+		u32 mac_loopback:1;	// bit 0
+#else
+		u32 mac_loopback:1;	// bit 0
+		u32 dma_loopback:1;	// bit 1
+		u32 unused:30;		// bits 2-31
+#endif
+	} bits;
+} LOOPBACK_t, *PLOOPBACK_t;
+
+/*
+ * GLOBAL Module of JAGCore Address Mapping
+ * Located at address 0x0000
+ */
+typedef struct _GLOBAL_t {			// Location:
+	Q_ADDR_t txq_start_addr;		//  0x0000
+	Q_ADDR_t txq_end_addr;			//  0x0004
+	Q_ADDR_t rxq_start_addr;		//  0x0008
+	Q_ADDR_t rxq_end_addr;			//  0x000C
+	PM_CSR_t pm_csr;			//  0x0010
+	u32 unused;				//  0x0014
+	INTERRUPT_t int_status;			//  0x0018
+	INTERRUPT_t int_mask;			//  0x001C
+	INTERRUPT_t int_alias_clr_en;		//  0x0020
+	INTERRUPT_t int_status_alias;		//  0x0024
+	SW_RESET_t sw_reset;			//  0x0028
+	SLV_TIMER_t slv_timer;			//  0x002C
+	MSI_CONFIG_t msi_config;		//  0x0030
+	LOOPBACK_t loopback;			//  0x0034
+	u32 watchdog_timer;			//  0x0038
+} GLOBAL_t, *PGLOBAL_t;
+
+/* END OF GLOBAL REGISTER ADDRESS MAP */
+
+
+/* START OF TXDMA REGISTER ADDRESS MAP */
+
+/*
+ * structure for txdma control status reg in txdma address map
+ * located at address 0x1000
+ */
+typedef union _TXDMA_CSR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused2:19;		// bits 13-31
+		u32 traffic_class:4;	// bits 9-12
+		u32 sngl_epkt_mode:1;	// bit 8
+		u32 cache_thrshld:4;	// bits 4-7
+		u32 unused1:2;		// bits 2-3
+		u32 drop_TLP_disable:1;	// bit 1
+		u32 halt:1;		// bit 0
+#else
+		u32 halt:1;		// bit 0
+		u32 drop_TLP_disable:1;	// bit 1
+		u32 unused1:2;		// bits 2-3
+		u32 cache_thrshld:4;	// bits 4-7
+		u32 sngl_epkt_mode:1;	// bit 8
+		u32 traffic_class:4;	// bits 9-12
+		u32 unused2:19;		// bits 13-31
+#endif
+	} bits;
+} TXDMA_CSR_t, *PTXDMA_CSR_t;
+
+/*
+ * structure for txdma packet ring base address hi reg in txdma address map
+ * located at address 0x1004
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for txdma packet ring base address low reg in txdma address map
+ * located at address 0x1008
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for txdma packet ring number of descriptor reg in txdma address
+ * map.  Located at address 0x100C
+ */
+typedef union _TXDMA_PR_NUM_DES_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:22;	// bits 10-31
+		u32 pr_ndes:10;	// bits 0-9
+#else
+		u32 pr_ndes:10;	// bits 0-9
+		u32 unused:22;	// bits 10-31
+#endif
+	} bits;
+} TXDMA_PR_NUM_DES_t, *PTXDMA_PR_NUM_DES_t;
+
+
+typedef union _DMA10W_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:21;	// bits 11-31
+		u32 wrap:1;	// bit 10
+		u32 val:10;	// bits 0-9
+#else
+		u32 val:10;	// bits 0-9
+		u32 wrap:1;	// bit 10
+		u32 unused:21;	// bits 11-31
+#endif
+	} bits;
+} DMA10W_t, *PDMA10W_t;
+
+/*
+ * structure for txdma tx queue write address reg in txdma address map
+ * located at address 0x1010
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for txdma tx queue write address external reg in txdma address map
+ * located at address 0x1014
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for txdma tx queue read address reg in txdma address map
+ * located at address 0x1018
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for txdma status writeback address hi reg in txdma address map
+ * located at address 0x101C
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for txdma status writeback address lo reg in txdma address map
+ * located at address 0x1020
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for txdma service request reg in txdma address map
+ * located at address 0x1024
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for txdma service complete reg in txdma address map
+ * located at address 0x1028
+ * Defined earlier (DMA10W_t)
+ */
+
+typedef union _DMA4W_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:27;	// bits 5-31
+		u32 wrap:1;	// bit 4
+		u32 val:4;		// bit 0-3
+#else
+		u32 val:4;		// bits 0-3
+		u32 wrap:1;	// bit 4
+		u32 unused:27;	// bits 5-31
+#endif
+	} bits;
+} DMA4W_t, *PDMA4W_t;
+
+/*
+ * structure for txdma tx descriptor cache read index reg in txdma address map
+ * located at address 0x102C
+ * Defined earlier (DMA4W_t)
+ */
+
+/*
+ * structure for txdma tx descriptor cache write index reg in txdma address map
+ * located at address 0x1030
+ * Defined earlier (DMA4W_t)
+ */
+
+/*
+ * structure for txdma error reg in txdma address map
+ * located at address 0x1034
+ */
+typedef union _TXDMA_ERROR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused3:22;		// bits 10-31
+		u32 WrbkRewind:1;	// bit 9
+		u32 WrbkResend:1;	// bit 8
+		u32 unused2:2;		// bits 6-7
+		u32 DescrRewind:1;	// bit 5
+		u32 DescrResend:1;	// bit 4
+		u32 unused1:2;		// bits 2-3
+		u32 PyldRewind:1;	// bit 1
+		u32 PyldResend:1;	// bit 0
+#else
+		u32 PyldResend:1;	// bit 0
+		u32 PyldRewind:1;	// bit 1
+		u32 unused1:2;		// bits 2-3
+		u32 DescrResend:1;	// bit 4
+		u32 DescrRewind:1;	// bit 5
+		u32 unused2:2;		// bits 6-7
+		u32 WrbkResend:1;	// bit 8
+		u32 WrbkRewind:1;	// bit 9
+		u32 unused3:22;		// bits 10-31
+#endif
+	} bits;
+} TXDMA_ERROR_t, *PTXDMA_ERROR_t;
+
+/*
+ * Tx DMA Module of JAGCore Address Mapping
+ * Located at address 0x1000
+ */
+typedef struct _TXDMA_t {		// Location:
+	TXDMA_CSR_t csr;		//  0x1000
+	u32 pr_base_hi;			//  0x1004
+	u32 pr_base_lo;			//  0x1008
+	TXDMA_PR_NUM_DES_t pr_num_des;	//  0x100C
+	DMA10W_t txq_wr_addr;		//  0x1010
+	DMA10W_t txq_wr_addr_ext;	//  0x1014
+	DMA10W_t txq_rd_addr;		//  0x1018
+	u32 dma_wb_base_hi;		//  0x101C
+	u32 dma_wb_base_lo;		//  0x1020
+	DMA10W_t service_request;	//  0x1024
+	DMA10W_t service_complete;	//  0x1028
+	DMA4W_t cache_rd_index;		//  0x102C
+	DMA4W_t cache_wr_index;		//  0x1030
+	TXDMA_ERROR_t TxDmaError;	//  0x1034
+	u32 DescAbortCount;		//  0x1038
+	u32 PayloadAbortCnt;		//  0x103c
+	u32 WriteBackAbortCnt;		//  0x1040
+	u32 DescTimeoutCnt;		//  0x1044
+	u32 PayloadTimeoutCnt;		//  0x1048
+	u32 WriteBackTimeoutCnt;	//  0x104c
+	u32 DescErrorCount;		//  0x1050
+	u32 PayloadErrorCnt;		//  0x1054
+	u32 WriteBackErrorCnt;		//  0x1058
+	u32 DroppedTLPCount;		//  0x105c
+	DMA10W_t NewServiceComplete;	//  0x1060
+	u32 EthernetPacketCount;	//  0x1064
+} TXDMA_t, *PTXDMA_t;
+
+/* END OF TXDMA REGISTER ADDRESS MAP */
+
+
+/* START OF RXDMA REGISTER ADDRESS MAP */
+
+/*
+ * structure for control status reg in rxdma address map
+ * Located at address 0x2000
+ */
+typedef union _RXDMA_CSR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused2:14;		// bits 18-31
+		u32 halt_status:1;	// bit 17
+		u32 pkt_done_flush:1;	// bit 16
+		u32 pkt_drop_disable:1;	// bit 15
+		u32 unused1:1;		// bit 14
+		u32 fbr1_enable:1;	// bit 13
+		u32 fbr1_size:2;	// bits 11-12
+		u32 fbr0_enable:1;	// bit 10
+		u32 fbr0_size:2;	// bits 8-9
+		u32 dma_big_endian:1;	// bit 7
+		u32 pkt_big_endian:1;	// bit 6
+		u32 psr_big_endian:1;	// bit 5
+		u32 fbr_big_endian:1;	// bit 4
+		u32 tc:3;		// bits 1-3
+		u32 halt:1;		// bit 0
+#else
+		u32 halt:1;		// bit 0
+		u32 tc:3;		// bits 1-3
+		u32 fbr_big_endian:1;	// bit 4
+		u32 psr_big_endian:1;	// bit 5
+		u32 pkt_big_endian:1;	// bit 6
+		u32 dma_big_endian:1;	// bit 7
+		u32 fbr0_size:2;	// bits 8-9
+		u32 fbr0_enable:1;	// bit 10
+		u32 fbr1_size:2;	// bits 11-12
+		u32 fbr1_enable:1;	// bit 13
+		u32 unused1:1;		// bit 14
+		u32 pkt_drop_disable:1;	// bit 15
+		u32 pkt_done_flush:1;	// bit 16
+		u32 halt_status:1;	// bit 17
+		u32 unused2:14;		// bits 18-31
+#endif
+	} bits;
+} RXDMA_CSR_t, *PRXDMA_CSR_t;
+
+/*
+ * structure for dma writeback lo reg in rxdma address map
+ * located at address 0x2004
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for dma writeback hi reg in rxdma address map
+ * located at address 0x2008
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for number of packets done reg in rxdma address map
+ * located at address 0x200C
+ */
+typedef union _RXDMA_NUM_PKT_DONE_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:24;	// bits 8-31
+		u32 num_done:8;	// bits 0-7
+#else
+		u32 num_done:8;	// bits 0-7
+		u32 unused:24;	// bits 8-31
+#endif
+	} bits;
+} RXDMA_NUM_PKT_DONE_t, *PRXDMA_NUM_PKT_DONE_t;
+
+/*
+ * structure for max packet time reg in rxdma address map
+ * located at address 0x2010
+ */
+typedef union _RXDMA_MAX_PKT_TIME_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:14;		// bits 18-31
+		u32 time_done:18;	// bits 0-17
+#else
+		u32 time_done:18;	// bits 0-17
+		u32 unused:14;		// bits 18-31
+#endif
+	} bits;
+} RXDMA_MAX_PKT_TIME_t, *PRXDMA_MAX_PKT_TIME_t;
+
+/*
+ * structure for rx queue read address reg in rxdma address map
+ * located at address 0x2014
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for rx queue read address external reg in rxdma address map
+ * located at address 0x2018
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for rx queue write address reg in rxdma address map
+ * located at address 0x201C
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for packet status ring base address lo reg in rxdma address map
+ * located at address 0x2020
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for packet status ring base address hi reg in rxdma address map
+ * located at address 0x2024
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for packet status ring number of descriptors reg in rxdma address
+ * map.  Located at address 0x2028
+ */
+typedef union _RXDMA_PSR_NUM_DES_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:20;		// bits 12-31
+		u32 psr_ndes:12;	// bit 0-11
+#else
+		u32 psr_ndes:12;	// bit 0-11
+		u32 unused:20;		// bits 12-31
+#endif
+	} bits;
+} RXDMA_PSR_NUM_DES_t, *PRXDMA_PSR_NUM_DES_t;
+
+/*
+ * structure for packet status ring available offset reg in rxdma address map
+ * located at address 0x202C
+ */
+typedef union _RXDMA_PSR_AVAIL_OFFSET_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:19;		// bits 13-31
+		u32 psr_avail_wrap:1;	// bit 12
+		u32 psr_avail:12;	// bit 0-11
+#else
+		u32 psr_avail:12;	// bit 0-11
+		u32 psr_avail_wrap:1;	// bit 12
+		u32 unused:19;		// bits 13-31
+#endif
+	} bits;
+} RXDMA_PSR_AVAIL_OFFSET_t, *PRXDMA_PSR_AVAIL_OFFSET_t;
+
+/*
+ * structure for packet status ring full offset reg in rxdma address map
+ * located at address 0x2030
+ */
+typedef union _RXDMA_PSR_FULL_OFFSET_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:19;		// bits 13-31
+		u32 psr_full_wrap:1;	// bit 12
+		u32 psr_full:12;	// bit 0-11
+#else
+		u32 psr_full:12;	// bit 0-11
+		u32 psr_full_wrap:1;	// bit 12
+		u32 unused:19;		// bits 13-31
+#endif
+	} bits;
+} RXDMA_PSR_FULL_OFFSET_t, *PRXDMA_PSR_FULL_OFFSET_t;
+
+/*
+ * structure for packet status ring access index reg in rxdma address map
+ * located at address 0x2034
+ */
+typedef union _RXDMA_PSR_ACCESS_INDEX_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:27;	// bits 5-31
+		u32 psr_ai:5;	// bits 0-4
+#else
+		u32 psr_ai:5;	// bits 0-4
+		u32 unused:27;	// bits 5-31
+#endif
+	} bits;
+} RXDMA_PSR_ACCESS_INDEX_t, *PRXDMA_PSR_ACCESS_INDEX_t;
+
+/*
+ * structure for packet status ring minimum descriptors reg in rxdma address
+ * map.  Located at address 0x2038
+ */
+typedef union _RXDMA_PSR_MIN_DES_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:20;	// bits 12-31
+		u32 psr_min:12;	// bits 0-11
+#else
+		u32 psr_min:12;	// bits 0-11
+		u32 unused:20;	// bits 12-31
+#endif
+	} bits;
+} RXDMA_PSR_MIN_DES_t, *PRXDMA_PSR_MIN_DES_t;
+
+/*
+ * structure for free buffer ring base lo address reg in rxdma address map
+ * located at address 0x203C
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for free buffer ring base hi address reg in rxdma address map
+ * located at address 0x2040
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for free buffer ring number of descriptors reg in rxdma address
+ * map.  Located at address 0x2044
+ */
+typedef union _RXDMA_FBR_NUM_DES_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:22;		// bits 10-31
+		u32 fbr_ndesc:10;	// bits 0-9
+#else
+		u32 fbr_ndesc:10;	// bits 0-9
+		u32 unused:22;		// bits 10-31
+#endif
+	} bits;
+} RXDMA_FBR_NUM_DES_t, *PRXDMA_FBR_NUM_DES_t;
+
+/*
+ * structure for free buffer ring 0 available offset reg in rxdma address map
+ * located at address 0x2048
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for free buffer ring 0 full offset reg in rxdma address map
+ * located at address 0x204C
+ * Defined earlier (DMA10W_t)
+ */
+
+/*
+ * structure for free buffer cache 0 full offset reg in rxdma address map
+ * located at address 0x2050
+ */
+typedef union _RXDMA_FBC_RD_INDEX_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:27;	// bits 5-31
+		u32 fbc_rdi:5;	// bit 0-4
+#else
+		u32 fbc_rdi:5;	// bit 0-4
+		u32 unused:27;	// bits 5-31
+#endif
+	} bits;
+} RXDMA_FBC_RD_INDEX_t, *PRXDMA_FBC_RD_INDEX_t;
+
+/*
+ * structure for free buffer ring 0 minimum descriptor reg in rxdma address map
+ * located at address 0x2054
+ */
+typedef union _RXDMA_FBR_MIN_DES_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:22;	// bits 10-31
+		u32 fbr_min:10;	// bits 0-9
+#else
+		u32 fbr_min:10;	// bits 0-9
+		u32 unused:22;	// bits 10-31
+#endif
+	} bits;
+} RXDMA_FBR_MIN_DES_t, *PRXDMA_FBR_MIN_DES_t;
+
+/*
+ * structure for free buffer ring 1 base address lo reg in rxdma address map
+ * located at address 0x2058 - 0x205C
+ * Defined earlier (RXDMA_FBR_BASE_LO_t and RXDMA_FBR_BASE_HI_t)
+ */
+
+/*
+ * structure for free buffer ring 1 number of descriptors reg in rxdma address
+ * map.  Located at address 0x2060
+ * Defined earlier (RXDMA_FBR_NUM_DES_t)
+ */
+
+/*
+ * structure for free buffer ring 1 available offset reg in rxdma address map
+ * located at address 0x2064
+ * Defined Earlier (RXDMA_FBR_AVAIL_OFFSET_t)
+ */
+
+/*
+ * structure for free buffer ring 1 full offset reg in rxdma address map
+ * located at address 0x2068
+ * Defined Earlier (RXDMA_FBR_FULL_OFFSET_t)
+ */
+
+/*
+ * structure for free buffer cache 1 read index reg in rxdma address map
+ * located at address 0x206C
+ * Defined Earlier (RXDMA_FBC_RD_INDEX_t)
+ */
+
+/*
+ * structure for free buffer ring 1 minimum descriptor reg in rxdma address map
+ * located at address 0x2070
+ * Defined Earlier (RXDMA_FBR_MIN_DES_t)
+ */
+
+/*
+ * Rx DMA Module of JAGCore Address Mapping
+ * Located at address 0x2000
+ */
+typedef struct _RXDMA_t {				// Location:
+	RXDMA_CSR_t csr;				//  0x2000
+	u32 dma_wb_base_lo;				//  0x2004
+	u32 dma_wb_base_hi;				//  0x2008
+	RXDMA_NUM_PKT_DONE_t num_pkt_done;		//  0x200C
+	RXDMA_MAX_PKT_TIME_t max_pkt_time;		//  0x2010
+	DMA10W_t rxq_rd_addr;				//  0x2014
+	DMA10W_t rxq_rd_addr_ext;			//  0x2018
+	DMA10W_t rxq_wr_addr;				//  0x201C
+	u32 psr_base_lo;				//  0x2020
+	u32 psr_base_hi;				//  0x2024
+	RXDMA_PSR_NUM_DES_t psr_num_des;		//  0x2028
+	RXDMA_PSR_AVAIL_OFFSET_t psr_avail_offset;	//  0x202C
+	RXDMA_PSR_FULL_OFFSET_t psr_full_offset;	//  0x2030
+	RXDMA_PSR_ACCESS_INDEX_t psr_access_index;	//  0x2034
+	RXDMA_PSR_MIN_DES_t psr_min_des;		//  0x2038
+	u32 fbr0_base_lo;				//  0x203C
+	u32 fbr0_base_hi;				//  0x2040
+	RXDMA_FBR_NUM_DES_t fbr0_num_des;		//  0x2044
+	DMA10W_t fbr0_avail_offset;			//  0x2048
+	DMA10W_t fbr0_full_offset;			//  0x204C
+	RXDMA_FBC_RD_INDEX_t fbr0_rd_index;		//  0x2050
+	RXDMA_FBR_MIN_DES_t fbr0_min_des;		//  0x2054
+	u32 fbr1_base_lo;				//  0x2058
+	u32 fbr1_base_hi;				//  0x205C
+	RXDMA_FBR_NUM_DES_t fbr1_num_des;		//  0x2060
+	DMA10W_t fbr1_avail_offset;			//  0x2064
+	DMA10W_t fbr1_full_offset;			//  0x2068
+	RXDMA_FBC_RD_INDEX_t fbr1_rd_index;		//  0x206C
+	RXDMA_FBR_MIN_DES_t fbr1_min_des;		//  0x2070
+} RXDMA_t, *PRXDMA_t;
+
+/* END OF RXDMA REGISTER ADDRESS MAP */
+
+
+/* START OF TXMAC REGISTER ADDRESS MAP */
+
+/*
+ * structure for control reg in txmac address map
+ * located at address 0x3000
+ */
+typedef union _TXMAC_CTL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:24;		// bits 8-31
+		u32 cklseg_diable:1;	// bit 7
+		u32 ckbcnt_disable:1;	// bit 6
+		u32 cksegnum:1;		// bit 5
+		u32 async_disable:1;	// bit 4
+		u32 fc_disable:1;	// bit 3
+		u32 mcif_disable:1;	// bit 2
+		u32 mif_disable:1;	// bit 1
+		u32 txmac_en:1;		// bit 0
+#else
+		u32 txmac_en:1;		// bit 0
+		u32 mif_disable:1;	// bit 1 mac interface
+		u32 mcif_disable:1;	// bit 2 mem. contr. interface
+		u32 fc_disable:1;	// bit 3
+		u32 async_disable:1;	// bit 4
+		u32 cksegnum:1;		// bit 5
+		u32 ckbcnt_disable:1;	// bit 6
+		u32 cklseg_diable:1;	// bit 7
+		u32 unused:24;		// bits 8-31
+#endif
+	} bits;
+} TXMAC_CTL_t, *PTXMAC_CTL_t;
+
+/*
+ * structure for shadow pointer reg in txmac address map
+ * located at address 0x3004
+ */
+typedef union _TXMAC_SHADOW_PTR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved2:5;	// bits 27-31
+		u32 txq_rd_ptr:11;	// bits 16-26
+		u32 reserved:5;		// bits 11-15
+		u32 txq_wr_ptr:11;	// bits 0-10
+#else
+		u32 txq_wr_ptr:11;	// bits 0-10
+		u32 reserved:5;		// bits 11-15
+		u32 txq_rd_ptr:11;	// bits 16-26
+		u32 reserved2:5;	// bits 27-31
+#endif
+	} bits;
+} TXMAC_SHADOW_PTR_t, *PTXMAC_SHADOW_PTR_t;
+
+/*
+ * structure for error count reg in txmac address map
+ * located at address 0x3008
+ */
+typedef union _TXMAC_ERR_CNT_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:20;		// bits 12-31
+		u32 reserved:4;		// bits 8-11
+		u32 txq_underrun:4;	// bits 4-7
+		u32 fifo_underrun:4;	// bits 0-3
+#else
+		u32 fifo_underrun:4;	// bits 0-3
+		u32 txq_underrun:4;	// bits 4-7
+		u32 reserved:4;		// bits 8-11
+		u32 unused:20;		// bits 12-31
+#endif
+	} bits;
+} TXMAC_ERR_CNT_t, *PTXMAC_ERR_CNT_t;
+
+/*
+ * structure for max fill reg in txmac address map
+ * located at address 0x300C
+ */
+typedef union _TXMAC_MAX_FILL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:20;		// bits 12-31
+		u32 max_fill:12;	// bits 0-11
+#else
+		u32 max_fill:12;	// bits 0-11
+		u32 unused:20;		// bits 12-31
+#endif
+	} bits;
+} TXMAC_MAX_FILL_t, *PTXMAC_MAX_FILL_t;
+
+/*
+ * structure for cf parameter reg in txmac address map
+ * located at address 0x3010
+ */
+typedef union _TXMAC_CF_PARAM_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 cfep:16;	// bits 16-31
+		u32 cfpt:16;	// bits 0-15
+#else
+		u32 cfpt:16;	// bits 0-15
+		u32 cfep:16;	// bits 16-31
+#endif
+	} bits;
+} TXMAC_CF_PARAM_t, *PTXMAC_CF_PARAM_t;
+
+/*
+ * structure for tx test reg in txmac address map
+ * located at address 0x3014
+ */
+typedef union _TXMAC_TXTEST_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused2:15;		// bits 17-31
+		u32 reserved1:1;	// bit 16
+		u32 txtest_en:1;	// bit 15
+		u32 unused1:4;		// bits 11-14
+		u32 txqtest_ptr:11;	// bits 0-11
+#else
+		u32 txqtest_ptr:11;	// bits 0-10
+		u32 unused1:4;		// bits 11-14
+		u32 txtest_en:1;	// bit 15
+		u32 reserved1:1;	// bit 16
+		u32 unused2:15;		// bits 17-31
+#endif
+	} bits;
+} TXMAC_TXTEST_t, *PTXMAC_TXTEST_t;
+
+/*
+ * structure for error reg in txmac address map
+ * located at address 0x3018
+ */
+typedef union _TXMAC_ERR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused2:23;		// bits 9-31
+		u32 fifo_underrun:1;	// bit 8
+		u32 unused1:2;		// bits 6-7
+		u32 ctrl2_err:1;	// bit 5
+		u32 txq_underrun:1;	// bit 4
+		u32 bcnt_err:1;		// bit 3
+		u32 lseg_err:1;		// bit 2
+		u32 segnum_err:1;	// bit 1
+		u32 seg0_err:1;		// bit 0
+#else
+		u32 seg0_err:1;		// bit 0
+		u32 segnum_err:1;	// bit 1
+		u32 lseg_err:1;		// bit 2
+		u32 bcnt_err:1;		// bit 3
+		u32 txq_underrun:1;	// bit 4
+		u32 ctrl2_err:1;	// bit 5
+		u32 unused1:2;		// bits 6-7
+		u32 fifo_underrun:1;	// bit 8
+		u32 unused2:23;		// bits 9-31
+#endif
+	} bits;
+} TXMAC_ERR_t, *PTXMAC_ERR_t;
+
+/*
+ * structure for error interrupt reg in txmac address map
+ * located at address 0x301C
+ */
+typedef union _TXMAC_ERR_INT_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused2:23;		// bits 9-31
+		u32 fifo_underrun:1;	// bit 8
+		u32 unused1:2;		// bits 6-7
+		u32 ctrl2_err:1;	// bit 5
+		u32 txq_underrun:1;	// bit 4
+		u32 bcnt_err:1;		// bit 3
+		u32 lseg_err:1;		// bit 2
+		u32 segnum_err:1;	// bit 1
+		u32 seg0_err:1;		// bit 0
+#else
+		u32 seg0_err:1;		// bit 0
+		u32 segnum_err:1;	// bit 1
+		u32 lseg_err:1;		// bit 2
+		u32 bcnt_err:1;		// bit 3
+		u32 txq_underrun:1;	// bit 4
+		u32 ctrl2_err:1;	// bit 5
+		u32 unused1:2;		// bits 6-7
+		u32 fifo_underrun:1;	// bit 8
+		u32 unused2:23;		// bits 9-31
+#endif
+	} bits;
+} TXMAC_ERR_INT_t, *PTXMAC_ERR_INT_t;
+
+/*
+ * structure for error interrupt reg in txmac address map
+ * located at address 0x3020
+ */
+typedef union _TXMAC_CP_CTRL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:30;		// bits 2-31
+		u32 bp_req:1;		// bit 1
+		u32 bp_xonxoff:1;	// bit 0
+#else
+		u32 bp_xonxoff:1;	// bit 0
+		u32 bp_req:1;		// bit 1
+		u32 unused:30;		// bits 2-31
+#endif
+	} bits;
+} TXMAC_BP_CTRL_t, *PTXMAC_BP_CTRL_t;
+
+/*
+ * Tx MAC Module of JAGCore Address Mapping
+ */
+typedef struct _TXMAC_t {		// Location:
+	TXMAC_CTL_t ctl;		//  0x3000
+	TXMAC_SHADOW_PTR_t shadow_ptr;	//  0x3004
+	TXMAC_ERR_CNT_t err_cnt;	//  0x3008
+	TXMAC_MAX_FILL_t max_fill;	//  0x300C
+	TXMAC_CF_PARAM_t cf_param;	//  0x3010
+	TXMAC_TXTEST_t tx_test;		//  0x3014
+	TXMAC_ERR_t err;		//  0x3018
+	TXMAC_ERR_INT_t err_int;	//  0x301C
+	TXMAC_BP_CTRL_t bp_ctrl;	//  0x3020
+} TXMAC_t, *PTXMAC_t;
+
+/* END OF TXMAC REGISTER ADDRESS MAP */
+
+/* START OF RXMAC REGISTER ADDRESS MAP */
+
+/*
+ * structure for rxmac control reg in rxmac address map
+ * located at address 0x4000
+ */
+typedef union _RXMAC_CTRL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:25;		// bits 7-31
+		u32 rxmac_int_disable:1;	// bit 6
+		u32 async_disable:1;		// bit 5
+		u32 mif_disable:1;		// bit 4
+		u32 wol_disable:1;		// bit 3
+		u32 pkt_filter_disable:1;	// bit 2
+		u32 mcif_disable:1;		// bit 1
+		u32 rxmac_en:1;			// bit 0
+#else
+		u32 rxmac_en:1;			// bit 0
+		u32 mcif_disable:1;		// bit 1
+		u32 pkt_filter_disable:1;	// bit 2
+		u32 wol_disable:1;		// bit 3
+		u32 mif_disable:1;		// bit 4
+		u32 async_disable:1;		// bit 5
+		u32 rxmac_int_disable:1;	// bit 6
+		u32 reserved:25;		// bits 7-31
+#endif
+	} bits;
+} RXMAC_CTRL_t, *PRXMAC_CTRL_t;
+
+/*
+ * structure for Wake On Lan Control and CRC 0 reg in rxmac address map
+ * located at address 0x4004
+ */
+typedef union _RXMAC_WOL_CTL_CRC0_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 crc0:16;		// bits 16-31
+		u32 reserve:4;		// bits 12-15
+		u32 ignore_pp:1;	// bit 11
+		u32 ignore_mp:1;	// bit 10
+		u32 clr_intr:1;		// bit 9
+		u32 ignore_link_chg:1;	// bit 8
+		u32 ignore_uni:1;	// bit 7
+		u32 ignore_multi:1;	// bit 6
+		u32 ignore_broad:1;	// bit 5
+		u32 valid_crc4:1;	// bit 4
+		u32 valid_crc3:1;	// bit 3
+		u32 valid_crc2:1;	// bit 2
+		u32 valid_crc1:1;	// bit 1
+		u32 valid_crc0:1;	// bit 0
+#else
+		u32 valid_crc0:1;	// bit 0
+		u32 valid_crc1:1;	// bit 1
+		u32 valid_crc2:1;	// bit 2
+		u32 valid_crc3:1;	// bit 3
+		u32 valid_crc4:1;	// bit 4
+		u32 ignore_broad:1;	// bit 5
+		u32 ignore_multi:1;	// bit 6
+		u32 ignore_uni:1;	// bit 7
+		u32 ignore_link_chg:1;	// bit 8
+		u32 clr_intr:1;		// bit 9
+		u32 ignore_mp:1;	// bit 10
+		u32 ignore_pp:1;	// bit 11
+		u32 reserve:4;		// bits 12-15
+		u32 crc0:16;		// bits 16-31
+#endif
+	} bits;
+} RXMAC_WOL_CTL_CRC0_t, *PRXMAC_WOL_CTL_CRC0_t;
+
+/*
+ * structure for CRC 1 and CRC 2 reg in rxmac address map
+ * located at address 0x4008
+ */
+typedef union _RXMAC_WOL_CRC12_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 crc2:16;	// bits 16-31
+		u32 crc1:16;	// bits 0-15
+#else
+		u32 crc1:16;	// bits 0-15
+		u32 crc2:16;	// bits 16-31
+#endif
+	} bits;
+} RXMAC_WOL_CRC12_t, *PRXMAC_WOL_CRC12_t;
+
+/*
+ * structure for CRC 3 and CRC 4 reg in rxmac address map
+ * located at address 0x400C
+ */
+typedef union _RXMAC_WOL_CRC34_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 crc4:16;	// bits 16-31
+		u32 crc3:16;	// bits 0-15
+#else
+		u32 crc3:16;	// bits 0-15
+		u32 crc4:16;	// bits 16-31
+#endif
+	} bits;
+} RXMAC_WOL_CRC34_t, *PRXMAC_WOL_CRC34_t;
+
+/*
+ * structure for Wake On Lan Source Address Lo reg in rxmac address map
+ * located at address 0x4010
+ */
+typedef union _RXMAC_WOL_SA_LO_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 sa3:8;	// bits 24-31
+		u32 sa4:8;	// bits 16-23
+		u32 sa5:8;	// bits 8-15
+		u32 sa6:8;	// bits 0-7
+#else
+		u32 sa6:8;	// bits 0-7
+		u32 sa5:8;	// bits 8-15
+		u32 sa4:8;	// bits 16-23
+		u32 sa3:8;	// bits 24-31
+#endif
+	} bits;
+} RXMAC_WOL_SA_LO_t, *PRXMAC_WOL_SA_LO_t;
+
+/*
+ * structure for Wake On Lan Source Address Hi reg in rxmac address map
+ * located at address 0x4014
+ */
+typedef union _RXMAC_WOL_SA_HI_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:16;	// bits 16-31
+		u32 sa1:8;		// bits 8-15
+		u32 sa2:8;		// bits 0-7
+#else
+		u32 sa2:8;		// bits 0-7
+		u32 sa1:8;		// bits 8-15
+		u32 reserved:16;	// bits 16-31
+#endif
+	} bits;
+} RXMAC_WOL_SA_HI_t, *PRXMAC_WOL_SA_HI_t;
+
+/*
+ * structure for Wake On Lan mask reg in rxmac address map
+ * located at address 0x4018 - 0x4064
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for Unicast Paket Filter Address 1 reg in rxmac address map
+ * located at address 0x4068
+ */
+typedef union _RXMAC_UNI_PF_ADDR1_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 addr1_3:8;	// bits 24-31
+		u32 addr1_4:8;	// bits 16-23
+		u32 addr1_5:8;	// bits 8-15
+		u32 addr1_6:8;	// bits 0-7
+#else
+		u32 addr1_6:8;	// bits 0-7
+		u32 addr1_5:8;	// bits 8-15
+		u32 addr1_4:8;	// bits 16-23
+		u32 addr1_3:8;	// bits 24-31
+#endif
+	} bits;
+} RXMAC_UNI_PF_ADDR1_t, *PRXMAC_UNI_PF_ADDR1_t;
+
+/*
+ * structure for Unicast Paket Filter Address 2 reg in rxmac address map
+ * located at address 0x406C
+ */
+typedef union _RXMAC_UNI_PF_ADDR2_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 addr2_3:8;	// bits 24-31
+		u32 addr2_4:8;	// bits 16-23
+		u32 addr2_5:8;	// bits 8-15
+		u32 addr2_6:8;	// bits 0-7
+#else
+		u32 addr2_6:8;	// bits 0-7
+		u32 addr2_5:8;	// bits 8-15
+		u32 addr2_4:8;	// bits 16-23
+		u32 addr2_3:8;	// bits 24-31
+#endif
+	} bits;
+} RXMAC_UNI_PF_ADDR2_t, *PRXMAC_UNI_PF_ADDR2_t;
+
+/*
+ * structure for Unicast Paket Filter Address 1 & 2 reg in rxmac address map
+ * located at address 0x4070
+ */
+typedef union _RXMAC_UNI_PF_ADDR3_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 addr2_1:8;	// bits 24-31
+		u32 addr2_2:8;	// bits 16-23
+		u32 addr1_1:8;	// bits 8-15
+		u32 addr1_2:8;	// bits 0-7
+#else
+		u32 addr1_2:8;	// bits 0-7
+		u32 addr1_1:8;	// bits 8-15
+		u32 addr2_2:8;	// bits 16-23
+		u32 addr2_1:8;	// bits 24-31
+#endif
+	} bits;
+} RXMAC_UNI_PF_ADDR3_t, *PRXMAC_UNI_PF_ADDR3_t;
+
+/*
+ * structure for Multicast Hash reg in rxmac address map
+ * located at address 0x4074 - 0x4080
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for Packet Filter Control reg in rxmac address map
+ * located at address 0x4084
+ */
+typedef union _RXMAC_PF_CTRL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused2:9;		// bits 23-31
+		u32 min_pkt_size:7;	// bits 16-22
+		u32 unused1:12;		// bits 4-15
+		u32 filter_frag_en:1;	// bit 3
+		u32 filter_uni_en:1;	// bit 2
+		u32 filter_multi_en:1;	// bit 1
+		u32 filter_broad_en:1;	// bit 0
+#else
+		u32 filter_broad_en:1;	// bit 0
+		u32 filter_multi_en:1;	// bit 1
+		u32 filter_uni_en:1;	// bit 2
+		u32 filter_frag_en:1;	// bit 3
+		u32 unused1:12;		// bits 4-15
+		u32 min_pkt_size:7;	// bits 16-22
+		u32 unused2:9;		// bits 23-31
+#endif
+	} bits;
+} RXMAC_PF_CTRL_t, *PRXMAC_PF_CTRL_t;
+
+/*
+ * structure for Memory Controller Interface Control Max Segment reg in rxmac
+ * address map.  Located at address 0x4088
+ */
+typedef union _RXMAC_MCIF_CTRL_MAX_SEG_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:22;	// bits 10-31
+		u32 max_size:8;	// bits 2-9
+		u32 fc_en:1;	// bit 1
+		u32 seg_en:1;	// bit 0
+#else
+		u32 seg_en:1;	// bit 0
+		u32 fc_en:1;	// bit 1
+		u32 max_size:8;	// bits 2-9
+		u32 reserved:22;	// bits 10-31
+#endif
+	} bits;
+} RXMAC_MCIF_CTRL_MAX_SEG_t, *PRXMAC_MCIF_CTRL_MAX_SEG_t;
+
+/*
+ * structure for Memory Controller Interface Water Mark reg in rxmac address
+ * map.  Located at address 0x408C
+ */
+typedef union _RXMAC_MCIF_WATER_MARK_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved2:6;	// bits 26-31
+		u32 mark_hi:10;	// bits 16-25
+		u32 reserved1:6;	// bits 10-15
+		u32 mark_lo:10;	// bits 0-9
+#else
+		u32 mark_lo:10;	// bits 0-9
+		u32 reserved1:6;	// bits 10-15
+		u32 mark_hi:10;	// bits 16-25
+		u32 reserved2:6;	// bits 26-31
+#endif
+	} bits;
+} RXMAC_MCIF_WATER_MARK_t, *PRXMAC_MCIF_WATER_MARK_t;
+
+/*
+ * structure for Rx Queue Dialog reg in rxmac address map.
+ * located at address 0x4090
+ */
+typedef union _RXMAC_RXQ_DIAG_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved2:6;	// bits 26-31
+		u32 rd_ptr:10;	// bits 16-25
+		u32 reserved1:6;	// bits 10-15
+		u32 wr_ptr:10;	// bits 0-9
+#else
+		u32 wr_ptr:10;	// bits 0-9
+		u32 reserved1:6;	// bits 10-15
+		u32 rd_ptr:10;	// bits 16-25
+		u32 reserved2:6;	// bits 26-31
+#endif
+	} bits;
+} RXMAC_RXQ_DIAG_t, *PRXMAC_RXQ_DIAG_t;
+
+/*
+ * structure for space availiable reg in rxmac address map.
+ * located at address 0x4094
+ */
+typedef union _RXMAC_SPACE_AVAIL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved2:15;		// bits 17-31
+		u32 space_avail_en:1;	// bit 16
+		u32 reserved1:6;		// bits 10-15
+		u32 space_avail:10;	// bits 0-9
+#else
+		u32 space_avail:10;	// bits 0-9
+		u32 reserved1:6;		// bits 10-15
+		u32 space_avail_en:1;	// bit 16
+		u32 reserved2:15;		// bits 17-31
+#endif
+	} bits;
+} RXMAC_SPACE_AVAIL_t, *PRXMAC_SPACE_AVAIL_t;
+
+/*
+ * structure for management interface reg in rxmac address map.
+ * located at address 0x4098
+ */
+typedef union _RXMAC_MIF_CTL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserve:14;		// bits 18-31
+		u32 drop_pkt_en:1;		// bit 17
+		u32 drop_pkt_mask:17;	// bits 0-16
+#else
+		u32 drop_pkt_mask:17;	// bits 0-16
+		u32 drop_pkt_en:1;		// bit 17
+		u32 reserve:14;		// bits 18-31
+#endif
+	} bits;
+} RXMAC_MIF_CTL_t, *PRXMAC_MIF_CTL_t;
+
+/*
+ * structure for Error reg in rxmac address map.
+ * located at address 0x409C
+ */
+typedef union _RXMAC_ERROR_REG_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserve:28;	// bits 4-31
+		u32 mif:1;		// bit 3
+		u32 async:1;	// bit 2
+		u32 pkt_filter:1;	// bit 1
+		u32 mcif:1;	// bit 0
+#else
+		u32 mcif:1;	// bit 0
+		u32 pkt_filter:1;	// bit 1
+		u32 async:1;	// bit 2
+		u32 mif:1;		// bit 3
+		u32 reserve:28;	// bits 4-31
+#endif
+	} bits;
+} RXMAC_ERROR_REG_t, *PRXMAC_ERROR_REG_t;
+
+/*
+ * Rx MAC Module of JAGCore Address Mapping
+ */
+typedef struct _RXMAC_t {				// Location:
+	RXMAC_CTRL_t ctrl;				//  0x4000
+	RXMAC_WOL_CTL_CRC0_t crc0;			//  0x4004
+	RXMAC_WOL_CRC12_t crc12;			//  0x4008
+	RXMAC_WOL_CRC34_t crc34;			//  0x400C
+	RXMAC_WOL_SA_LO_t sa_lo;			//  0x4010
+	RXMAC_WOL_SA_HI_t sa_hi;			//  0x4014
+	u32 mask0_word0;				//  0x4018
+	u32 mask0_word1;				//  0x401C
+	u32 mask0_word2;				//  0x4020
+	u32 mask0_word3;				//  0x4024
+	u32 mask1_word0;				//  0x4028
+	u32 mask1_word1;				//  0x402C
+	u32 mask1_word2;				//  0x4030
+	u32 mask1_word3;				//  0x4034
+	u32 mask2_word0;				//  0x4038
+	u32 mask2_word1;				//  0x403C
+	u32 mask2_word2;				//  0x4040
+	u32 mask2_word3;				//  0x4044
+	u32 mask3_word0;				//  0x4048
+	u32 mask3_word1;				//  0x404C
+	u32 mask3_word2;				//  0x4050
+	u32 mask3_word3;				//  0x4054
+	u32 mask4_word0;				//  0x4058
+	u32 mask4_word1;				//  0x405C
+	u32 mask4_word2;				//  0x4060
+	u32 mask4_word3;				//  0x4064
+	RXMAC_UNI_PF_ADDR1_t uni_pf_addr1;		//  0x4068
+	RXMAC_UNI_PF_ADDR2_t uni_pf_addr2;		//  0x406C
+	RXMAC_UNI_PF_ADDR3_t uni_pf_addr3;		//  0x4070
+	u32 multi_hash1;				//  0x4074
+	u32 multi_hash2;				//  0x4078
+	u32 multi_hash3;				//  0x407C
+	u32 multi_hash4;				//  0x4080
+	RXMAC_PF_CTRL_t pf_ctrl;			//  0x4084
+	RXMAC_MCIF_CTRL_MAX_SEG_t mcif_ctrl_max_seg;	//  0x4088
+	RXMAC_MCIF_WATER_MARK_t mcif_water_mark;	//  0x408C
+	RXMAC_RXQ_DIAG_t rxq_diag;			//  0x4090
+	RXMAC_SPACE_AVAIL_t space_avail;		//  0x4094
+
+	RXMAC_MIF_CTL_t mif_ctrl;			//  0x4098
+	RXMAC_ERROR_REG_t err_reg;			//  0x409C
+} RXMAC_t, *PRXMAC_t;
+
+/* END OF TXMAC REGISTER ADDRESS MAP */
+
+
+/* START OF MAC REGISTER ADDRESS MAP */
+
+/*
+ * structure for configuration #1 reg in mac address map.
+ * located at address 0x5000
+ */
+typedef union _MAC_CFG1_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 soft_reset:1;		// bit 31
+		u32 sim_reset:1;		// bit 30
+		u32 reserved3:10;		// bits 20-29
+		u32 reset_rx_mc:1;		// bit 19
+		u32 reset_tx_mc:1;		// bit 18
+		u32 reset_rx_fun:1;	// bit 17
+		u32 reset_tx_fun:1;	// bit 16
+		u32 reserved2:7;		// bits 9-15
+		u32 loop_back:1;		// bit 8
+		u32 reserved1:2;		// bits 6-7
+		u32 rx_flow:1;		// bit 5
+		u32 tx_flow:1;		// bit 4
+		u32 syncd_rx_en:1;		// bit 3
+		u32 rx_enable:1;		// bit 2
+		u32 syncd_tx_en:1;		// bit 1
+		u32 tx_enable:1;		// bit 0
+#else
+		u32 tx_enable:1;		// bit 0
+		u32 syncd_tx_en:1;		// bit 1
+		u32 rx_enable:1;		// bit 2
+		u32 syncd_rx_en:1;		// bit 3
+		u32 tx_flow:1;		// bit 4
+		u32 rx_flow:1;		// bit 5
+		u32 reserved1:2;		// bits 6-7
+		u32 loop_back:1;		// bit 8
+		u32 reserved2:7;		// bits 9-15
+		u32 reset_tx_fun:1;	// bit 16
+		u32 reset_rx_fun:1;	// bit 17
+		u32 reset_tx_mc:1;		// bit 18
+		u32 reset_rx_mc:1;		// bit 19
+		u32 reserved3:10;		// bits 20-29
+		u32 sim_reset:1;		// bit 30
+		u32 soft_reset:1;		// bit 31
+#endif
+	} bits;
+} MAC_CFG1_t, *PMAC_CFG1_t;
+
+/*
+ * structure for configuration #2 reg in mac address map.
+ * located at address 0x5004
+ */
+typedef union _MAC_CFG2_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved3:16;		// bits 16-31
+		u32 preamble_len:4;	// bits 12-15
+		u32 reserved2:2;		// bits 10-11
+		u32 if_mode:2;		// bits 8-9
+		u32 reserved1:2;		// bits 6-7
+		u32 huge_frame:1;		// bit 5
+		u32 len_check:1;		// bit 4
+		u32 undefined:1;		// bit 3
+		u32 pad_crc:1;		// bit 2
+		u32 crc_enable:1;		// bit 1
+		u32 full_duplex:1;		// bit 0
+#else
+		u32 full_duplex:1;		// bit 0
+		u32 crc_enable:1;		// bit 1
+		u32 pad_crc:1;		// bit 2
+		u32 undefined:1;		// bit 3
+		u32 len_check:1;		// bit 4
+		u32 huge_frame:1;		// bit 5
+		u32 reserved1:2;		// bits 6-7
+		u32 if_mode:2;		// bits 8-9
+		u32 reserved2:2;		// bits 10-11
+		u32 preamble_len:4;	// bits 12-15
+		u32 reserved3:16;		// bits 16-31
+#endif
+	} bits;
+} MAC_CFG2_t, *PMAC_CFG2_t;
+
+/*
+ * structure for Interpacket gap reg in mac address map.
+ * located at address 0x5008
+ */
+typedef union _MAC_IPG_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:1;		// bit 31
+		u32 non_B2B_ipg_1:7;	// bits 24-30
+		u32 undefined2:1;		// bit 23
+		u32 non_B2B_ipg_2:7;	// bits 16-22
+		u32 min_ifg_enforce:8;	// bits 8-15
+		u32 undefined1:1;		// bit 7
+		u32 B2B_ipg:7;		// bits 0-6
+#else
+		u32 B2B_ipg:7;		// bits 0-6
+		u32 undefined1:1;		// bit 7
+		u32 min_ifg_enforce:8;	// bits 8-15
+		u32 non_B2B_ipg_2:7;	// bits 16-22
+		u32 undefined2:1;		// bit 23
+		u32 non_B2B_ipg_1:7;	// bits 24-30
+		u32 reserved:1;		// bit 31
+#endif
+	} bits;
+} MAC_IPG_t, *PMAC_IPG_t;
+
+/*
+ * structure for half duplex reg in mac address map.
+ * located at address 0x500C
+ */
+typedef union _MAC_HFDP_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved2:8;		// bits 24-31
+		u32 alt_beb_trunc:4;	// bits 23-20
+		u32 alt_beb_enable:1;	// bit 19
+		u32 bp_no_backoff:1;	// bit 18
+		u32 no_backoff:1;		// bit 17
+		u32 excess_defer:1;	// bit 16
+		u32 rexmit_max:4;		// bits 12-15
+		u32 reserved1:2;		// bits 10-11
+		u32 coll_window:10;	// bits 0-9
+#else
+		u32 coll_window:10;	// bits 0-9
+		u32 reserved1:2;		// bits 10-11
+		u32 rexmit_max:4;		// bits 12-15
+		u32 excess_defer:1;	// bit 16
+		u32 no_backoff:1;		// bit 17
+		u32 bp_no_backoff:1;	// bit 18
+		u32 alt_beb_enable:1;	// bit 19
+		u32 alt_beb_trunc:4;	// bits 23-20
+		u32 reserved2:8;		// bits 24-31
+#endif
+	} bits;
+} MAC_HFDP_t, *PMAC_HFDP_t;
+
+/*
+ * structure for Maximum Frame Length reg in mac address map.
+ * located at address 0x5010
+ */
+typedef union _MAC_MAX_FM_LEN_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:16;	// bits 16-31
+		u32 max_len:16;	// bits 0-15
+#else
+		u32 max_len:16;	// bits 0-15
+		u32 reserved:16;	// bits 16-31
+#endif
+	} bits;
+} MAC_MAX_FM_LEN_t, *PMAC_MAX_FM_LEN_t;
+
+/*
+ * structure for Reserve 1 reg in mac address map.
+ * located at address 0x5014 - 0x5018
+ * Defined earlier (u32)
+ */
+
+/*
+ * structure for Test reg in mac address map.
+ * located at address 0x501C
+ */
+typedef union _MAC_TEST_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:29;	// bits 3-31
+		u32 mac_test:3;	// bits 0-2
+#else
+		u32 mac_test:3;	// bits 0-2
+		u32 unused:29;	// bits 3-31
+#endif
+	} bits;
+} MAC_TEST_t, *PMAC_TEST_t;
+
+/*
+ * structure for MII Management Configuration reg in mac address map.
+ * located at address 0x5020
+ */
+typedef union _MII_MGMT_CFG_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reset_mii_mgmt:1;	// bit 31
+		u32 reserved:25;		// bits 6-30
+		u32 scan_auto_incremt:1;	// bit 5
+		u32 preamble_suppress:1;	// bit 4
+		u32 undefined:1;		// bit 3
+		u32 mgmt_clk_reset:3;	// bits 0-2
+#else
+		u32 mgmt_clk_reset:3;	// bits 0-2
+		u32 undefined:1;		// bit 3
+		u32 preamble_suppress:1;	// bit 4
+		u32 scan_auto_incremt:1;	// bit 5
+		u32 reserved:25;		// bits 6-30
+		u32 reset_mii_mgmt:1;	// bit 31
+#endif
+	} bits;
+} MII_MGMT_CFG_t, *PMII_MGMT_CFG_t;
+
+/*
+ * structure for MII Management Command reg in mac address map.
+ * located at address 0x5024
+ */
+typedef union _MII_MGMT_CMD_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:30;	// bits 2-31
+		u32 scan_cycle:1;	// bit 1
+		u32 read_cycle:1;	// bit 0
+#else
+		u32 read_cycle:1;	// bit 0
+		u32 scan_cycle:1;	// bit 1
+		u32 reserved:30;	// bits 2-31
+#endif
+	} bits;
+} MII_MGMT_CMD_t, *PMII_MGMT_CMD_t;
+
+/*
+ * structure for MII Management Address reg in mac address map.
+ * located at address 0x5028
+ */
+typedef union _MII_MGMT_ADDR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved2:19;	// bit 13-31
+		u32 phy_addr:5;	// bits 8-12
+		u32 reserved1:3;	// bits 5-7
+		u32 reg_addr:5;	// bits 0-4
+#else
+		u32 reg_addr:5;	// bits 0-4
+		u32 reserved1:3;	// bits 5-7
+		u32 phy_addr:5;	// bits 8-12
+		u32 reserved2:19;	// bit 13-31
+#endif
+	} bits;
+} MII_MGMT_ADDR_t, *PMII_MGMT_ADDR_t;
+
+/*
+ * structure for MII Management Control reg in mac address map.
+ * located at address 0x502C
+ */
+typedef union _MII_MGMT_CTRL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:16;	// bits 16-31
+		u32 phy_ctrl:16;	// bits 0-15
+#else
+		u32 phy_ctrl:16;	// bits 0-15
+		u32 reserved:16;	// bits 16-31
+#endif
+	} bits;
+} MII_MGMT_CTRL_t, *PMII_MGMT_CTRL_t;
+
+/*
+ * structure for MII Management Status reg in mac address map.
+ * located at address 0x5030
+ */
+typedef union _MII_MGMT_STAT_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:16;	// bits 16-31
+		u32 phy_stat:16;	// bits 0-15
+#else
+		u32 phy_stat:16;	// bits 0-15
+		u32 reserved:16;	// bits 16-31
+#endif
+	} bits;
+} MII_MGMT_STAT_t, *PMII_MGMT_STAT_t;
+
+/*
+ * structure for MII Management Indicators reg in mac address map.
+ * located at address 0x5034
+ */
+typedef union _MII_MGMT_INDICATOR_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:29;	// bits 3-31
+		u32 not_valid:1;	// bit 2
+		u32 scanning:1;	// bit 1
+		u32 busy:1;	// bit 0
+#else
+		u32 busy:1;	// bit 0
+		u32 scanning:1;	// bit 1
+		u32 not_valid:1;	// bit 2
+		u32 reserved:29;	// bits 3-31
+#endif
+	} bits;
+} MII_MGMT_INDICATOR_t, *PMII_MGMT_INDICATOR_t;
+
+/*
+ * structure for Interface Control reg in mac address map.
+ * located at address 0x5038
+ */
+typedef union _MAC_IF_CTRL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reset_if_module:1;	// bit 31
+		u32 reserved4:3;		// bit 28-30
+		u32 tbi_mode:1;		// bit 27
+		u32 ghd_mode:1;		// bit 26
+		u32 lhd_mode:1;		// bit 25
+		u32 phy_mode:1;		// bit 24
+		u32 reset_per_mii:1;	// bit 23
+		u32 reserved3:6;		// bits 17-22
+		u32 speed:1;		// bit 16
+		u32 reset_pe100x:1;	// bit 15
+		u32 reserved2:4;		// bits 11-14
+		u32 force_quiet:1;		// bit 10
+		u32 no_cipher:1;		// bit 9
+		u32 disable_link_fail:1;	// bit 8
+		u32 reset_gpsi:1;		// bit 7
+		u32 reserved1:6;		// bits 1-6
+		u32 enab_jab_protect:1;	// bit 0
+#else
+		u32 enab_jab_protect:1;	// bit 0
+		u32 reserved1:6;		// bits 1-6
+		u32 reset_gpsi:1;		// bit 7
+		u32 disable_link_fail:1;	// bit 8
+		u32 no_cipher:1;		// bit 9
+		u32 force_quiet:1;		// bit 10
+		u32 reserved2:4;		// bits 11-14
+		u32 reset_pe100x:1;	// bit 15
+		u32 speed:1;		// bit 16
+		u32 reserved3:6;		// bits 17-22
+		u32 reset_per_mii:1;	// bit 23
+		u32 phy_mode:1;		// bit 24
+		u32 lhd_mode:1;		// bit 25
+		u32 ghd_mode:1;		// bit 26
+		u32 tbi_mode:1;		// bit 27
+		u32 reserved4:3;		// bit 28-30
+		u32 reset_if_module:1;	// bit 31
+#endif
+	} bits;
+} MAC_IF_CTRL_t, *PMAC_IF_CTRL_t;
+
+/*
+ * structure for Interface Status reg in mac address map.
+ * located at address 0x503C
+ */
+typedef union _MAC_IF_STAT_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:22;		// bits 10-31
+		u32 excess_defer:1;	// bit 9
+		u32 clash:1;		// bit 8
+		u32 phy_jabber:1;		// bit 7
+		u32 phy_link_ok:1;		// bit 6
+		u32 phy_full_duplex:1;	// bit 5
+		u32 phy_speed:1;		// bit 4
+		u32 pe100x_link_fail:1;	// bit 3
+		u32 pe10t_loss_carrie:1;	// bit 2
+		u32 pe10t_sqe_error:1;	// bit 1
+		u32 pe10t_jabber:1;	// bit 0
+#else
+		u32 pe10t_jabber:1;	// bit 0
+		u32 pe10t_sqe_error:1;	// bit 1
+		u32 pe10t_loss_carrie:1;	// bit 2
+		u32 pe100x_link_fail:1;	// bit 3
+		u32 phy_speed:1;		// bit 4
+		u32 phy_full_duplex:1;	// bit 5
+		u32 phy_link_ok:1;		// bit 6
+		u32 phy_jabber:1;		// bit 7
+		u32 clash:1;		// bit 8
+		u32 excess_defer:1;	// bit 9
+		u32 reserved:22;		// bits 10-31
+#endif
+	} bits;
+} MAC_IF_STAT_t, *PMAC_IF_STAT_t;
+
+/*
+ * structure for Mac Station Address, Part 1 reg in mac address map.
+ * located at address 0x5040
+ */
+typedef union _MAC_STATION_ADDR1_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 Octet6:8;	// bits 24-31
+		u32 Octet5:8;	// bits 16-23
+		u32 Octet4:8;	// bits 8-15
+		u32 Octet3:8;	// bits 0-7
+#else
+		u32 Octet3:8;	// bits 0-7
+		u32 Octet4:8;	// bits 8-15
+		u32 Octet5:8;	// bits 16-23
+		u32 Octet6:8;	// bits 24-31
+#endif
+	} bits;
+} MAC_STATION_ADDR1_t, *PMAC_STATION_ADDR1_t;
+
+/*
+ * structure for Mac Station Address, Part 2 reg in mac address map.
+ * located at address 0x5044
+ */
+typedef union _MAC_STATION_ADDR2_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 Octet2:8;	// bits 24-31
+		u32 Octet1:8;	// bits 16-23
+		u32 reserved:16;	// bits 0-15
+#else
+		u32 reserved:16;	// bit 0-15
+		u32 Octet1:8;	// bits 16-23
+		u32 Octet2:8;	// bits 24-31
+#endif
+	} bits;
+} MAC_STATION_ADDR2_t, *PMAC_STATION_ADDR2_t;
+
+/*
+ * MAC Module of JAGCore Address Mapping
+ */
+typedef struct _MAC_t {					// Location:
+	MAC_CFG1_t cfg1;				//  0x5000
+	MAC_CFG2_t cfg2;				//  0x5004
+	MAC_IPG_t ipg;					//  0x5008
+	MAC_HFDP_t hfdp;				//  0x500C
+	MAC_MAX_FM_LEN_t max_fm_len;			//  0x5010
+	u32 rsv1;					//  0x5014
+	u32 rsv2;					//  0x5018
+	MAC_TEST_t mac_test;				//  0x501C
+	MII_MGMT_CFG_t mii_mgmt_cfg;			//  0x5020
+	MII_MGMT_CMD_t mii_mgmt_cmd;			//  0x5024
+	MII_MGMT_ADDR_t mii_mgmt_addr;			//  0x5028
+	MII_MGMT_CTRL_t mii_mgmt_ctrl;			//  0x502C
+	MII_MGMT_STAT_t mii_mgmt_stat;			//  0x5030
+	MII_MGMT_INDICATOR_t mii_mgmt_indicator;	//  0x5034
+	MAC_IF_CTRL_t if_ctrl;				//  0x5038
+	MAC_IF_STAT_t if_stat;				//  0x503C
+	MAC_STATION_ADDR1_t station_addr_1;		//  0x5040
+	MAC_STATION_ADDR2_t station_addr_2;		//  0x5044
+} MAC_t, *PMAC_t;
+
+/* END OF MAC REGISTER ADDRESS MAP */
+
+/* START OF MAC STAT REGISTER ADDRESS MAP */
+
+/*
+ * structure for Carry Register One and it's Mask Register reg located in mac
+ * stat address map address 0x6130 and 0x6138.
+ */
+typedef union _MAC_STAT_REG_1_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 tr64:1;	// bit 31
+		u32 tr127:1;	// bit 30
+		u32 tr255:1;	// bit 29
+		u32 tr511:1;	// bit 28
+		u32 tr1k:1;	// bit 27
+		u32 trmax:1;	// bit 26
+		u32 trmgv:1;	// bit 25
+		u32 unused:8;	// bits 17-24
+		u32 rbyt:1;	// bit 16
+		u32 rpkt:1;	// bit 15
+		u32 rfcs:1;	// bit 14
+		u32 rmca:1;	// bit 13
+		u32 rbca:1;	// bit 12
+		u32 rxcf:1;	// bit 11
+		u32 rxpf:1;	// bit 10
+		u32 rxuo:1;	// bit 9
+		u32 raln:1;	// bit 8
+		u32 rflr:1;	// bit 7
+		u32 rcde:1;	// bit 6
+		u32 rcse:1;	// bit 5
+		u32 rund:1;	// bit 4
+		u32 rovr:1;	// bit 3
+		u32 rfrg:1;	// bit 2
+		u32 rjbr:1;	// bit 1
+		u32 rdrp:1;	// bit 0
+#else
+		u32 rdrp:1;	// bit 0
+		u32 rjbr:1;	// bit 1
+		u32 rfrg:1;	// bit 2
+		u32 rovr:1;	// bit 3
+		u32 rund:1;	// bit 4
+		u32 rcse:1;	// bit 5
+		u32 rcde:1;	// bit 6
+		u32 rflr:1;	// bit 7
+		u32 raln:1;	// bit 8
+		u32 rxuo:1;	// bit 9
+		u32 rxpf:1;	// bit 10
+		u32 rxcf:1;	// bit 11
+		u32 rbca:1;	// bit 12
+		u32 rmca:1;	// bit 13
+		u32 rfcs:1;	// bit 14
+		u32 rpkt:1;	// bit 15
+		u32 rbyt:1;	// bit 16
+		u32 unused:8;	// bits 17-24
+		u32 trmgv:1;	// bit 25
+		u32 trmax:1;	// bit 26
+		u32 tr1k:1;	// bit 27
+		u32 tr511:1;	// bit 28
+		u32 tr255:1;	// bit 29
+		u32 tr127:1;	// bit 30
+		u32 tr64:1;	// bit 31
+#endif
+	} bits;
+} MAC_STAT_REG_1_t, *PMAC_STAT_REG_1_t;
+
+/*
+ * structure for Carry Register Two Mask Register reg in mac stat address map.
+ * located at address 0x613C
+ */
+typedef union _MAC_STAT_REG_2_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:12;	// bit 20-31
+		u32 tjbr:1;	// bit 19
+		u32 tfcs:1;	// bit 18
+		u32 txcf:1;	// bit 17
+		u32 tovr:1;	// bit 16
+		u32 tund:1;	// bit 15
+		u32 tfrg:1;	// bit 14
+		u32 tbyt:1;	// bit 13
+		u32 tpkt:1;	// bit 12
+		u32 tmca:1;	// bit 11
+		u32 tbca:1;	// bit 10
+		u32 txpf:1;	// bit 9
+		u32 tdfr:1;	// bit 8
+		u32 tedf:1;	// bit 7
+		u32 tscl:1;	// bit 6
+		u32 tmcl:1;	// bit 5
+		u32 tlcl:1;	// bit 4
+		u32 txcl:1;	// bit 3
+		u32 tncl:1;	// bit 2
+		u32 tpfh:1;	// bit 1
+		u32 tdrp:1;	// bit 0
+#else
+		u32 tdrp:1;	// bit 0
+		u32 tpfh:1;	// bit 1
+		u32 tncl:1;	// bit 2
+		u32 txcl:1;	// bit 3
+		u32 tlcl:1;	// bit 4
+		u32 tmcl:1;	// bit 5
+		u32 tscl:1;	// bit 6
+		u32 tedf:1;	// bit 7
+		u32 tdfr:1;	// bit 8
+		u32 txpf:1;	// bit 9
+		u32 tbca:1;	// bit 10
+		u32 tmca:1;	// bit 11
+		u32 tpkt:1;	// bit 12
+		u32 tbyt:1;	// bit 13
+		u32 tfrg:1;	// bit 14
+		u32 tund:1;	// bit 15
+		u32 tovr:1;	// bit 16
+		u32 txcf:1;	// bit 17
+		u32 tfcs:1;	// bit 18
+		u32 tjbr:1;	// bit 19
+		u32 unused:12;	// bit 20-31
+#endif
+	} bits;
+} MAC_STAT_REG_2_t, *PMAC_STAT_REG_2_t;
+
+/*
+ * MAC STATS Module of JAGCore Address Mapping
+ */
+typedef struct _MAC_STAT_t {		// Location:
+	u32 pad[32];		//  0x6000 - 607C
+
+	// Tx/Rx 0-64 Byte Frame Counter
+	u32 TR64;			//  0x6080
+
+	// Tx/Rx 65-127 Byte Frame Counter
+	u32 TR127;			//  0x6084
+
+	// Tx/Rx 128-255 Byte Frame Counter
+	u32 TR255;			//  0x6088
+
+	// Tx/Rx 256-511 Byte Frame Counter
+	u32 TR511;			//  0x608C
+
+	// Tx/Rx 512-1023 Byte Frame Counter
+	u32 TR1K;			//  0x6090
+
+	// Tx/Rx 1024-1518 Byte Frame Counter
+	u32 TRMax;			//  0x6094
+
+	// Tx/Rx 1519-1522 Byte Good VLAN Frame Count
+	u32 TRMgv;			//  0x6098
+
+	// Rx Byte Counter
+	u32 RByt;			//  0x609C
+
+	// Rx Packet Counter
+	u32 RPkt;			//  0x60A0
+
+	// Rx FCS Error Counter
+	u32 RFcs;			//  0x60A4
+
+	// Rx Multicast Packet Counter
+	u32 RMca;			//  0x60A8
+
+	// Rx Broadcast Packet Counter
+	u32 RBca;			//  0x60AC
+
+	// Rx Control Frame Packet Counter
+	u32 RxCf;			//  0x60B0
+
+	// Rx Pause Frame Packet Counter
+	u32 RxPf;			//  0x60B4
+
+	// Rx Unknown OP Code Counter
+	u32 RxUo;			//  0x60B8
+
+	// Rx Alignment Error Counter
+	u32 RAln;			//  0x60BC
+
+	// Rx Frame Length Error Counter
+	u32 RFlr;			//  0x60C0
+
+	// Rx Code Error Counter
+	u32 RCde;			//  0x60C4
+
+	// Rx Carrier Sense Error Counter
+	u32 RCse;			//  0x60C8
+
+	// Rx Undersize Packet Counter
+	u32 RUnd;			//  0x60CC
+
+	// Rx Oversize Packet Counter
+	u32 ROvr;			//  0x60D0
+
+	// Rx Fragment Counter
+	u32 RFrg;			//  0x60D4
+
+	// Rx Jabber Counter
+	u32 RJbr;			//  0x60D8
+
+	// Rx Drop
+	u32 RDrp;			//  0x60DC
+
+	// Tx Byte Counter
+	u32 TByt;			//  0x60E0
+
+	// Tx Packet Counter
+	u32 TPkt;			//  0x60E4
+
+	// Tx Multicast Packet Counter
+	u32 TMca;			//  0x60E8
+
+	// Tx Broadcast Packet Counter
+	u32 TBca;			//  0x60EC
+
+	// Tx Pause Control Frame Counter
+	u32 TxPf;			//  0x60F0
+
+	// Tx Deferral Packet Counter
+	u32 TDfr;			//  0x60F4
+
+	// Tx Excessive Deferral Packet Counter
+	u32 TEdf;			//  0x60F8
+
+	// Tx Single Collision Packet Counter
+	u32 TScl;			//  0x60FC
+
+	// Tx Multiple Collision Packet Counter
+	u32 TMcl;			//  0x6100
+
+	// Tx Late Collision Packet Counter
+	u32 TLcl;			//  0x6104
+
+	// Tx Excessive Collision Packet Counter
+	u32 TXcl;			//  0x6108
+
+	// Tx Total Collision Packet Counter
+	u32 TNcl;			//  0x610C
+
+	// Tx Pause Frame Honored Counter
+	u32 TPfh;			//  0x6110
+
+	// Tx Drop Frame Counter
+	u32 TDrp;			//  0x6114
+
+	// Tx Jabber Frame Counter
+	u32 TJbr;			//  0x6118
+
+	// Tx FCS Error Counter
+	u32 TFcs;			//  0x611C
+
+	// Tx Control Frame Counter
+	u32 TxCf;			//  0x6120
+
+	// Tx Oversize Frame Counter
+	u32 TOvr;			//  0x6124
+
+	// Tx Undersize Frame Counter
+	u32 TUnd;			//  0x6128
+
+	// Tx Fragments Frame Counter
+	u32 TFrg;			//  0x612C
+
+	// Carry Register One Register
+	MAC_STAT_REG_1_t Carry1;	//  0x6130
+
+	// Carry Register Two Register
+	MAC_STAT_REG_2_t Carry2;	//  0x6134
+
+	// Carry Register One Mask Register
+	MAC_STAT_REG_1_t Carry1M;	//  0x6138
+
+	// Carry Register Two Mask Register
+	MAC_STAT_REG_2_t Carry2M;	//  0x613C
+} MAC_STAT_t, *PMAC_STAT_t;
+
+/* END OF MAC STAT REGISTER ADDRESS MAP */
+
+
+/* START OF MMC REGISTER ADDRESS MAP */
+
+/*
+ * structure for Main Memory Controller Control reg in mmc address map.
+ * located at address 0x7000
+ */
+typedef union _MMC_CTRL_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:25;		// bits 7-31
+		u32 force_ce:1;		// bit 6
+		u32 rxdma_disable:1;	// bit 5
+		u32 txdma_disable:1;	// bit 4
+		u32 txmac_disable:1;	// bit 3
+		u32 rxmac_disable:1;	// bit 2
+		u32 arb_disable:1;		// bit 1
+		u32 mmc_enable:1;		// bit 0
+#else
+		u32 mmc_enable:1;		// bit 0
+		u32 arb_disable:1;		// bit 1
+		u32 rxmac_disable:1;	// bit 2
+		u32 txmac_disable:1;	// bit 3
+		u32 txdma_disable:1;	// bit 4
+		u32 rxdma_disable:1;	// bit 5
+		u32 force_ce:1;		// bit 6
+		u32 reserved:25;		// bits 7-31
+#endif
+	} bits;
+} MMC_CTRL_t, *PMMC_CTRL_t;
+
+/*
+ * structure for Main Memory Controller Host Memory Access Address reg in mmc
+ * address map.  Located at address 0x7004
+ */
+typedef union _MMC_SRAM_ACCESS_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 byte_enable:16;	// bits 16-31
+		u32 reserved2:2;		// bits 14-15
+		u32 req_addr:10;		// bits 4-13
+		u32 reserved1:1;		// bit 3
+		u32 is_ctrl_word:1;	// bit 2
+		u32 wr_access:1;		// bit 1
+		u32 req_access:1;		// bit 0
+#else
+		u32 req_access:1;		// bit 0
+		u32 wr_access:1;		// bit 1
+		u32 is_ctrl_word:1;	// bit 2
+		u32 reserved1:1;		// bit 3
+		u32 req_addr:10;		// bits 4-13
+		u32 reserved2:2;		// bits 14-15
+		u32 byte_enable:16;	// bits 16-31
+#endif
+	} bits;
+} MMC_SRAM_ACCESS_t, *PMMC_SRAM_ACCESS_t;
+
+/*
+ * structure for Main Memory Controller Host Memory Access Data reg in mmc
+ * address map.  Located at address 0x7008 - 0x7014
+ * Defined earlier (u32)
+ */
+
+/*
+ * Memory Control Module of JAGCore Address Mapping
+ */
+typedef struct _MMC_t {			// Location:
+	MMC_CTRL_t mmc_ctrl;		//  0x7000
+	MMC_SRAM_ACCESS_t sram_access;	//  0x7004
+	u32 sram_word1;		//  0x7008
+	u32 sram_word2;		//  0x700C
+	u32 sram_word3;		//  0x7010
+	u32 sram_word4;		//  0x7014
+} MMC_t, *PMMC_t;
+
+/* END OF MMC REGISTER ADDRESS MAP */
+
+
+/* START OF EXP ROM REGISTER ADDRESS MAP */
+
+/*
+ * Expansion ROM Module of JAGCore Address Mapping
+ */
+
+/* Take this out until it is not empty */
+#if 0
+typedef struct _EXP_ROM_t {
+
+} EXP_ROM_t, *PEXP_ROM_t;
+#endif
+
+/* END OF EXP ROM REGISTER ADDRESS MAP */
+
+
+/*
+ * JAGCore Address Mapping
+ */
+typedef struct _ADDRESS_MAP_t {
+	GLOBAL_t global;
+	// unused section of global address map
+	u8 unused_global[4096 - sizeof(GLOBAL_t)];
+	TXDMA_t txdma;
+	// unused section of txdma address map
+	u8 unused_txdma[4096 - sizeof(TXDMA_t)];
+	RXDMA_t rxdma;
+	// unused section of rxdma address map
+	u8 unused_rxdma[4096 - sizeof(RXDMA_t)];
+	TXMAC_t txmac;
+	// unused section of txmac address map
+	u8 unused_txmac[4096 - sizeof(TXMAC_t)];
+	RXMAC_t rxmac;
+	// unused section of rxmac address map
+	u8 unused_rxmac[4096 - sizeof(RXMAC_t)];
+	MAC_t mac;
+	// unused section of mac address map
+	u8 unused_mac[4096 - sizeof(MAC_t)];
+	MAC_STAT_t macStat;
+	// unused section of mac stat address map
+	u8 unused_mac_stat[4096 - sizeof(MAC_STAT_t)];
+	MMC_t mmc;
+	// unused section of mmc address map
+	u8 unused_mmc[4096 - sizeof(MMC_t)];
+	// unused section of address map
+	u8 unused_[1015808];
+
+/* Take this out until it is not empty */
+#if 0
+	EXP_ROM_t exp_rom;
+#endif
+
+	u8 unused_exp_rom[4096];	// MGS-size TBD
+	u8 unused__[524288];	// unused section of address map
+} ADDRESS_MAP_t, *PADDRESS_MAP_t;
+
+#endif /* _ET1310_ADDRESS_MAP_H_ */
diff --git a/drivers/staging/et131x/et1310_eeprom.c b/drivers/staging/et131x/et1310_eeprom.c
new file mode 100644
index 0000000..c2b194e
--- /dev/null
+++ b/drivers/staging/et131x/et1310_eeprom.c
@@ -0,0 +1,480 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_eeprom.c - Code used to access the device's EEPROM
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+#include "et1310_eeprom.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+#include "et131x_isr.h"
+
+#include "et1310_tx.h"
+
+
+/*
+ * EEPROM Defines
+ */
+
+/* LBCIF Register Groups (addressed via 32-bit offsets) */
+#define LBCIF_DWORD0_GROUP_OFFSET       0xAC
+#define LBCIF_DWORD1_GROUP_OFFSET       0xB0
+
+/* LBCIF Registers (addressed via 8-bit offsets) */
+#define LBCIF_ADDRESS_REGISTER_OFFSET   0xAC
+#define LBCIF_DATA_REGISTER_OFFSET      0xB0
+#define LBCIF_CONTROL_REGISTER_OFFSET   0xB1
+#define LBCIF_STATUS_REGISTER_OFFSET    0xB2
+
+/* LBCIF Control Register Bits */
+#define LBCIF_CONTROL_SEQUENTIAL_READ   0x01
+#define LBCIF_CONTROL_PAGE_WRITE        0x02
+#define LBCIF_CONTROL_UNUSED1           0x04
+#define LBCIF_CONTROL_EEPROM_RELOAD     0x08
+#define LBCIF_CONTROL_UNUSED2           0x10
+#define LBCIF_CONTROL_TWO_BYTE_ADDR     0x20
+#define LBCIF_CONTROL_I2C_WRITE         0x40
+#define LBCIF_CONTROL_LBCIF_ENABLE      0x80
+
+/* LBCIF Status Register Bits */
+#define LBCIF_STATUS_PHY_QUEUE_AVAIL    0x01
+#define LBCIF_STATUS_I2C_IDLE           0x02
+#define LBCIF_STATUS_ACK_ERROR          0x04
+#define LBCIF_STATUS_GENERAL_ERROR      0x08
+#define LBCIF_STATUS_UNUSED             0x30
+#define LBCIF_STATUS_CHECKSUM_ERROR     0x40
+#define LBCIF_STATUS_EEPROM_PRESENT     0x80
+
+/* Miscellaneous Constraints */
+#define MAX_NUM_REGISTER_POLLS          1000
+#define MAX_NUM_WRITE_RETRIES           2
+
+/*
+ * Define macros that allow individual register values to be extracted from a
+ * DWORD1 register grouping
+ */
+#define EXTRACT_DATA_REGISTER(x)    (uint8_t)(x & 0xFF)
+#define EXTRACT_STATUS_REGISTER(x)  (uint8_t)((x >> 16) & 0xFF)
+#define EXTRACT_CONTROL_REG(x)      (uint8_t)((x >> 8) & 0xFF)
+
+/**
+ * EepromWriteByte - Write a byte to the ET1310's EEPROM
+ * @pAdapter: pointer to our private adapter structure
+ * @unAddress: the address to write
+ * @bData: the value to write
+ * @unEepronId: the ID of the EEPROM
+ * @unAddressingMode: how the EEPROM is to be accessed
+ *
+ * Returns SUCCESS or FAILURE
+ */
+int32_t EepromWriteByte(struct et131x_adapter *pAdapter, uint32_t unAddress,
+			uint8_t bData, uint32_t unEepromId,
+			uint32_t unAddressingMode)
+{
+        struct pci_dev *pdev = pAdapter->pdev;
+	int32_t nIndex;
+	int32_t nRetries;
+	int32_t nError = false;
+	int32_t nI2CWriteActive = 0;
+	int32_t nWriteSuccessful = 0;
+	uint8_t bControl;
+	uint8_t bStatus = 0;
+	uint32_t unDword1 = 0;
+	uint32_t unData = 0;
+
+	/*
+	 * The following excerpt is from "Serial EEPROM HW Design
+	 * Specification" Version 0.92 (9/20/2004):
+	 *
+	 * Single Byte Writes
+	 *
+	 * For an EEPROM, an I2C single byte write is defined as a START
+	 * condition followed by the device address, EEPROM address, one byte
+	 * of data and a STOP condition.  The STOP condition will trigger the
+	 * EEPROM's internally timed write cycle to the nonvolatile memory.
+	 * All inputs are disabled during this write cycle and the EEPROM will
+	 * not respond to any access until the internal write is complete.
+	 * The steps to execute a single byte write are as follows:
+	 *
+	 * 1. Check LBCIF Status Register for bits 6 & 3:2 all equal to 0 and
+	 *    bits 7,1:0 both equal to 1, at least once after reset.
+	 *    Subsequent operations need only to check that bits 1:0 are
+	 *    equal to 1 prior to starting a single byte write.
+	 *
+	 * 2. Write to the LBCIF Control Register:  bit 7=1, bit 6=1, bit 3=0,
+	 *    and bits 1:0 both =0.  Bit 5 should be set according to the
+	 *    type of EEPROM being accessed (1=two byte addressing, 0=one
+	 *    byte addressing).
+	 *
+	 * 3. Write the address to the LBCIF Address Register.
+	 *
+	 * 4. Write the data to the LBCIF Data Register (the I2C write will
+	 *    begin).
+	 *
+	 * 5. Monitor bit 1:0 of the LBCIF Status Register.  When bits 1:0 are
+	 *    both equal to 1, the I2C write has completed and the internal
+	 *    write cycle of the EEPROM is about to start. (bits 1:0 = 01 is
+	 *    a legal state while waiting from both equal to 1, but bits
+	 *    1:0 = 10 is invalid and implies that something is broken).
+	 *
+	 * 6. Check bit 3 of the LBCIF Status Register.  If  equal to 1, an
+	 *    error has occurred.
+	 *
+	 * 7. Check bit 2 of the LBCIF Status Register.  If equal to 1 an ACK
+	 *    error has occurred on the address phase of the write.  This
+	 *    could be due to an actual hardware failure or the EEPROM may
+	 *    still be in its internal write cycle from a previous write.
+	 *    This write operation was ignored and must be repeated later.
+	 *
+	 * 8. Set bit 6 of the LBCIF Control Register = 0. If another write is
+	 *    required, go to step 1.
+	 */
+
+	/* Step 1: */
+	for (nIndex = 0; nIndex < MAX_NUM_REGISTER_POLLS; nIndex++) {
+		/* Read registers grouped in DWORD1 */
+		if (pci_read_config_dword(pdev, LBCIF_DWORD1_GROUP_OFFSET,
+					  &unDword1)) {
+			nError = 1;
+			break;
+		}
+
+		bStatus = EXTRACT_STATUS_REGISTER(unDword1);
+
+		if (bStatus & LBCIF_STATUS_PHY_QUEUE_AVAIL &&
+		    bStatus & LBCIF_STATUS_I2C_IDLE) {
+		    	/* bits 1:0 are equal to 1 */
+			break;
+		}
+	}
+
+	if (nError || (nIndex >= MAX_NUM_REGISTER_POLLS)) {
+		return FAILURE;
+	}
+
+	/* Step 2: */
+	bControl = 0;
+	bControl |= LBCIF_CONTROL_LBCIF_ENABLE | LBCIF_CONTROL_I2C_WRITE;
+
+	if (unAddressingMode == DUAL_BYTE) {
+		bControl |= LBCIF_CONTROL_TWO_BYTE_ADDR;
+	}
+
+	if (pci_write_config_byte(pdev, LBCIF_CONTROL_REGISTER_OFFSET,
+				  bControl)) {
+		return FAILURE;
+	}
+
+	nI2CWriteActive = 1;
+
+	/* Prepare EEPROM address for Step 3 */
+	unAddress |= (unAddressingMode == DUAL_BYTE) ?
+	    (unEepromId << 16) : (unEepromId << 8);
+
+	for (nRetries = 0; nRetries < MAX_NUM_WRITE_RETRIES; nRetries++) {
+		/* Step 3:*/
+		if (pci_write_config_dword(pdev, LBCIF_ADDRESS_REGISTER_OFFSET,
+					   unAddress)) {
+			break;
+		}
+
+		/* Step 4: */
+		if (pci_write_config_byte(pdev, LBCIF_DATA_REGISTER_OFFSET,
+					  bData)) {
+			break;
+		}
+
+		/* Step 5: */
+		for (nIndex = 0; nIndex < MAX_NUM_REGISTER_POLLS; nIndex++) {
+			/* Read registers grouped in DWORD1 */
+			if (pci_read_config_dword(pdev,
+						  LBCIF_DWORD1_GROUP_OFFSET,
+						  &unDword1)) {
+				nError = 1;
+				break;
+			}
+
+			bStatus = EXTRACT_STATUS_REGISTER(unDword1);
+
+			if (bStatus & LBCIF_STATUS_PHY_QUEUE_AVAIL &&
+			    bStatus & LBCIF_STATUS_I2C_IDLE) {
+			    	/* I2C write complete */
+				break;
+			}
+		}
+
+		if (nError || (nIndex >= MAX_NUM_REGISTER_POLLS)) {
+			break;
+		}
+
+		/*
+		 * Step 6: Don't break here if we are revision 1, this is
+		 *	   so we do a blind write for load bug.
+	         */
+		if (bStatus & LBCIF_STATUS_GENERAL_ERROR
+		    && pAdapter->RevisionID == 0) {
+			break;
+		}
+
+		/* Step 7 */
+		if (bStatus & LBCIF_STATUS_ACK_ERROR) {
+			/*
+			 * This could be due to an actual hardware failure
+			 * or the EEPROM may still be in its internal write
+			 * cycle from a previous write. This write operation
+			 * was ignored and must be repeated later.
+			 */
+			udelay(10);
+			continue;
+		}
+
+		nWriteSuccessful = 1;
+		break;
+	}
+
+	/* Step 8: */
+	udelay(10);
+	nIndex = 0;
+	while (nI2CWriteActive) {
+		bControl &= ~LBCIF_CONTROL_I2C_WRITE;
+
+		if (pci_write_config_byte(pdev, LBCIF_CONTROL_REGISTER_OFFSET,
+					  bControl)) {
+			nWriteSuccessful = 0;
+		}
+
+		/* Do read until internal ACK_ERROR goes away meaning write
+		 * completed
+		 */
+		do {
+			pci_write_config_dword(pdev,
+					       LBCIF_ADDRESS_REGISTER_OFFSET,
+					       unAddress);
+			do {
+				pci_read_config_dword(pdev,
+					LBCIF_DATA_REGISTER_OFFSET, &unData);
+			} while ((unData & 0x00010000) == 0);
+		} while (unData & 0x00040000);
+
+		bControl = EXTRACT_CONTROL_REG(unData);
+
+		if (bControl != 0xC0 || nIndex == 10000) {
+			break;
+		}
+
+		nIndex++;
+	}
+
+	return nWriteSuccessful ? SUCCESS : FAILURE;
+}
+
+/**
+ * EepromReadByte - Read a byte from the ET1310's EEPROM
+ * @pAdapter: pointer to our private adapter structure
+ * @unAddress: the address from which to read
+ * @pbData: a pointer to a byte in which to store the value of the read
+ * @unEepronId: the ID of the EEPROM
+ * @unAddressingMode: how the EEPROM is to be accessed
+ *
+ * Returns SUCCESS or FAILURE
+ */
+int32_t EepromReadByte(struct et131x_adapter *pAdapter, uint32_t unAddress,
+		       uint8_t *pbData, uint32_t unEepromId,
+		       uint32_t unAddressingMode)
+{
+        struct pci_dev *pdev = pAdapter->pdev;
+	int32_t nIndex;
+	int32_t nError = 0;
+	uint8_t bControl;
+	uint8_t bStatus = 0;
+	uint32_t unDword1 = 0;
+
+	/*
+	 * The following excerpt is from "Serial EEPROM HW Design
+	 * Specification" Version 0.92 (9/20/2004):
+	 *
+	 * Single Byte Reads
+	 *
+	 * A single byte read is similar to the single byte write, with the
+	 * exception of the data flow:
+	 *
+	 * 1. Check LBCIF Status Register for bits 6 & 3:2 all equal to 0 and
+	 *    bits 7,1:0 both equal to 1, at least once after reset.
+	 *    Subsequent operations need only to check that bits 1:0 are equal
+	 *    to 1 prior to starting a single byte read.
+	 *
+	 * 2. Write to the LBCIF Control Register:  bit 7=1, bit 6=0, bit 3=0,
+	 *    and bits 1:0 both =0.  Bit 5 should be set according to the type
+	 *    of EEPROM being accessed (1=two byte addressing, 0=one byte
+	 *    addressing).
+	 *
+	 * 3. Write the address to the LBCIF Address Register (I2C read will
+	 *    begin).
+	 *
+	 * 4. Monitor bit 0 of the LBCIF Status Register.  When =1, I2C read
+	 *    is complete. (if bit 1 =1 and bit 0 stays =0, a hardware failure
+	 *    has occurred).
+	 *
+	 * 5. Check bit 2 of the LBCIF Status Register.  If =1, then an error
+	 *    has occurred.  The data that has been returned from the PHY may
+	 *    be invalid.
+	 *
+	 * 6. Regardless of error status, read data byte from LBCIF Data
+	 *    Register.  If another byte is required, go to step 1.
+	 */
+
+	/* Step 1: */
+	for (nIndex = 0; nIndex < MAX_NUM_REGISTER_POLLS; nIndex++) {
+		/* Read registers grouped in DWORD1 */
+		if (pci_read_config_dword(pdev, LBCIF_DWORD1_GROUP_OFFSET,
+					  &unDword1)) {
+			nError = 1;
+			break;
+		}
+
+		bStatus = EXTRACT_STATUS_REGISTER(unDword1);
+
+		if (bStatus & LBCIF_STATUS_PHY_QUEUE_AVAIL &&
+		    bStatus & LBCIF_STATUS_I2C_IDLE) {
+			/* bits 1:0 are equal to 1 */
+			break;
+		}
+	}
+
+	if (nError || (nIndex >= MAX_NUM_REGISTER_POLLS)) {
+		return FAILURE;
+	}
+
+	/* Step 2: */
+	bControl = 0;
+	bControl |= LBCIF_CONTROL_LBCIF_ENABLE;
+
+	if (unAddressingMode == DUAL_BYTE) {
+		bControl |= LBCIF_CONTROL_TWO_BYTE_ADDR;
+	}
+
+	if (pci_write_config_byte(pdev, LBCIF_CONTROL_REGISTER_OFFSET,
+				  bControl)) {
+		return FAILURE;
+	}
+
+	/* Step 3: */
+	unAddress |= (unAddressingMode == DUAL_BYTE) ?
+	    (unEepromId << 16) : (unEepromId << 8);
+
+	if (pci_write_config_dword(pdev, LBCIF_ADDRESS_REGISTER_OFFSET,
+				   unAddress)) {
+		return FAILURE;
+	}
+
+	/* Step 4: */
+	for (nIndex = 0; nIndex < MAX_NUM_REGISTER_POLLS; nIndex++) {
+		/* Read registers grouped in DWORD1 */
+		if (pci_read_config_dword(pdev, LBCIF_DWORD1_GROUP_OFFSET,
+					  &unDword1)) {
+			nError = 1;
+			break;
+		}
+
+		bStatus = EXTRACT_STATUS_REGISTER(unDword1);
+
+		if (bStatus & LBCIF_STATUS_PHY_QUEUE_AVAIL
+		    && bStatus & LBCIF_STATUS_I2C_IDLE) {
+			/* I2C read complete */
+			break;
+		}
+	}
+
+	if (nError || (nIndex >= MAX_NUM_REGISTER_POLLS)) {
+		return FAILURE;
+	}
+
+	/* Step 6: */
+	*pbData = EXTRACT_DATA_REGISTER(unDword1);
+
+	return (bStatus & LBCIF_STATUS_ACK_ERROR) ? FAILURE : SUCCESS;
+}
diff --git a/drivers/staging/et131x/et1310_eeprom.h b/drivers/staging/et131x/et1310_eeprom.h
new file mode 100644
index 0000000..9b6f8ad
--- /dev/null
+++ b/drivers/staging/et131x/et1310_eeprom.h
@@ -0,0 +1,89 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_eeprom.h - Defines, structs, enums, prototypes, etc. used for EEPROM
+ *                   access routines
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET1310_EEPROM_H__
+#define __ET1310_EEPROM_H__
+
+#include "et1310_address_map.h"
+
+#ifndef SUCCESS
+#define SUCCESS		0
+#define FAILURE		1
+#endif
+
+#ifndef READ
+#define READ		0
+#define WRITE		1
+#endif
+
+#ifndef SINGLE_BYTE
+#define SINGLE_BYTE	0
+#define DUAL_BYTE	1
+#endif
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+int32_t EepromWriteByte(struct et131x_adapter *adapter, u32 unAddress,
+			u8 bData, u32 unEepromId,
+			u32 unAddressingMode);
+int32_t EepromReadByte(struct et131x_adapter *adapter, u32 unAddress,
+		       u8 *pbData, u32 unEepromId,
+		       u32 unAddressingMode);
+
+#endif /* _ET1310_EEPROM_H_ */
diff --git a/drivers/staging/et131x/et1310_jagcore.c b/drivers/staging/et131x/et1310_jagcore.c
new file mode 100644
index 0000000..993b30e
--- /dev/null
+++ b/drivers/staging/et131x/et1310_jagcore.c
@@ -0,0 +1,220 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_jagcore.c - All code pertaining to the ET1301/ET131x's JAGcore
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+/**
+ * ConfigGlobalRegs - Used to configure the global registers on the JAGCore
+ * @pAdpater: pointer to our adapter structure
+ */
+void ConfigGlobalRegs(struct et131x_adapter *pAdapter)
+{
+	struct _GLOBAL_t __iomem *pGbl = &pAdapter->CSRAddress->global;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	if (pAdapter->RegistryPhyLoopbk == false) {
+		if (pAdapter->RegistryJumboPacket < 2048) {
+			/* Tx / RxDMA and Tx/Rx MAC interfaces have a 1k word
+			 * block of RAM that the driver can split between Tx
+			 * and Rx as it desires.  Our default is to split it
+			 * 50/50:
+			 */
+			writel(0, &pGbl->rxq_start_addr.value);
+			writel(pAdapter->RegistryRxMemEnd,
+			       &pGbl->rxq_end_addr.value);
+			writel(pAdapter->RegistryRxMemEnd + 1,
+			       &pGbl->txq_start_addr.value);
+			writel(INTERNAL_MEM_SIZE - 1,
+			       &pGbl->txq_end_addr.value);
+		} else if (pAdapter->RegistryJumboPacket < 8192) {
+			/* For jumbo packets > 2k but < 8k, split 50-50. */
+			writel(0, &pGbl->rxq_start_addr.value);
+			writel(INTERNAL_MEM_RX_OFFSET,
+			       &pGbl->rxq_end_addr.value);
+			writel(INTERNAL_MEM_RX_OFFSET + 1,
+			       &pGbl->txq_start_addr.value);
+			writel(INTERNAL_MEM_SIZE - 1,
+			       &pGbl->txq_end_addr.value);
+		} else {
+			/* 9216 is the only packet size greater than 8k that
+			 * is available. The Tx buffer has to be big enough
+			 * for one whole packet on the Tx side. We'll make
+			 * the Tx 9408, and give the rest to Rx
+			 */
+			writel(0x0000, &pGbl->rxq_start_addr.value);
+			writel(0x01b3, &pGbl->rxq_end_addr.value);
+			writel(0x01b4, &pGbl->txq_start_addr.value);
+			writel(INTERNAL_MEM_SIZE - 1,
+			       &pGbl->txq_end_addr.value);
+		}
+
+		/* Initialize the loopback register. Disable all loopbacks. */
+		writel(0, &pGbl->loopback.value);
+	} else {
+		/* For PHY Line loopback, the memory is configured as if Tx
+		 * and Rx both have all the memory.  This is because the
+		 * RxMAC will write data into the space, and the TxMAC will
+		 * read it out.
+		 */
+		writel(0, &pGbl->rxq_start_addr.value);
+		writel(INTERNAL_MEM_SIZE - 1, &pGbl->rxq_end_addr.value);
+		writel(0, &pGbl->txq_start_addr.value);
+		writel(INTERNAL_MEM_SIZE - 1, &pGbl->txq_end_addr.value);
+
+		/* Initialize the loopback register (MAC loopback). */
+		writel(1, &pGbl->loopback.value);
+	}
+
+	/* MSI Register */
+	writel(0, &pGbl->msi_config.value);
+
+	/* By default, disable the watchdog timer.  It will be enabled when
+	 * a packet is queued.
+	 */
+	writel(0, &pGbl->watchdog_timer);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * ConfigMMCRegs - Used to configure the main memory registers in the JAGCore
+ * @pAdapter: pointer to our adapter structure
+ */
+void ConfigMMCRegs(struct et131x_adapter *pAdapter)
+{
+	MMC_CTRL_t mmc_ctrl = { 0 };
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* All we need to do is initialize the Memory Control Register */
+	mmc_ctrl.bits.force_ce = 0x0;
+	mmc_ctrl.bits.rxdma_disable = 0x0;
+	mmc_ctrl.bits.txdma_disable = 0x0;
+	mmc_ctrl.bits.txmac_disable = 0x0;
+	mmc_ctrl.bits.rxmac_disable = 0x0;
+	mmc_ctrl.bits.arb_disable = 0x0;
+	mmc_ctrl.bits.mmc_enable = 0x1;
+
+	writel(mmc_ctrl.value, &pAdapter->CSRAddress->mmc.mmc_ctrl.value);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void et131x_enable_interrupts(struct et131x_adapter *adapter)
+{
+	uint32_t MaskValue;
+
+	/* Enable all global interrupts */
+	if ((adapter->FlowControl == TxOnly) || (adapter->FlowControl == Both)) {
+		MaskValue = INT_MASK_ENABLE;
+	} else {
+		MaskValue = INT_MASK_ENABLE_NO_FLOW;
+	}
+
+	if (adapter->DriverNoPhyAccess) {
+		MaskValue |= 0x10000;
+	}
+
+	adapter->CachedMaskValue.value = MaskValue;
+	writel(MaskValue, &adapter->CSRAddress->global.int_mask.value);
+}
+
+void et131x_disable_interrupts(struct et131x_adapter * adapter)
+{
+	/* Disable all global interrupts */
+	adapter->CachedMaskValue.value = INT_MASK_DISABLE;
+	writel(INT_MASK_DISABLE, &adapter->CSRAddress->global.int_mask.value);
+}
diff --git a/drivers/staging/et131x/et1310_jagcore.h b/drivers/staging/et131x/et1310_jagcore.h
new file mode 100644
index 0000000..9fc8293
--- /dev/null
+++ b/drivers/staging/et131x/et1310_jagcore.h
@@ -0,0 +1,112 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_jagcore.h - Defines, structs, enums, prototypes, etc. pertaining to
+ *                    the JAGCore
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET1310_JAGCORE_H__
+#define __ET1310_JAGCORE_H__
+
+#include "et1310_address_map.h"
+
+
+#define INTERNAL_MEM_SIZE       0x400	//1024 of internal memory
+#define INTERNAL_MEM_RX_OFFSET  0x1FF	//50%   Tx, 50%   Rx
+
+#define REGS_MAX_ARRAY          4096
+
+/*
+ * For interrupts, normal running is:
+ *       rxdma_xfr_done, phy_interrupt, mac_stat_interrupt,
+ *       watchdog_interrupt & txdma_xfer_done
+ *
+ * In both cases, when flow control is enabled for either Tx or bi-direction,
+ * we additional enable rx_fbr0_low and rx_fbr1_low, so we know when the
+ * buffer rings are running low.
+ */
+#define INT_MASK_DISABLE            0xffffffff
+
+// NOTE: Masking out MAC_STAT Interrupt for now...
+//#define INT_MASK_ENABLE             0xfff6bf17
+//#define INT_MASK_ENABLE_NO_FLOW     0xfff6bfd7
+#define INT_MASK_ENABLE             0xfffebf17
+#define INT_MASK_ENABLE_NO_FLOW     0xfffebfd7
+
+/* DATA STRUCTURES FOR DIRECT REGISTER ACCESS */
+
+typedef struct {
+	u8 bReadWrite;
+	u32 nRegCount;
+	u32 nData[REGS_MAX_ARRAY];
+	u32 nOffsets[REGS_MAX_ARRAY];
+} JAGCORE_ACCESS_REGS, *PJAGCORE_ACCESS_REGS;
+
+typedef struct {
+	u8 bReadWrite;
+	u32 nDataWidth;
+	u32 nRegCount;
+	u32 nOffsets[REGS_MAX_ARRAY];
+	u32 nData[REGS_MAX_ARRAY];
+} PCI_CFG_SPACE_REGS, *PPCI_CFG_SPACE_REGS;
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+void ConfigGlobalRegs(struct et131x_adapter *pAdapter);
+void ConfigMMCRegs(struct et131x_adapter *pAdapter);
+void et131x_enable_interrupts(struct et131x_adapter *adapter);
+void et131x_disable_interrupts(struct et131x_adapter *adapter);
+
+#endif /* __ET1310_JAGCORE_H__ */
diff --git a/drivers/staging/et131x/et1310_mac.c b/drivers/staging/et131x/et1310_mac.c
new file mode 100644
index 0000000..1924968
--- /dev/null
+++ b/drivers/staging/et131x/et1310_mac.c
@@ -0,0 +1,792 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_mac.c - All code and routines pertaining to the MAC
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/crc32.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+#include "et1310_mac.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+/**
+ * ConfigMacRegs1 - Initialize the first part of MAC regs
+ * @pAdpater: pointer to our adapter structure
+ */
+void ConfigMACRegs1(struct et131x_adapter *pAdapter)
+{
+	struct _MAC_t __iomem *pMac = &pAdapter->CSRAddress->mac;
+	MAC_STATION_ADDR1_t station1;
+	MAC_STATION_ADDR2_t station2;
+	MAC_IPG_t ipg;
+	MAC_HFDP_t hfdp;
+	MII_MGMT_CFG_t mii_mgmt_cfg;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* First we need to reset everything.  Write to MAC configuration
+	 * register 1 to perform reset.
+	 */
+	writel(0xC00F0000, &pMac->cfg1.value);
+
+	/* Next lets configure the MAC Inter-packet gap register */
+	ipg.bits.non_B2B_ipg_1 = 0x38;		// 58d
+	ipg.bits.non_B2B_ipg_2 = 0x58;		// 88d
+	ipg.bits.min_ifg_enforce = 0x50;	// 80d
+	ipg.bits.B2B_ipg = 0x60;		// 96d
+	writel(ipg.value, &pMac->ipg.value);
+
+	/* Next lets configure the MAC Half Duplex register */
+	hfdp.bits.alt_beb_trunc = 0xA;
+	hfdp.bits.alt_beb_enable = 0x0;
+	hfdp.bits.bp_no_backoff = 0x0;
+	hfdp.bits.no_backoff = 0x0;
+	hfdp.bits.excess_defer = 0x1;
+	hfdp.bits.rexmit_max = 0xF;
+	hfdp.bits.coll_window = 0x37;		// 55d
+	writel(hfdp.value, &pMac->hfdp.value);
+
+	/* Next lets configure the MAC Interface Control register */
+	writel(0, &pMac->if_ctrl.value);
+
+	/* Let's move on to setting up the mii managment configuration */
+	mii_mgmt_cfg.bits.reset_mii_mgmt = 0;
+	mii_mgmt_cfg.bits.scan_auto_incremt = 0;
+	mii_mgmt_cfg.bits.preamble_suppress = 0;
+	mii_mgmt_cfg.bits.mgmt_clk_reset = 0x7;
+	writel(mii_mgmt_cfg.value, &pMac->mii_mgmt_cfg.value);
+
+	/* Next lets configure the MAC Station Address register.  These
+	 * values are read from the EEPROM during initialization and stored
+	 * in the adapter structure.  We write what is stored in the adapter
+	 * structure to the MAC Station Address registers high and low.  This
+	 * station address is used for generating and checking pause control
+	 * packets.
+	 */
+	station2.bits.Octet1 = pAdapter->CurrentAddress[0];
+	station2.bits.Octet2 = pAdapter->CurrentAddress[1];
+	station1.bits.Octet3 = pAdapter->CurrentAddress[2];
+	station1.bits.Octet4 = pAdapter->CurrentAddress[3];
+	station1.bits.Octet5 = pAdapter->CurrentAddress[4];
+	station1.bits.Octet6 = pAdapter->CurrentAddress[5];
+	writel(station1.value, &pMac->station_addr_1.value);
+	writel(station2.value, &pMac->station_addr_2.value);
+
+	/* Max ethernet packet in bytes that will passed by the mac without
+	 * being truncated.  Allow the MAC to pass 4 more than our max packet
+	 * size.  This is 4 for the Ethernet CRC.
+	 *
+	 * Packets larger than (RegistryJumboPacket) that do not contain a
+	 * VLAN ID will be dropped by the Rx function.
+	 */
+	writel(pAdapter->RegistryJumboPacket + 4, &pMac->max_fm_len.value);
+
+	/* clear out MAC config reset */
+	writel(0, &pMac->cfg1.value);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * ConfigMacRegs2 - Initialize the second part of MAC regs
+ * @pAdpater: pointer to our adapter structure
+ */
+void ConfigMACRegs2(struct et131x_adapter *pAdapter)
+{
+	int32_t delay = 0;
+	struct _MAC_t __iomem *pMac = &pAdapter->CSRAddress->mac;
+	MAC_CFG1_t cfg1;
+	MAC_CFG2_t cfg2;
+	MAC_IF_CTRL_t ifctrl;
+	TXMAC_CTL_t ctl;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	ctl.value = readl(&pAdapter->CSRAddress->txmac.ctl.value);
+	cfg1.value = readl(&pMac->cfg1.value);
+	cfg2.value = readl(&pMac->cfg2.value);
+	ifctrl.value = readl(&pMac->if_ctrl.value);
+
+	if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) {
+		cfg2.bits.if_mode = 0x2;
+		ifctrl.bits.phy_mode = 0x0;
+	} else {
+		cfg2.bits.if_mode = 0x1;
+		ifctrl.bits.phy_mode = 0x1;
+	}
+
+	/* We need to enable Rx/Tx */
+	cfg1.bits.rx_enable = 0x1;
+	cfg1.bits.tx_enable = 0x1;
+
+	/* Set up flow control */
+	cfg1.bits.tx_flow = 0x1;
+
+	if ((pAdapter->FlowControl == RxOnly) ||
+	    (pAdapter->FlowControl == Both)) {
+		cfg1.bits.rx_flow = 0x1;
+	} else {
+		cfg1.bits.rx_flow = 0x0;
+	}
+
+	/* Initialize loop back to off */
+	cfg1.bits.loop_back = 0;
+
+	writel(cfg1.value, &pMac->cfg1.value);
+
+	/* Now we need to initialize the MAC Configuration 2 register */
+	cfg2.bits.preamble_len = 0x7;
+	cfg2.bits.huge_frame = 0x0;
+	/* LENGTH FIELD CHECKING bit4: Set this bit to cause the MAC to check
+	 * the frame's length field to ensure it matches the actual data
+	 * field length. Clear this bit if no length field checking is
+	 * desired. Its default is 0.
+	 */
+	cfg2.bits.len_check = 0x1;
+
+	if (pAdapter->RegistryPhyLoopbk == false) {
+		cfg2.bits.pad_crc = 0x1;
+		cfg2.bits.crc_enable = 0x1;
+	} else {
+		cfg2.bits.pad_crc = 0;
+		cfg2.bits.crc_enable = 0;
+	}
+
+	/* 1 - full duplex, 0 - half-duplex */
+	cfg2.bits.full_duplex = pAdapter->uiDuplexMode;
+	ifctrl.bits.ghd_mode = !pAdapter->uiDuplexMode;
+
+	writel(ifctrl.value, &pMac->if_ctrl.value);
+	writel(cfg2.value, &pMac->cfg2.value);
+
+	do {
+		udelay(10);
+		delay++;
+		cfg1.value = readl(&pMac->cfg1.value);
+	} while ((!cfg1.bits.syncd_rx_en ||
+		  !cfg1.bits.syncd_tx_en) &&
+		 delay < 100);
+
+	if (delay == 100) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Syncd bits did not respond correctly cfg1 word 0x%08x\n",
+			  cfg1.value);
+	}
+
+	DBG_TRACE(et131x_dbginfo,
+		  "Speed %d, Dup %d, CFG1 0x%08x, CFG2 0x%08x, if_ctrl 0x%08x\n",
+		  pAdapter->uiLinkSpeed, pAdapter->uiDuplexMode,
+		  readl(&pMac->cfg1.value), readl(&pMac->cfg2.value),
+		  readl(&pMac->if_ctrl.value));
+
+	/* Enable TXMAC */
+	ctl.bits.txmac_en = 0x1;
+	ctl.bits.fc_disable = 0x1;
+	writel(ctl.value, &pAdapter->CSRAddress->txmac.ctl.value);
+
+	/* Ready to start the RXDMA/TXDMA engine */
+	if (!MP_TEST_FLAG(pAdapter, fMP_ADAPTER_LOWER_POWER)) {
+		et131x_rx_dma_enable(pAdapter);
+		et131x_tx_dma_enable(pAdapter);
+	} else {
+		DBG_WARNING(et131x_dbginfo,
+			    "Didn't enable Rx/Tx due to low-power mode\n");
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void ConfigRxMacRegs(struct et131x_adapter *pAdapter)
+{
+	struct _RXMAC_t __iomem *pRxMac = &pAdapter->CSRAddress->rxmac;
+	RXMAC_WOL_SA_LO_t sa_lo;
+	RXMAC_WOL_SA_HI_t sa_hi;
+	RXMAC_PF_CTRL_t pf_ctrl = { 0 };
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Disable the MAC while it is being configured (also disable WOL) */
+	writel(0x8, &pRxMac->ctrl.value);
+
+	/* Initialize WOL to disabled. */
+	writel(0, &pRxMac->crc0.value);
+	writel(0, &pRxMac->crc12.value);
+	writel(0, &pRxMac->crc34.value);
+
+	/* We need to set the WOL mask0 - mask4 next.  We initialize it to
+	 * its default Values of 0x00000000 because there are not WOL masks
+	 * as of this time.
+	 */
+	writel(0, &pRxMac->mask0_word0);
+	writel(0, &pRxMac->mask0_word1);
+	writel(0, &pRxMac->mask0_word2);
+	writel(0, &pRxMac->mask0_word3);
+
+	writel(0, &pRxMac->mask1_word0);
+	writel(0, &pRxMac->mask1_word1);
+	writel(0, &pRxMac->mask1_word2);
+	writel(0, &pRxMac->mask1_word3);
+
+	writel(0, &pRxMac->mask2_word0);
+	writel(0, &pRxMac->mask2_word1);
+	writel(0, &pRxMac->mask2_word2);
+	writel(0, &pRxMac->mask2_word3);
+
+	writel(0, &pRxMac->mask3_word0);
+	writel(0, &pRxMac->mask3_word1);
+	writel(0, &pRxMac->mask3_word2);
+	writel(0, &pRxMac->mask3_word3);
+
+	writel(0, &pRxMac->mask4_word0);
+	writel(0, &pRxMac->mask4_word1);
+	writel(0, &pRxMac->mask4_word2);
+	writel(0, &pRxMac->mask4_word3);
+
+	/* Lets setup the WOL Source Address */
+	sa_lo.bits.sa3 = pAdapter->CurrentAddress[2];
+	sa_lo.bits.sa4 = pAdapter->CurrentAddress[3];
+	sa_lo.bits.sa5 = pAdapter->CurrentAddress[4];
+	sa_lo.bits.sa6 = pAdapter->CurrentAddress[5];
+	writel(sa_lo.value, &pRxMac->sa_lo.value);
+
+	sa_hi.bits.sa1 = pAdapter->CurrentAddress[0];
+	sa_hi.bits.sa2 = pAdapter->CurrentAddress[1];
+	writel(sa_hi.value, &pRxMac->sa_hi.value);
+
+	/* Disable all Packet Filtering */
+	writel(0, &pRxMac->pf_ctrl.value);
+
+	/* Let's initialize the Unicast Packet filtering address */
+	if (pAdapter->PacketFilter & ET131X_PACKET_TYPE_DIRECTED) {
+		SetupDeviceForUnicast(pAdapter);
+		pf_ctrl.bits.filter_uni_en = 1;
+	} else {
+		writel(0, &pRxMac->uni_pf_addr1.value);
+		writel(0, &pRxMac->uni_pf_addr2.value);
+		writel(0, &pRxMac->uni_pf_addr3.value);
+	}
+
+	/* Let's initialize the Multicast hash */
+	if (pAdapter->PacketFilter & ET131X_PACKET_TYPE_ALL_MULTICAST) {
+		pf_ctrl.bits.filter_multi_en = 0;
+	} else {
+		pf_ctrl.bits.filter_multi_en = 1;
+		SetupDeviceForMulticast(pAdapter);
+	}
+
+	/* Runt packet filtering.  Didn't work in version A silicon. */
+	pf_ctrl.bits.min_pkt_size = NIC_MIN_PACKET_SIZE + 4;
+	pf_ctrl.bits.filter_frag_en = 1;
+
+	if (pAdapter->RegistryJumboPacket > 8192) {
+		RXMAC_MCIF_CTRL_MAX_SEG_t mcif_ctrl_max_seg;
+
+		/* In order to transmit jumbo packets greater than 8k, the
+		 * FIFO between RxMAC and RxDMA needs to be reduced in size
+		 * to (16k - Jumbo packet size).  In order to implement this,
+		 * we must use "cut through" mode in the RxMAC, which chops
+		 * packets down into segments which are (max_size * 16).  In
+		 * this case we selected 256 bytes, since this is the size of
+		 * the PCI-Express TLP's that the 1310 uses.
+		 */
+		mcif_ctrl_max_seg.bits.seg_en = 0x1;
+		mcif_ctrl_max_seg.bits.fc_en = 0x0;
+		mcif_ctrl_max_seg.bits.max_size = 0x10;
+
+		writel(mcif_ctrl_max_seg.value,
+		       &pRxMac->mcif_ctrl_max_seg.value);
+	} else {
+		writel(0, &pRxMac->mcif_ctrl_max_seg.value);
+	}
+
+	/* Initialize the MCIF water marks */
+	writel(0, &pRxMac->mcif_water_mark.value);
+
+	/*  Initialize the MIF control */
+	writel(0, &pRxMac->mif_ctrl.value);
+
+	/* Initialize the Space Available Register */
+	writel(0, &pRxMac->space_avail.value);
+
+	/* Initialize the the mif_ctrl register
+	 * bit 3:  Receive code error. One or more nibbles were signaled as
+	 *	   errors  during the reception of the packet.  Clear this
+	 *	   bit in Gigabit, set it in 100Mbit.  This was derived
+	 *	   experimentally at UNH.
+	 * bit 4:  Receive CRC error. The packet's CRC did not match the
+	 *	   internally generated CRC.
+	 * bit 5:  Receive length check error. Indicates that frame length
+	 *	   field value in the packet does not match the actual data
+	 *	   byte length and is not a type field.
+	 * bit 16: Receive frame truncated.
+	 * bit 17: Drop packet enable
+	 */
+	if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_100MBPS) {
+		writel(0x30038, &pRxMac->mif_ctrl.value);
+	} else {
+		writel(0x30030, &pRxMac->mif_ctrl.value);
+	}
+
+	/* Finally we initialize RxMac to be enabled & WOL disabled.  Packet
+	 * filter is always enabled since it is where the runt packets are
+	 * supposed to be dropped.  For version A silicon, runt packet
+	 * dropping doesn't work, so it is disabled in the pf_ctrl register,
+	 * but we still leave the packet filter on.
+	 */
+	writel(pf_ctrl.value, &pRxMac->pf_ctrl.value);
+	writel(0x9, &pRxMac->ctrl.value);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void ConfigTxMacRegs(struct et131x_adapter *pAdapter)
+{
+	struct _TXMAC_t __iomem *pTxMac = &pAdapter->CSRAddress->txmac;
+	TXMAC_CF_PARAM_t Local;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* We need to update the Control Frame Parameters
+	 * cfpt - control frame pause timer set to 64 (0x40)
+	 * cfep - control frame extended pause timer set to 0x0
+	 */
+	if (pAdapter->FlowControl == None) {
+		writel(0, &pTxMac->cf_param.value);
+	} else {
+		Local.bits.cfpt = 0x40;
+		Local.bits.cfep = 0x0;
+		writel(Local.value, &pTxMac->cf_param.value);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void ConfigMacStatRegs(struct et131x_adapter *pAdapter)
+{
+	struct _MAC_STAT_t __iomem *pDevMacStat =
+		&pAdapter->CSRAddress->macStat;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Next we need to initialize all the MAC_STAT registers to zero on
+	 * the device.
+	 */
+	writel(0, &pDevMacStat->RFcs);
+	writel(0, &pDevMacStat->RAln);
+	writel(0, &pDevMacStat->RFlr);
+	writel(0, &pDevMacStat->RDrp);
+	writel(0, &pDevMacStat->RCde);
+	writel(0, &pDevMacStat->ROvr);
+	writel(0, &pDevMacStat->RFrg);
+
+	writel(0, &pDevMacStat->TScl);
+	writel(0, &pDevMacStat->TDfr);
+	writel(0, &pDevMacStat->TMcl);
+	writel(0, &pDevMacStat->TLcl);
+	writel(0, &pDevMacStat->TNcl);
+	writel(0, &pDevMacStat->TOvr);
+	writel(0, &pDevMacStat->TUnd);
+
+	/* Unmask any counters that we want to track the overflow of.
+	 * Initially this will be all counters.  It may become clear later
+	 * that we do not need to track all counters.
+	 */
+	{
+		MAC_STAT_REG_1_t Carry1M = { 0xffffffff };
+
+		Carry1M.bits.rdrp = 0;
+		Carry1M.bits.rjbr = 1;
+		Carry1M.bits.rfrg = 0;
+		Carry1M.bits.rovr = 0;
+		Carry1M.bits.rund = 1;
+		Carry1M.bits.rcse = 1;
+		Carry1M.bits.rcde = 0;
+		Carry1M.bits.rflr = 0;
+		Carry1M.bits.raln = 0;
+		Carry1M.bits.rxuo = 1;
+		Carry1M.bits.rxpf = 1;
+		Carry1M.bits.rxcf = 1;
+		Carry1M.bits.rbca = 1;
+		Carry1M.bits.rmca = 1;
+		Carry1M.bits.rfcs = 0;
+		Carry1M.bits.rpkt = 1;
+		Carry1M.bits.rbyt = 1;
+		Carry1M.bits.trmgv = 1;
+		Carry1M.bits.trmax = 1;
+		Carry1M.bits.tr1k = 1;
+		Carry1M.bits.tr511 = 1;
+		Carry1M.bits.tr255 = 1;
+		Carry1M.bits.tr127 = 1;
+		Carry1M.bits.tr64 = 1;
+
+		writel(Carry1M.value, &pDevMacStat->Carry1M.value);
+	}
+
+	{
+		MAC_STAT_REG_2_t Carry2M = { 0xffffffff };
+
+		Carry2M.bits.tdrp = 1;
+		Carry2M.bits.tpfh = 1;
+		Carry2M.bits.tncl = 0;
+		Carry2M.bits.txcl = 1;
+		Carry2M.bits.tlcl = 0;
+		Carry2M.bits.tmcl = 0;
+		Carry2M.bits.tscl = 0;
+		Carry2M.bits.tedf = 1;
+		Carry2M.bits.tdfr = 0;
+		Carry2M.bits.txpf = 1;
+		Carry2M.bits.tbca = 1;
+		Carry2M.bits.tmca = 1;
+		Carry2M.bits.tpkt = 1;
+		Carry2M.bits.tbyt = 1;
+		Carry2M.bits.tfrg = 1;
+		Carry2M.bits.tund = 0;
+		Carry2M.bits.tovr = 0;
+		Carry2M.bits.txcf = 1;
+		Carry2M.bits.tfcs = 1;
+		Carry2M.bits.tjbr = 1;
+
+		writel(Carry2M.value, &pDevMacStat->Carry2M.value);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void ConfigFlowControl(struct et131x_adapter * pAdapter)
+{
+	if (pAdapter->uiDuplexMode == 0) {
+		pAdapter->FlowControl = None;
+	} else {
+		char RemotePause, RemoteAsyncPause;
+
+		ET1310_PhyAccessMiBit(pAdapter,
+				      TRUEPHY_BIT_READ, 5, 10, &RemotePause);
+		ET1310_PhyAccessMiBit(pAdapter,
+				      TRUEPHY_BIT_READ, 5, 11,
+				      &RemoteAsyncPause);
+
+		if ((RemotePause == TRUEPHY_BIT_SET) &&
+		    (RemoteAsyncPause == TRUEPHY_BIT_SET)) {
+			pAdapter->FlowControl = pAdapter->RegistryFlowControl;
+		} else if ((RemotePause == TRUEPHY_BIT_SET) &&
+			   (RemoteAsyncPause == TRUEPHY_BIT_CLEAR)) {
+			if (pAdapter->RegistryFlowControl == Both) {
+				pAdapter->FlowControl = Both;
+			} else {
+				pAdapter->FlowControl = None;
+			}
+		} else if ((RemotePause == TRUEPHY_BIT_CLEAR) &&
+			   (RemoteAsyncPause == TRUEPHY_BIT_CLEAR)) {
+			pAdapter->FlowControl = None;
+		} else {/* if (RemotePause == TRUEPHY_CLEAR_BIT &&
+			       RemoteAsyncPause == TRUEPHY_SET_BIT) */
+			if (pAdapter->RegistryFlowControl == Both) {
+				pAdapter->FlowControl = RxOnly;
+			} else {
+				pAdapter->FlowControl = None;
+			}
+		}
+	}
+}
+
+/**
+ * UpdateMacStatHostCounters - Update the local copy of the statistics
+ * @pAdapter: pointer to the adapter structure
+ */
+void UpdateMacStatHostCounters(struct et131x_adapter *pAdapter)
+{
+	struct _ce_stats_t *stats = &pAdapter->Stats;
+	struct _MAC_STAT_t __iomem *pDevMacStat =
+		&pAdapter->CSRAddress->macStat;
+
+	stats->collisions += readl(&pDevMacStat->TNcl);
+	stats->first_collision += readl(&pDevMacStat->TScl);
+	stats->tx_deferred += readl(&pDevMacStat->TDfr);
+	stats->excessive_collisions += readl(&pDevMacStat->TMcl);
+	stats->late_collisions += readl(&pDevMacStat->TLcl);
+	stats->tx_uflo += readl(&pDevMacStat->TUnd);
+	stats->max_pkt_error += readl(&pDevMacStat->TOvr);
+
+	stats->alignment_err += readl(&pDevMacStat->RAln);
+	stats->crc_err += readl(&pDevMacStat->RCde);
+	stats->norcvbuf += readl(&pDevMacStat->RDrp);
+	stats->rx_ov_flow += readl(&pDevMacStat->ROvr);
+	stats->code_violations += readl(&pDevMacStat->RFcs);
+	stats->length_err += readl(&pDevMacStat->RFlr);
+
+	stats->other_errors += readl(&pDevMacStat->RFrg);
+}
+
+/**
+ * HandleMacStatInterrupt
+ * @pAdapter: pointer to the adapter structure
+ *
+ * One of the MACSTAT counters has wrapped.  Update the local copy of
+ * the statistics held in the adapter structure, checking the "wrap"
+ * bit for each counter.
+ */
+void HandleMacStatInterrupt(struct et131x_adapter *pAdapter)
+{
+	MAC_STAT_REG_1_t Carry1;
+	MAC_STAT_REG_2_t Carry2;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Read the interrupt bits from the register(s).  These are Clear On
+	 * Write.
+	 */
+	Carry1.value = readl(&pAdapter->CSRAddress->macStat.Carry1.value);
+	Carry2.value = readl(&pAdapter->CSRAddress->macStat.Carry2.value);
+
+	writel(Carry1.value, &pAdapter->CSRAddress->macStat.Carry1.value);
+	writel(Carry2.value, &pAdapter->CSRAddress->macStat.Carry2.value);
+
+	/* We need to do update the host copy of all the MAC_STAT counters.
+	 * For each counter, check it's overflow bit.  If the overflow bit is
+	 * set, then increment the host version of the count by one complete
+	 * revolution of the counter.  This routine is called when the counter
+	 * block indicates that one of the counters has wrapped.
+	 */
+	if (Carry1.bits.rfcs) {
+		pAdapter->Stats.code_violations += COUNTER_WRAP_16_BIT;
+	}
+	if (Carry1.bits.raln) {
+		pAdapter->Stats.alignment_err += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry1.bits.rflr) {
+		pAdapter->Stats.length_err += COUNTER_WRAP_16_BIT;
+	}
+	if (Carry1.bits.rfrg) {
+		pAdapter->Stats.other_errors += COUNTER_WRAP_16_BIT;
+	}
+	if (Carry1.bits.rcde) {
+		pAdapter->Stats.crc_err += COUNTER_WRAP_16_BIT;
+	}
+	if (Carry1.bits.rovr) {
+		pAdapter->Stats.rx_ov_flow += COUNTER_WRAP_16_BIT;
+	}
+	if (Carry1.bits.rdrp) {
+		pAdapter->Stats.norcvbuf += COUNTER_WRAP_16_BIT;
+	}
+	if (Carry2.bits.tovr) {
+		pAdapter->Stats.max_pkt_error += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry2.bits.tund) {
+		pAdapter->Stats.tx_uflo += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry2.bits.tscl) {
+		pAdapter->Stats.first_collision += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry2.bits.tdfr) {
+		pAdapter->Stats.tx_deferred += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry2.bits.tmcl) {
+		pAdapter->Stats.excessive_collisions += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry2.bits.tlcl) {
+		pAdapter->Stats.late_collisions += COUNTER_WRAP_12_BIT;
+	}
+	if (Carry2.bits.tncl) {
+		pAdapter->Stats.collisions += COUNTER_WRAP_12_BIT;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void SetupDeviceForMulticast(struct et131x_adapter *pAdapter)
+{
+	struct _RXMAC_t __iomem *rxmac = &pAdapter->CSRAddress->rxmac;
+	uint32_t nIndex;
+	uint32_t result;
+	uint32_t hash1 = 0;
+	uint32_t hash2 = 0;
+	uint32_t hash3 = 0;
+	uint32_t hash4 = 0;
+	PM_CSR_t pm_csr;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* If ET131X_PACKET_TYPE_MULTICAST is specified, then we provision
+	 * the multi-cast LIST.  If it is NOT specified, (and "ALL" is not
+	 * specified) then we should pass NO multi-cast addresses to the
+	 * driver.
+	 */
+	if (pAdapter->PacketFilter & ET131X_PACKET_TYPE_MULTICAST) {
+		DBG_VERBOSE(et131x_dbginfo,
+			    "MULTICAST flag is set, MCCount: %d\n",
+			    pAdapter->MCAddressCount);
+
+		/* Loop through our multicast array and set up the device */
+		for (nIndex = 0; nIndex < pAdapter->MCAddressCount; nIndex++) {
+			DBG_VERBOSE(et131x_dbginfo,
+				    "MCList[%d]: %02x:%02x:%02x:%02x:%02x:%02x\n",
+				    nIndex,
+				    pAdapter->MCList[nIndex][0],
+				    pAdapter->MCList[nIndex][1],
+				    pAdapter->MCList[nIndex][2],
+				    pAdapter->MCList[nIndex][3],
+				    pAdapter->MCList[nIndex][4],
+				    pAdapter->MCList[nIndex][5]);
+
+			result = ether_crc(6, pAdapter->MCList[nIndex]);
+
+			result = (result & 0x3F800000) >> 23;
+
+			if (result < 32) {
+				hash1 |= (1 << result);
+			} else if ((31 < result) && (result < 64)) {
+				result -= 32;
+				hash2 |= (1 << result);
+			} else if ((63 < result) && (result < 96)) {
+				result -= 64;
+				hash3 |= (1 << result);
+			} else {
+				result -= 96;
+				hash4 |= (1 << result);
+			}
+		}
+	}
+
+	/* Write out the new hash to the device */
+	pm_csr.value = readl(&pAdapter->CSRAddress->global.pm_csr.value);
+	if (pm_csr.bits.pm_phy_sw_coma == 0) {
+		writel(hash1, &rxmac->multi_hash1);
+		writel(hash2, &rxmac->multi_hash2);
+		writel(hash3, &rxmac->multi_hash3);
+		writel(hash4, &rxmac->multi_hash4);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+void SetupDeviceForUnicast(struct et131x_adapter *pAdapter)
+{
+	struct _RXMAC_t __iomem *rxmac = &pAdapter->CSRAddress->rxmac;
+	RXMAC_UNI_PF_ADDR1_t uni_pf1;
+	RXMAC_UNI_PF_ADDR2_t uni_pf2;
+	RXMAC_UNI_PF_ADDR3_t uni_pf3;
+	PM_CSR_t pm_csr;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Set up unicast packet filter reg 3 to be the first two octets of
+	 * the MAC address for both address
+	 *
+	 * Set up unicast packet filter reg 2 to be the octets 2 - 5 of the
+	 * MAC address for second address
+	 *
+	 * Set up unicast packet filter reg 3 to be the octets 2 - 5 of the
+	 * MAC address for first address
+	 */
+	uni_pf3.bits.addr1_1 = pAdapter->CurrentAddress[0];
+	uni_pf3.bits.addr1_2 = pAdapter->CurrentAddress[1];
+	uni_pf3.bits.addr2_1 = pAdapter->CurrentAddress[0];
+	uni_pf3.bits.addr2_2 = pAdapter->CurrentAddress[1];
+
+	uni_pf2.bits.addr2_3 = pAdapter->CurrentAddress[2];
+	uni_pf2.bits.addr2_4 = pAdapter->CurrentAddress[3];
+	uni_pf2.bits.addr2_5 = pAdapter->CurrentAddress[4];
+	uni_pf2.bits.addr2_6 = pAdapter->CurrentAddress[5];
+
+	uni_pf1.bits.addr1_3 = pAdapter->CurrentAddress[2];
+	uni_pf1.bits.addr1_4 = pAdapter->CurrentAddress[3];
+	uni_pf1.bits.addr1_5 = pAdapter->CurrentAddress[4];
+	uni_pf1.bits.addr1_6 = pAdapter->CurrentAddress[5];
+
+	pm_csr.value = readl(&pAdapter->CSRAddress->global.pm_csr.value);
+	if (pm_csr.bits.pm_phy_sw_coma == 0) {
+		writel(uni_pf1.value, &rxmac->uni_pf_addr1.value);
+		writel(uni_pf2.value, &rxmac->uni_pf_addr2.value);
+		writel(uni_pf3.value, &rxmac->uni_pf_addr3.value);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
diff --git a/drivers/staging/et131x/et1310_mac.h b/drivers/staging/et131x/et1310_mac.h
new file mode 100644
index 0000000..bd26cd3
--- /dev/null
+++ b/drivers/staging/et131x/et1310_mac.h
@@ -0,0 +1,93 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_mac.h -  Defines, structs, enums, prototypes, etc. pertaining to the
+ *                 MAC.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef _ET1310_MAC_H_
+#define _ET1310_MAC_H_
+
+
+#include "et1310_address_map.h"
+
+
+#define COUNTER_WRAP_28_BIT 0x10000000
+#define COUNTER_WRAP_22_BIT 0x400000
+#define COUNTER_WRAP_16_BIT 0x10000
+#define COUNTER_WRAP_12_BIT 0x1000
+
+#define COUNTER_MASK_28_BIT (COUNTER_WRAP_28_BIT - 1)
+#define COUNTER_MASK_22_BIT (COUNTER_WRAP_22_BIT - 1)
+#define COUNTER_MASK_16_BIT (COUNTER_WRAP_16_BIT - 1)
+#define COUNTER_MASK_12_BIT (COUNTER_WRAP_12_BIT - 1)
+
+#define UPDATE_COUNTER(HostCnt,DevCnt) \
+    HostCnt = HostCnt + DevCnt;
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+void ConfigMACRegs1(struct et131x_adapter *adapter);
+void ConfigMACRegs2(struct et131x_adapter *adapter);
+void ConfigRxMacRegs(struct et131x_adapter *adapter);
+void ConfigTxMacRegs(struct et131x_adapter *adapter);
+void ConfigMacStatRegs(struct et131x_adapter *adapter);
+void ConfigFlowControl(struct et131x_adapter *adapter);
+void UpdateMacStatHostCounters(struct et131x_adapter *adapter);
+void HandleMacStatInterrupt(struct et131x_adapter *adapter);
+void SetupDeviceForMulticast(struct et131x_adapter *adapter);
+void SetupDeviceForUnicast(struct et131x_adapter *adapter);
+
+#endif /* _ET1310_MAC_H_ */
diff --git a/drivers/staging/et131x/et1310_phy.c b/drivers/staging/et131x/et1310_phy.c
new file mode 100644
index 0000000..6c4fa54
--- /dev/null
+++ b/drivers/staging/et131x/et1310_phy.c
@@ -0,0 +1,1281 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_phy.c - Routines for configuring and accessing the PHY
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_netdev.h"
+#include "et131x_initpci.h"
+
+#include "et1310_address_map.h"
+#include "et1310_jagcore.h"
+#include "et1310_tx.h"
+#include "et1310_rx.h"
+#include "et1310_mac.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+/* Prototypes for functions with local scope */
+static int et131x_xcvr_init(struct et131x_adapter *adapter);
+
+/**
+ * PhyMiRead - Read from the PHY through the MII Interface on the MAC
+ * @adapter: pointer to our private adapter structure
+ * @xcvrAddr: the address of the transciever
+ * @xcvrReg: the register to read
+ * @value: pointer to a 16-bit value in which the value will be stored
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int PhyMiRead(struct et131x_adapter *adapter, uint8_t xcvrAddr,
+	      uint8_t xcvrReg, uint16_t *value)
+{
+	struct _MAC_t __iomem *mac = &adapter->CSRAddress->mac;
+	int status = 0;
+	uint32_t delay;
+	MII_MGMT_ADDR_t miiAddr;
+	MII_MGMT_CMD_t miiCmd;
+	MII_MGMT_INDICATOR_t miiIndicator;
+
+	/* Save a local copy of the registers we are dealing with so we can
+	 * set them back
+	 */
+	miiAddr.value = readl(&mac->mii_mgmt_addr.value);
+	miiCmd.value = readl(&mac->mii_mgmt_cmd.value);
+
+	/* Stop the current operation */
+	writel(0, &mac->mii_mgmt_cmd.value);
+
+	/* Set up the register we need to read from on the correct PHY */
+	{
+		MII_MGMT_ADDR_t mii_mgmt_addr = { 0 };
+
+		mii_mgmt_addr.bits.phy_addr = xcvrAddr;
+		mii_mgmt_addr.bits.reg_addr = xcvrReg;
+		writel(mii_mgmt_addr.value, &mac->mii_mgmt_addr.value);
+	}
+
+	/* Kick the read cycle off */
+	delay = 0;
+
+	writel(0x1, &mac->mii_mgmt_cmd.value);
+
+	do {
+		udelay(50);
+		delay++;
+		miiIndicator.value = readl(&mac->mii_mgmt_indicator.value);
+	} while ((miiIndicator.bits.not_valid || miiIndicator.bits.busy) &&
+		 delay < 50);
+
+	/* If we hit the max delay, we could not read the register */
+	if (delay >= 50) {
+		DBG_WARNING(et131x_dbginfo,
+			    "xcvrReg 0x%08x could not be read\n", xcvrReg);
+		DBG_WARNING(et131x_dbginfo, "status is  0x%08x\n",
+			    miiIndicator.value);
+
+		status = -EIO;
+	}
+
+	/* If we hit here we were able to read the register and we need to
+	 * return the value to the caller
+	 */
+	/* TODO: make this stuff a simple readw()?! */
+	{
+		MII_MGMT_STAT_t mii_mgmt_stat;
+
+		mii_mgmt_stat.value = readl(&mac->mii_mgmt_stat.value);
+		*value = (uint16_t) mii_mgmt_stat.bits.phy_stat;
+	}
+
+	/* Stop the read operation */
+	writel(0, &mac->mii_mgmt_cmd.value);
+
+	DBG_VERBOSE(et131x_dbginfo, "  xcvr_addr = 0x%02x, "
+		    "xcvr_reg  = 0x%02x, "
+		    "value     = 0x%04x.\n", xcvrAddr, xcvrReg, *value);
+
+	/* set the registers we touched back to the state at which we entered
+	 * this function
+	 */
+	writel(miiAddr.value, &mac->mii_mgmt_addr.value);
+	writel(miiCmd.value, &mac->mii_mgmt_cmd.value);
+
+	return status;
+}
+
+/**
+ * MiWrite - Write to a PHY register through the MII interface of the MAC
+ * @adapter: pointer to our private adapter structure
+ * @xcvrReg: the register to read
+ * @value: 16-bit value to write
+ *
+ * Return 0 on success, errno on failure (as defined in errno.h)
+ */
+int MiWrite(struct et131x_adapter *adapter, uint8_t xcvrReg, uint16_t value)
+{
+	struct _MAC_t __iomem *mac = &adapter->CSRAddress->mac;
+	int status = 0;
+	uint8_t xcvrAddr = adapter->Stats.xcvr_addr;
+	uint32_t delay;
+	MII_MGMT_ADDR_t miiAddr;
+	MII_MGMT_CMD_t miiCmd;
+	MII_MGMT_INDICATOR_t miiIndicator;
+
+	/* Save a local copy of the registers we are dealing with so we can
+	 * set them back
+	 */
+	miiAddr.value = readl(&mac->mii_mgmt_addr.value);
+	miiCmd.value = readl(&mac->mii_mgmt_cmd.value);
+
+	/* Stop the current operation */
+	writel(0, &mac->mii_mgmt_cmd.value);
+
+	/* Set up the register we need to write to on the correct PHY */
+	{
+		MII_MGMT_ADDR_t mii_mgmt_addr;
+
+		mii_mgmt_addr.bits.phy_addr = xcvrAddr;
+		mii_mgmt_addr.bits.reg_addr = xcvrReg;
+		writel(mii_mgmt_addr.value, &mac->mii_mgmt_addr.value);
+	}
+
+	/* Add the value to write to the registers to the mac */
+	writel(value, &mac->mii_mgmt_ctrl.value);
+	delay = 0;
+
+	do {
+		udelay(50);
+		delay++;
+		miiIndicator.value = readl(&mac->mii_mgmt_indicator.value);
+	} while (miiIndicator.bits.busy && delay < 100);
+
+	/* If we hit the max delay, we could not write the register */
+	if (delay == 100) {
+		uint16_t TempValue;
+
+		DBG_WARNING(et131x_dbginfo,
+			    "xcvrReg 0x%08x could not be written", xcvrReg);
+		DBG_WARNING(et131x_dbginfo, "status is  0x%08x\n",
+			    miiIndicator.value);
+		DBG_WARNING(et131x_dbginfo, "command is  0x%08x\n",
+			    readl(&mac->mii_mgmt_cmd.value));
+
+		MiRead(adapter, xcvrReg, &TempValue);
+
+		status = -EIO;
+	}
+
+	/* Stop the write operation */
+	writel(0, &mac->mii_mgmt_cmd.value);
+
+	/* set the registers we touched back to the state at which we entered
+         * this function
+         */
+	writel(miiAddr.value, &mac->mii_mgmt_addr.value);
+	writel(miiCmd.value, &mac->mii_mgmt_cmd.value);
+
+	DBG_VERBOSE(et131x_dbginfo, " xcvr_addr = 0x%02x, "
+		    "xcvr_reg  = 0x%02x, "
+		    "value     = 0x%04x.\n", xcvrAddr, xcvrReg, value);
+
+	return status;
+}
+
+/**
+ * et131x_xcvr_find - Find the PHY ID
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_xcvr_find(struct et131x_adapter *adapter)
+{
+	int status = -ENODEV;
+	uint8_t xcvr_addr;
+	MI_IDR1_t idr1;
+	MI_IDR2_t idr2;
+	uint32_t xcvr_id;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* We need to get xcvr id and address we just get the first one */
+	for (xcvr_addr = 0; xcvr_addr < 32; xcvr_addr++) {
+		/* Read the ID from the PHY */
+		PhyMiRead(adapter, xcvr_addr,
+			  (uint8_t) offsetof(MI_REGS_t, idr1),
+			  &idr1.value);
+		PhyMiRead(adapter, xcvr_addr,
+			  (uint8_t) offsetof(MI_REGS_t, idr2),
+			  &idr2.value);
+
+		xcvr_id = (uint32_t) ((idr1.value << 16) | idr2.value);
+
+		if ((idr1.value != 0) && (idr1.value != 0xffff)) {
+			DBG_TRACE(et131x_dbginfo,
+				  "Xcvr addr: 0x%02x\tXcvr_id: 0x%08x\n",
+				  xcvr_addr, xcvr_id);
+
+			adapter->Stats.xcvr_id = xcvr_id;
+			adapter->Stats.xcvr_addr = xcvr_addr;
+
+			status = 0;
+			break;
+		}
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_setphy_normal - Set PHY for normal operation.
+ * @adapter: pointer to our private adapter structure
+ *
+ * Used by Power Management to force the PHY into 10 Base T half-duplex mode,
+ * when going to D3 in WOL mode. Also used during initialization to set the
+ * PHY for normal operation.
+ */
+int et131x_setphy_normal(struct et131x_adapter *adapter)
+{
+	int status;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Make sure the PHY is powered up */
+	ET1310_PhyPowerDown(adapter, 0);
+	status = et131x_xcvr_init(adapter);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_xcvr_init - Init the phy if we are setting it into force mode
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+static int et131x_xcvr_init(struct et131x_adapter *adapter)
+{
+	int status = 0;
+	MI_IMR_t imr;
+	MI_ISR_t isr;
+	MI_LCR2_t lcr2;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Zero out the adapter structure variable representing BMSR */
+	adapter->Bmsr.value = 0;
+
+	MiRead(adapter, (uint8_t) offsetof(MI_REGS_t, isr), &isr.value);
+
+	MiRead(adapter, (uint8_t) offsetof(MI_REGS_t, imr), &imr.value);
+
+	/* Set the link status interrupt only.  Bad behavior when link status
+	 * and auto neg are set, we run into a nested interrupt problem
+	 */
+	imr.bits.int_en = 0x1;
+	imr.bits.link_status = 0x1;
+	imr.bits.autoneg_status = 0x1;
+
+	MiWrite(adapter, (uint8_t) offsetof(MI_REGS_t, imr), imr.value);
+
+	/* Set the LED behavior such that LED 1 indicates speed (off =
+	 * 10Mbits, blink = 100Mbits, on = 1000Mbits) and LED 2 indicates
+	 * link and activity (on for link, blink off for activity).
+	 *
+	 * NOTE: Some customizations have been added here for specific
+	 * vendors; The LED behavior is now determined by vendor data in the
+	 * EEPROM. However, the above description is the default.
+	 */
+	if ((adapter->eepromData[1] & 0x4) == 0) {
+		MiRead(adapter, (uint8_t) offsetof(MI_REGS_t, lcr2),
+		       &lcr2.value);
+		if ((adapter->eepromData[1] & 0x8) == 0)
+			lcr2.bits.led_tx_rx = 0x3;
+		else
+			lcr2.bits.led_tx_rx = 0x4;
+		lcr2.bits.led_link = 0xa;
+		MiWrite(adapter, (uint8_t) offsetof(MI_REGS_t, lcr2),
+			lcr2.value);
+	}
+
+	/* Determine if we need to go into a force mode and set it */
+	if (adapter->AiForceSpeed == 0 && adapter->AiForceDpx == 0) {
+		if ((adapter->RegistryFlowControl == TxOnly) ||
+		    (adapter->RegistryFlowControl == Both)) {
+			ET1310_PhyAccessMiBit(adapter,
+					      TRUEPHY_BIT_SET, 4, 11, NULL);
+		} else {
+			ET1310_PhyAccessMiBit(adapter,
+					      TRUEPHY_BIT_CLEAR, 4, 11, NULL);
+		}
+
+		if (adapter->RegistryFlowControl == Both) {
+			ET1310_PhyAccessMiBit(adapter,
+					      TRUEPHY_BIT_SET, 4, 10, NULL);
+		} else {
+			ET1310_PhyAccessMiBit(adapter,
+					      TRUEPHY_BIT_CLEAR, 4, 10, NULL);
+		}
+
+		/* Set the phy to autonegotiation */
+		ET1310_PhyAutoNeg(adapter, true);
+
+		/* NOTE - Do we need this? */
+		ET1310_PhyAccessMiBit(adapter, TRUEPHY_BIT_SET, 0, 9, NULL);
+
+		DBG_LEAVE(et131x_dbginfo);
+		return status;
+	} else {
+		ET1310_PhyAutoNeg(adapter, false);
+
+		/* Set to the correct force mode. */
+		if (adapter->AiForceDpx != 1) {
+			if ((adapter->RegistryFlowControl == TxOnly) ||
+			    (adapter->RegistryFlowControl == Both)) {
+				ET1310_PhyAccessMiBit(adapter,
+						      TRUEPHY_BIT_SET, 4, 11,
+						      NULL);
+			} else {
+				ET1310_PhyAccessMiBit(adapter,
+						      TRUEPHY_BIT_CLEAR, 4, 11,
+						      NULL);
+			}
+
+			if (adapter->RegistryFlowControl == Both) {
+				ET1310_PhyAccessMiBit(adapter,
+						      TRUEPHY_BIT_SET, 4, 10,
+						      NULL);
+			} else {
+				ET1310_PhyAccessMiBit(adapter,
+						      TRUEPHY_BIT_CLEAR, 4, 10,
+						      NULL);
+			}
+		} else {
+			ET1310_PhyAccessMiBit(adapter,
+					      TRUEPHY_BIT_CLEAR, 4, 10, NULL);
+			ET1310_PhyAccessMiBit(adapter,
+					      TRUEPHY_BIT_CLEAR, 4, 11, NULL);
+		}
+
+		switch (adapter->AiForceSpeed) {
+		case 10:
+			if (adapter->AiForceDpx == 1) {
+				TPAL_SetPhy10HalfDuplex(adapter);
+			} else if (adapter->AiForceDpx == 2) {
+				TPAL_SetPhy10FullDuplex(adapter);
+			} else {
+				TPAL_SetPhy10Force(adapter);
+			}
+			break;
+		case 100:
+			if (adapter->AiForceDpx == 1) {
+				TPAL_SetPhy100HalfDuplex(adapter);
+			} else if (adapter->AiForceDpx == 2) {
+				TPAL_SetPhy100FullDuplex(adapter);
+			} else {
+				TPAL_SetPhy100Force(adapter);
+			}
+			break;
+		case 1000:
+			TPAL_SetPhy1000FullDuplex(adapter);
+			break;
+		}
+
+		DBG_LEAVE(et131x_dbginfo);
+		return status;
+	}
+}
+
+void et131x_Mii_check(struct et131x_adapter *pAdapter,
+		      MI_BMSR_t bmsr, MI_BMSR_t bmsr_ints)
+{
+	uint8_t ucLinkStatus;
+	uint32_t uiAutoNegStatus;
+	uint32_t uiSpeed;
+	uint32_t uiDuplex;
+	uint32_t uiMdiMdix;
+	uint32_t uiMasterSlave;
+	uint32_t uiPolarity;
+	unsigned long lockflags;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	if (bmsr_ints.bits.link_status) {
+		if (bmsr.bits.link_status) {
+			pAdapter->PoMgmt.TransPhyComaModeOnBoot = 20;
+
+			/* Update our state variables and indicate the
+			 * connected state
+			 */
+			spin_lock_irqsave(&pAdapter->Lock, lockflags);
+
+			pAdapter->MediaState = NETIF_STATUS_MEDIA_CONNECT;
+			MP_CLEAR_FLAG(pAdapter, fMP_ADAPTER_LINK_DETECTION);
+
+			spin_unlock_irqrestore(&pAdapter->Lock, lockflags);
+
+			/* Don't indicate state if we're in loopback mode */
+			if (pAdapter->RegistryPhyLoopbk == false) {
+				netif_carrier_on(pAdapter->netdev);
+			}
+		} else {
+			DBG_WARNING(et131x_dbginfo,
+				    "Link down cable problem\n");
+
+			if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_10MBPS) {
+				// NOTE - Is there a way to query this without TruePHY?
+				// && TRU_QueryCoreType(pAdapter->hTruePhy, 0) == EMI_TRUEPHY_A13O) {
+				uint16_t Register18;
+
+				MiRead(pAdapter, 0x12, &Register18);
+				MiWrite(pAdapter, 0x12, Register18 | 0x4);
+				MiWrite(pAdapter, 0x10, Register18 | 0x8402);
+				MiWrite(pAdapter, 0x11, Register18 | 511);
+				MiWrite(pAdapter, 0x12, Register18);
+			}
+
+			/* For the first N seconds of life, we are in "link
+			 * detection" When we are in this state, we should
+			 * only report "connected". When the LinkDetection
+			 * Timer expires, we can report disconnected (handled
+			 * in the LinkDetectionDPC).
+			 */
+			if ((MP_IS_FLAG_CLEAR
+			     (pAdapter, fMP_ADAPTER_LINK_DETECTION))
+			    || (pAdapter->MediaState ==
+				NETIF_STATUS_MEDIA_DISCONNECT)) {
+				spin_lock_irqsave(&pAdapter->Lock, lockflags);
+				pAdapter->MediaState =
+				    NETIF_STATUS_MEDIA_DISCONNECT;
+				spin_unlock_irqrestore(&pAdapter->Lock,
+						       lockflags);
+
+				/* Only indicate state if we're in loopback
+				 * mode
+				 */
+				if (pAdapter->RegistryPhyLoopbk == false) {
+					netif_carrier_off(pAdapter->netdev);
+				}
+			}
+
+			pAdapter->uiLinkSpeed = 0;
+			pAdapter->uiDuplexMode = 0;
+
+			/* Free the packets being actively sent & stopped */
+			et131x_free_busy_send_packets(pAdapter);
+
+			/* Re-initialize the send structures */
+			et131x_init_send(pAdapter);
+
+			/* Reset the RFD list and re-start RU */
+			et131x_reset_recv(pAdapter);
+
+			/*
+			 * Bring the device back to the state it was during
+			 * init prior to autonegotiation being complete. This
+			 * way, when we get the auto-neg complete interrupt,
+			 * we can complete init by calling ConfigMacREGS2.
+			 */
+			et131x_soft_reset(pAdapter);
+
+			/* Setup ET1310 as per the documentation */
+			et131x_adapter_setup(pAdapter);
+
+			/* Setup the PHY into coma mode until the cable is
+			 * plugged back in
+			 */
+			if (pAdapter->RegistryPhyComa == 1) {
+				EnablePhyComa(pAdapter);
+			}
+		}
+	}
+
+	if (bmsr_ints.bits.auto_neg_complete ||
+	    ((pAdapter->AiForceDpx == 3) && (bmsr_ints.bits.link_status))) {
+		if (bmsr.bits.auto_neg_complete || (pAdapter->AiForceDpx == 3)) {
+			ET1310_PhyLinkStatus(pAdapter,
+					     &ucLinkStatus, &uiAutoNegStatus,
+					     &uiSpeed, &uiDuplex, &uiMdiMdix,
+					     &uiMasterSlave, &uiPolarity);
+
+			pAdapter->uiLinkSpeed = uiSpeed;
+			pAdapter->uiDuplexMode = uiDuplex;
+
+			DBG_TRACE(et131x_dbginfo,
+				  "pAdapter->uiLinkSpeed 0x%04x, pAdapter->uiDuplex 0x%08x\n",
+				  pAdapter->uiLinkSpeed,
+				  pAdapter->uiDuplexMode);
+
+			pAdapter->PoMgmt.TransPhyComaModeOnBoot = 20;
+
+			if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_10MBPS) {
+				// NOTE - Is there a way to query this without TruePHY?
+				// && TRU_QueryCoreType(pAdapter->hTruePhy, 0) == EMI_TRUEPHY_A13O) {
+				uint16_t Register18;
+
+				MiRead(pAdapter, 0x12, &Register18);
+				MiWrite(pAdapter, 0x12, Register18 | 0x4);
+				MiWrite(pAdapter, 0x10, Register18 | 0x8402);
+				MiWrite(pAdapter, 0x11, Register18 | 511);
+				MiWrite(pAdapter, 0x12, Register18);
+			}
+
+			ConfigFlowControl(pAdapter);
+
+			if ((pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) &&
+			    (pAdapter->RegistryJumboPacket > 2048))
+			{
+				ET1310_PhyAndOrReg(pAdapter, 0x16, 0xcfff,
+						   0x2000);
+			}
+
+			SetRxDmaTimer(pAdapter);
+			ConfigMACRegs2(pAdapter);
+		}
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy10HalfDuplex - Force the phy into 10 Base T Half Duplex mode.
+ * @pAdapter: pointer to the adapter structure
+ *
+ * Also sets the MAC so it is syncd up properly
+ */
+void TPAL_SetPhy10HalfDuplex(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* First we need to turn off all other advertisement */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* Set our advertise values accordingly */
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_HALF);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy10FullDuplex - Force the phy into 10 Base T Full Duplex mode.
+ * @pAdapter: pointer to the adapter structure
+ *
+ * Also sets the MAC so it is syncd up properly
+ */
+void TPAL_SetPhy10FullDuplex(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* First we need to turn off all other advertisement */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* Set our advertise values accordingly */
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy10Force - Force Base-T FD mode WITHOUT using autonegotiation
+ * @pAdapter: pointer to the adapter structure
+ */
+void TPAL_SetPhy10Force(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* Disable autoneg */
+	ET1310_PhyAutoNeg(pAdapter, false);
+
+	/* Disable all advertisement */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* Force 10 Mbps */
+	ET1310_PhySpeedSelect(pAdapter, TRUEPHY_SPEED_10MBPS);
+
+	/* Force Full duplex */
+	ET1310_PhyDuplexMode(pAdapter, TRUEPHY_DUPLEX_FULL);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy100HalfDuplex - Force 100 Base T Half Duplex mode.
+ * @pAdapter: pointer to the adapter structure
+ *
+ * Also sets the MAC so it is syncd up properly.
+ */
+void TPAL_SetPhy100HalfDuplex(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* first we need to turn off all other advertisement */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* Set our advertise values accordingly */
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_HALF);
+
+	/* Set speed */
+	ET1310_PhySpeedSelect(pAdapter, TRUEPHY_SPEED_100MBPS);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy100FullDuplex - Force 100 Base T Full Duplex mode.
+ * @pAdapter: pointer to the adapter structure
+ *
+ * Also sets the MAC so it is syncd up properly
+ */
+void TPAL_SetPhy100FullDuplex(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* First we need to turn off all other advertisement */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* Set our advertise values accordingly */
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy100Force - Force 100 BaseT FD mode WITHOUT using autonegotiation
+ * @pAdapter: pointer to the adapter structure
+ */
+void TPAL_SetPhy100Force(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* Disable autoneg */
+	ET1310_PhyAutoNeg(pAdapter, false);
+
+	/* Disable all advertisement */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* Force 100 Mbps */
+	ET1310_PhySpeedSelect(pAdapter, TRUEPHY_SPEED_100MBPS);
+
+	/* Force Full duplex */
+	ET1310_PhyDuplexMode(pAdapter, TRUEPHY_DUPLEX_FULL);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhy1000FullDuplex - Force 1000 Base T Full Duplex mode
+ * @pAdapter: pointer to the adapter structure
+ *
+ * Also sets the MAC so it is syncd up properly.
+ */
+void TPAL_SetPhy1000FullDuplex(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* first we need to turn off all other advertisement */
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+
+	/* set our advertise values accordingly */
+	ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL);
+
+	/* power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * TPAL_SetPhyAutoNeg - Set phy to autonegotiation mode.
+ * @pAdapter: pointer to the adapter structure
+ */
+void TPAL_SetPhyAutoNeg(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/* Turn on advertisement of all capabilities */
+	ET1310_PhyAdvertise10BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_BOTH);
+
+	ET1310_PhyAdvertise100BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_BOTH);
+
+	if (pAdapter->DeviceID != ET131X_PCI_DEVICE_ID_FAST) {
+		ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL);
+	} else {
+		ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+	}
+
+	/* Make sure auto-neg is ON (it is disabled in FORCE modes) */
+	ET1310_PhyAutoNeg(pAdapter, true);
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+
+/*
+ * The routines which follow provide low-level access to the PHY, and are used
+ * primarily by the routines above (although there are a few places elsewhere
+ * in the driver where this level of access is required).
+ */
+
+static const uint16_t ConfigPhy[25][2] = {
+	/* Reg      Value      Register */
+	/* Addr                         */
+	{0x880B, 0x0926},	/* AfeIfCreg4B1000Msbs */
+	{0x880C, 0x0926},	/* AfeIfCreg4B100Msbs */
+	{0x880D, 0x0926},	/* AfeIfCreg4B10Msbs */
+
+	{0x880E, 0xB4D3},	/* AfeIfCreg4B1000Lsbs */
+	{0x880F, 0xB4D3},	/* AfeIfCreg4B100Lsbs */
+	{0x8810, 0xB4D3},	/* AfeIfCreg4B10Lsbs */
+
+	{0x8805, 0xB03E},	/* AfeIfCreg3B1000Msbs */
+	{0x8806, 0xB03E},	/* AfeIfCreg3B100Msbs */
+	{0x8807, 0xFF00},	/* AfeIfCreg3B10Msbs */
+
+	{0x8808, 0xE090},	/* AfeIfCreg3B1000Lsbs */
+	{0x8809, 0xE110},	/* AfeIfCreg3B100Lsbs */
+	{0x880A, 0x0000},	/* AfeIfCreg3B10Lsbs */
+
+	{0x300D, 1},		/* DisableNorm */
+
+	{0x280C, 0x0180},	/* LinkHoldEnd */
+
+	{0x1C21, 0x0002},	/* AlphaM */
+
+	{0x3821, 6},		/* FfeLkgTx0 */
+	{0x381D, 1},		/* FfeLkg1g4 */
+	{0x381E, 1},		/* FfeLkg1g5 */
+	{0x381F, 1},		/* FfeLkg1g6 */
+	{0x3820, 1},		/* FfeLkg1g7 */
+
+	{0x8402, 0x01F0},	/* Btinact */
+	{0x800E, 20},		/* LftrainTime */
+	{0x800F, 24},		/* DvguardTime */
+	{0x8010, 46},		/* IdlguardTime */
+
+	{0, 0}
+
+};
+
+/* condensed version of the phy initialization routine */
+void ET1310_PhyInit(struct et131x_adapter *pAdapter)
+{
+	uint16_t usData, usIndex;
+
+	if (pAdapter == NULL) {
+		return;
+	}
+
+	// get the identity (again ?)
+	MiRead(pAdapter, PHY_ID_1, &usData);
+	MiRead(pAdapter, PHY_ID_2, &usData);
+
+	// what does this do/achieve ?
+	MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData);	// should read 0002
+	MiWrite(pAdapter, PHY_MPHY_CONTROL_REG,	0x0006);
+
+	// read modem register 0402, should I do something with the return data ?
+	MiWrite(pAdapter, PHY_INDEX_REG, 0x0402);
+	MiRead(pAdapter, PHY_DATA_REG, &usData);
+
+	// what does this do/achieve ?
+	MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0002);
+
+	// get the identity (again ?)
+	MiRead(pAdapter, PHY_ID_1, &usData);
+	MiRead(pAdapter, PHY_ID_2, &usData);
+
+	// what does this achieve ?
+	MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData);	// should read 0002
+	MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0006);
+
+	// read modem register 0402, should I do something with the return data?
+	MiWrite(pAdapter, PHY_INDEX_REG, 0x0402);
+	MiRead(pAdapter, PHY_DATA_REG, &usData);
+
+	MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0002);
+
+	// what does this achieve (should return 0x1040)
+	MiRead(pAdapter, PHY_CONTROL, &usData);
+	MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData);	// should read 0002
+	MiWrite(pAdapter, PHY_CONTROL, 0x1840);
+
+	MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0007);
+
+	// here the writing of the array starts....
+	usIndex = 0;
+	while (ConfigPhy[usIndex][0] != 0x0000) {
+		// write value
+		MiWrite(pAdapter, PHY_INDEX_REG, ConfigPhy[usIndex][0]);
+		MiWrite(pAdapter, PHY_DATA_REG, ConfigPhy[usIndex][1]);
+
+		// read it back
+		MiWrite(pAdapter, PHY_INDEX_REG, ConfigPhy[usIndex][0]);
+		MiRead(pAdapter, PHY_DATA_REG, &usData);
+
+		// do a check on the value read back ?
+		usIndex++;
+	}
+	// here the writing of the array ends...
+
+	MiRead(pAdapter, PHY_CONTROL, &usData);	// 0x1840
+	MiRead(pAdapter, PHY_MPHY_CONTROL_REG, &usData);	// should read 0007
+	MiWrite(pAdapter, PHY_CONTROL, 0x1040);
+	MiWrite(pAdapter, PHY_MPHY_CONTROL_REG, 0x0002);
+}
+
+void ET1310_PhyReset(struct et131x_adapter *pAdapter)
+{
+	MiWrite(pAdapter, PHY_CONTROL, 0x8000);
+}
+
+void ET1310_PhyPowerDown(struct et131x_adapter *pAdapter, bool down)
+{
+	uint16_t usData;
+
+	MiRead(pAdapter, PHY_CONTROL, &usData);
+
+	if (down == false) {
+		// Power UP
+		usData &= ~0x0800;
+		MiWrite(pAdapter, PHY_CONTROL, usData);
+	} else {
+		// Power DOWN
+		usData |= 0x0800;
+		MiWrite(pAdapter, PHY_CONTROL, usData);
+	}
+}
+
+void ET1310_PhyAutoNeg(struct et131x_adapter *pAdapter, bool enable)
+{
+	uint16_t usData;
+
+	MiRead(pAdapter, PHY_CONTROL, &usData);
+
+	if (enable == true) {
+		// Autonegotiation ON
+		usData |= 0x1000;
+		MiWrite(pAdapter, PHY_CONTROL, usData);
+	} else {
+		// Autonegotiation OFF
+		usData &= ~0x1000;
+		MiWrite(pAdapter, PHY_CONTROL, usData);
+	}
+}
+
+void ET1310_PhyDuplexMode(struct et131x_adapter *pAdapter, uint16_t duplex)
+{
+	uint16_t usData;
+
+	MiRead(pAdapter, PHY_CONTROL, &usData);
+
+	if (duplex == TRUEPHY_DUPLEX_FULL) {
+		// Set Full Duplex
+		usData |= 0x100;
+		MiWrite(pAdapter, PHY_CONTROL, usData);
+	} else {
+		// Set Half Duplex
+		usData &= ~0x100;
+		MiWrite(pAdapter, PHY_CONTROL, usData);
+	}
+}
+
+void ET1310_PhySpeedSelect(struct et131x_adapter *pAdapter, uint16_t speed)
+{
+	uint16_t usData;
+
+	// Read the PHY control register
+	MiRead(pAdapter, PHY_CONTROL, &usData);
+
+	// Clear all Speed settings (Bits 6, 13)
+	usData &= ~0x2040;
+
+	// Reset the speed bits based on user selection
+	switch (speed) {
+	case TRUEPHY_SPEED_10MBPS:
+		// Bits already cleared above, do nothing
+		break;
+
+	case TRUEPHY_SPEED_100MBPS:
+		// 100M == Set bit 13
+		usData |= 0x2000;
+		break;
+
+	case TRUEPHY_SPEED_1000MBPS:
+	default:
+		usData |= 0x0040;
+		break;
+	}
+
+	// Write back the new speed
+	MiWrite(pAdapter, PHY_CONTROL, usData);
+}
+
+void ET1310_PhyAdvertise1000BaseT(struct et131x_adapter *pAdapter,
+				  uint16_t duplex)
+{
+	uint16_t usData;
+
+	// Read the PHY 1000 Base-T Control Register
+	MiRead(pAdapter, PHY_1000_CONTROL, &usData);
+
+	// Clear Bits 8,9
+	usData &= ~0x0300;
+
+	switch (duplex) {
+	case TRUEPHY_ADV_DUPLEX_NONE:
+		// Duplex already cleared, do nothing
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_FULL:
+		// Set Bit 9
+		usData |= 0x0200;
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_HALF:
+		// Set Bit 8
+		usData |= 0x0100;
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_BOTH:
+	default:
+		usData |= 0x0300;
+		break;
+	}
+
+	// Write back advertisement
+	MiWrite(pAdapter, PHY_1000_CONTROL, usData);
+}
+
+void ET1310_PhyAdvertise100BaseT(struct et131x_adapter *pAdapter,
+				 uint16_t duplex)
+{
+	uint16_t usData;
+
+	// Read the Autonegotiation Register (10/100)
+	MiRead(pAdapter, PHY_AUTO_ADVERTISEMENT, &usData);
+
+	// Clear bits 7,8
+	usData &= ~0x0180;
+
+	switch (duplex) {
+	case TRUEPHY_ADV_DUPLEX_NONE:
+		// Duplex already cleared, do nothing
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_FULL:
+		// Set Bit 8
+		usData |= 0x0100;
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_HALF:
+		// Set Bit 7
+		usData |= 0x0080;
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_BOTH:
+	default:
+		// Set Bits 7,8
+		usData |= 0x0180;
+		break;
+	}
+
+	// Write back advertisement
+	MiWrite(pAdapter, PHY_AUTO_ADVERTISEMENT, usData);
+}
+
+void ET1310_PhyAdvertise10BaseT(struct et131x_adapter *pAdapter,
+				uint16_t duplex)
+{
+	uint16_t usData;
+
+	// Read the Autonegotiation Register (10/100)
+	MiRead(pAdapter, PHY_AUTO_ADVERTISEMENT, &usData);
+
+	// Clear bits 5,6
+	usData &= ~0x0060;
+
+	switch (duplex) {
+	case TRUEPHY_ADV_DUPLEX_NONE:
+		// Duplex already cleared, do nothing
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_FULL:
+		// Set Bit 6
+		usData |= 0x0040;
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_HALF:
+		// Set Bit 5
+		usData |= 0x0020;
+		break;
+
+	case TRUEPHY_ADV_DUPLEX_BOTH:
+	default:
+		// Set Bits 5,6
+		usData |= 0x0060;
+		break;
+	}
+
+	// Write back advertisement
+	MiWrite(pAdapter, PHY_AUTO_ADVERTISEMENT, usData);
+}
+
+void ET1310_PhyLinkStatus(struct et131x_adapter *pAdapter,
+			  uint8_t *ucLinkStatus,
+			  uint32_t *uiAutoNeg,
+			  uint32_t *uiLinkSpeed,
+			  uint32_t *uiDuplexMode,
+			  uint32_t *uiMdiMdix,
+			  uint32_t *uiMasterSlave, uint32_t *uiPolarity)
+{
+	uint16_t usMiStatus = 0;
+	uint16_t us1000BaseT = 0;
+	uint16_t usVmiPhyStatus = 0;
+	uint16_t usControl = 0;
+
+	MiRead(pAdapter, PHY_STATUS, &usMiStatus);
+	MiRead(pAdapter, PHY_1000_STATUS, &us1000BaseT);
+	MiRead(pAdapter, PHY_PHY_STATUS, &usVmiPhyStatus);
+	MiRead(pAdapter, PHY_CONTROL, &usControl);
+
+	if (ucLinkStatus) {
+		*ucLinkStatus =
+		    (unsigned char)((usVmiPhyStatus & 0x0040) ? 1 : 0);
+	}
+
+	if (uiAutoNeg) {
+		*uiAutoNeg =
+		    (usControl & 0x1000) ? ((usVmiPhyStatus & 0x0020) ?
+					    TRUEPHY_ANEG_COMPLETE :
+					    TRUEPHY_ANEG_NOT_COMPLETE) :
+		    TRUEPHY_ANEG_DISABLED;
+	}
+
+	if (uiLinkSpeed) {
+		*uiLinkSpeed = (usVmiPhyStatus & 0x0300) >> 8;
+	}
+
+	if (uiDuplexMode) {
+		*uiDuplexMode = (usVmiPhyStatus & 0x0080) >> 7;
+	}
+
+	if (uiMdiMdix) {
+		/* NOTE: Need to complete this */
+		*uiMdiMdix = 0;
+	}
+
+	if (uiMasterSlave) {
+		*uiMasterSlave =
+		    (us1000BaseT & 0x4000) ? TRUEPHY_CFG_MASTER :
+		    TRUEPHY_CFG_SLAVE;
+	}
+
+	if (uiPolarity) {
+		*uiPolarity =
+		    (usVmiPhyStatus & 0x0400) ? TRUEPHY_POLARITY_INVERTED :
+		    TRUEPHY_POLARITY_NORMAL;
+	}
+}
+
+void ET1310_PhyAndOrReg(struct et131x_adapter *pAdapter,
+			uint16_t regnum, uint16_t andMask, uint16_t orMask)
+{
+	uint16_t reg;
+
+	// Read the requested register
+	MiRead(pAdapter, regnum, &reg);
+
+	// Apply the AND mask
+	reg &= andMask;
+
+	// Apply the OR mask
+	reg |= orMask;
+
+	// Write the value back to the register
+	MiWrite(pAdapter, regnum, reg);
+}
+
+void ET1310_PhyAccessMiBit(struct et131x_adapter *pAdapter, uint16_t action,
+			   uint16_t regnum, uint16_t bitnum, uint8_t *value)
+{
+	uint16_t reg;
+	uint16_t mask = 0;
+
+	// Create a mask to isolate the requested bit
+	mask = 0x0001 << bitnum;
+
+	// Read the requested register
+	MiRead(pAdapter, regnum, &reg);
+
+	switch (action) {
+	case TRUEPHY_BIT_READ:
+		if (value != NULL) {
+			*value = (reg & mask) >> bitnum;
+		}
+		break;
+
+	case TRUEPHY_BIT_SET:
+		reg |= mask;
+		MiWrite(pAdapter, regnum, reg);
+		break;
+
+	case TRUEPHY_BIT_CLEAR:
+		reg &= ~mask;
+		MiWrite(pAdapter, regnum, reg);
+		break;
+
+	default:
+		break;
+	}
+}
diff --git a/drivers/staging/et131x/et1310_phy.h b/drivers/staging/et131x/et1310_phy.h
new file mode 100644
index 0000000..d624cbb
--- /dev/null
+++ b/drivers/staging/et131x/et1310_phy.h
@@ -0,0 +1,910 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_phy.h - Defines, structs, enums, prototypes, etc. pertaining to the
+ *                PHY.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef _ET1310_PHY_H_
+#define _ET1310_PHY_H_
+
+#include "et1310_address_map.h"
+
+#define TRUEPHY_SUCCESS 0
+#define TRUEPHY_FAILURE 1
+typedef void *TRUEPHY_HANDLE;
+typedef void *TRUEPHY_PLATFORM_HANDLE;
+typedef void *TRUEPHY_OSAL_HANDLE;
+
+/* MI Register Addresses */
+#define MI_CONTROL_REG                      0
+#define MI_STATUS_REG                       1
+#define MI_PHY_IDENTIFIER_1_REG             2
+#define MI_PHY_IDENTIFIER_2_REG             3
+#define MI_AUTONEG_ADVERTISEMENT_REG        4
+#define MI_AUTONEG_LINK_PARTNER_ABILITY_REG 5
+#define MI_AUTONEG_EXPANSION_REG            6
+#define MI_AUTONEG_NEXT_PAGE_TRANSMIT_REG   7
+#define MI_LINK_PARTNER_NEXT_PAGE_REG       8
+#define MI_1000BASET_CONTROL_REG            9
+#define MI_1000BASET_STATUS_REG             10
+#define MI_RESERVED11_REG                   11
+#define MI_RESERVED12_REG                   12
+#define MI_RESERVED13_REG                   13
+#define MI_RESERVED14_REG                   14
+#define MI_EXTENDED_STATUS_REG              15
+
+/* VMI Register Addresses */
+#define VMI_RESERVED16_REG                  16
+#define VMI_RESERVED17_REG                  17
+#define VMI_RESERVED18_REG                  18
+#define VMI_LOOPBACK_CONTROL_REG            19
+#define VMI_RESERVED20_REG                  20
+#define VMI_MI_CONTROL_REG                  21
+#define VMI_PHY_CONFIGURATION_REG           22
+#define VMI_PHY_CONTROL_REG                 23
+#define VMI_INTERRUPT_MASK_REG              24
+#define VMI_INTERRUPT_STATUS_REG            25
+#define VMI_PHY_STATUS_REG                  26
+#define VMI_LED_CONTROL_1_REG               27
+#define VMI_LED_CONTROL_2_REG               28
+#define VMI_RESERVED29_REG                  29
+#define VMI_RESERVED30_REG                  30
+#define VMI_RESERVED31_REG                  31
+
+/* PHY Register Mapping(MI) Management Interface Regs */
+typedef struct _MI_REGS_t {
+	u8 bmcr;		// Basic mode control reg(Reg 0x00)
+	u8 bmsr;		// Basic mode status reg(Reg 0x01)
+	u8 idr1;		// Phy identifier reg 1(Reg 0x02)
+	u8 idr2;		// Phy identifier reg 2(Reg 0x03)
+	u8 anar;		// Auto-Negotiation advertisement(Reg 0x04)
+	u8 anlpar;		// Auto-Negotiation link Partner Ability(Reg 0x05)
+	u8 aner;		// Auto-Negotiation expansion reg(Reg 0x06)
+	u8 annptr;		// Auto-Negotiation next page transmit reg(Reg 0x07)
+	u8 lpnpr;		// link partner next page reg(Reg 0x08)
+	u8 gcr;		// Gigabit basic mode control reg(Reg 0x09)
+	u8 gsr;		// Gigabit basic mode status reg(Reg 0x0A)
+	u8 mi_res1[4];	// Future use by MI working group(Reg 0x0B - 0x0E)
+	u8 esr;		// Extended status reg(Reg 0x0F)
+	u8 mi_res2[3];	// Future use by MI working group(Reg 0x10 - 0x12)
+	u8 loop_ctl;	// Loopback Control Reg(Reg 0x13)
+	u8 mi_res3;	// Future use by MI working group(Reg 0x14)
+	u8 mcr;		// MI Control Reg(Reg 0x15)
+	u8 pcr;		// Configuration Reg(Reg 0x16)
+	u8 phy_ctl;	// PHY Control Reg(Reg 0x17)
+	u8 imr;		// Interrupt Mask Reg(Reg 0x18)
+	u8 isr;		// Interrupt Status Reg(Reg 0x19)
+	u8 psr;		// PHY Status Reg(Reg 0x1A)
+	u8 lcr1;		// LED Control 1 Reg(Reg 0x1B)
+	u8 lcr2;		// LED Control 2 Reg(Reg 0x1C)
+	u8 mi_res4[3];	// Future use by MI working group(Reg 0x1D - 0x1F)
+} MI_REGS_t, *PMI_REGS_t;
+
+/* MI Register 0: Basic mode control register */
+typedef union _MI_BMCR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 reset:1;		// bit 15
+		u16 loopback:1;		// bit 14
+		u16 speed_sel:1;		// bit 13
+		u16 enable_autoneg:1;	// bit 12
+		u16 power_down:1;		// bit 11
+		u16 isolate:1;		// bit 10
+		u16 restart_autoneg:1;	// bit 9
+		u16 duplex_mode:1;		// bit 8
+		u16 col_test:1;		// bit 7
+		u16 speed_1000_sel:1;	// bit 6
+		u16 res1:6;		// bits 0-5
+#else
+		u16 res1:6;		// bits 0-5
+		u16 speed_1000_sel:1;	// bit 6
+		u16 col_test:1;		// bit 7
+		u16 duplex_mode:1;		// bit 8
+		u16 restart_autoneg:1;	// bit 9
+		u16 isolate:1;		// bit 10
+		u16 power_down:1;		// bit 11
+		u16 enable_autoneg:1;	// bit 12
+		u16 speed_sel:1;		// bit 13
+		u16 loopback:1;		// bit 14
+		u16 reset:1;		// bit 15
+#endif
+	} bits;
+} MI_BMCR_t, *PMI_BMCR_t;
+
+/* MI Register 1:  Basic mode status register */
+typedef union _MI_BMSR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 link_100T4:1;		// bit 15
+		u16 link_100fdx:1;		// bit 14
+		u16 link_100hdx:1;		// bit 13
+		u16 link_10fdx:1;		// bit 12
+		u16 link_10hdx:1;		// bit 11
+		u16 link_100T2fdx:1;	// bit 10
+		u16 link_100T2hdx:1;	// bit 9
+		u16 extend_status:1;	// bit 8
+		u16 res1:1;		// bit 7
+		u16 preamble_supress:1;	// bit 6
+		u16 auto_neg_complete:1;	// bit 5
+		u16 remote_fault:1;	// bit 4
+		u16 auto_neg_able:1;	// bit 3
+		u16 link_status:1;		// bit 2
+		u16 jabber_detect:1;	// bit 1
+		u16 ext_cap:1;		// bit 0
+#else
+		u16 ext_cap:1;		// bit 0
+		u16 jabber_detect:1;	// bit 1
+		u16 link_status:1;		// bit 2
+		u16 auto_neg_able:1;	// bit 3
+		u16 remote_fault:1;	// bit 4
+		u16 auto_neg_complete:1;	// bit 5
+		u16 preamble_supress:1;	// bit 6
+		u16 res1:1;		// bit 7
+		u16 extend_status:1;	// bit 8
+		u16 link_100T2hdx:1;	// bit 9
+		u16 link_100T2fdx:1;	// bit 10
+		u16 link_10hdx:1;		// bit 11
+		u16 link_10fdx:1;		// bit 12
+		u16 link_100hdx:1;		// bit 13
+		u16 link_100fdx:1;		// bit 14
+		u16 link_100T4:1;		// bit 15
+#endif
+	} bits;
+} MI_BMSR_t, *PMI_BMSR_t;
+
+/* MI Register 2: Physical Identifier 1 */
+typedef union _MI_IDR1_t {
+	u16 value;
+	struct {
+		u16 ieee_address:16;	// 0x0282 default(bits 0-15)
+	} bits;
+} MI_IDR1_t, *PMI_IDR1_t;
+
+/* MI Register 3: Physical Identifier 2 */
+typedef union _MI_IDR2_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 ieee_address:6;	// 111100 default(bits 10-15)
+		u16 model_no:6;		// 000001 default(bits 4-9)
+		u16 rev_no:4;		// 0010   default(bits 0-3)
+#else
+		u16 rev_no:4;		// 0010   default(bits 0-3)
+		u16 model_no:6;		// 000001 default(bits 4-9)
+		u16 ieee_address:6;	// 111100 default(bits 10-15)
+#endif
+	} bits;
+} MI_IDR2_t, *PMI_IDR2_t;
+
+/* MI Register 4: Auto-negotiation advertisement register */
+typedef union _MI_ANAR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 np_indication:1;	// bit 15
+		u16 res2:1;		// bit 14
+		u16 remote_fault:1;	// bit 13
+		u16 res1:1;		// bit 12
+		u16 cap_asmpause:1;	// bit 11
+		u16 cap_pause:1;		// bit 10
+		u16 cap_100T4:1;		// bit 9
+		u16 cap_100fdx:1;		// bit 8
+		u16 cap_100hdx:1;		// bit 7
+		u16 cap_10fdx:1;		// bit 6
+		u16 cap_10hdx:1;		// bit 5
+		u16 selector:5;		// bits 0-4
+#else
+		u16 selector:5;		// bits 0-4
+		u16 cap_10hdx:1;		// bit 5
+		u16 cap_10fdx:1;		// bit 6
+		u16 cap_100hdx:1;		// bit 7
+		u16 cap_100fdx:1;		// bit 8
+		u16 cap_100T4:1;		// bit 9
+		u16 cap_pause:1;		// bit 10
+		u16 cap_asmpause:1;	// bit 11
+		u16 res1:1;		// bit 12
+		u16 remote_fault:1;	// bit 13
+		u16 res2:1;		// bit 14
+		u16 np_indication:1;	// bit 15
+#endif
+	} bits;
+} MI_ANAR_t, *PMI_ANAR_t;
+
+/* MI Register 5: Auto-negotiation link partner advertisement register */
+typedef struct _MI_ANLPAR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 np_indication:1;	// bit 15
+		u16 acknowledge:1;		// bit 14
+		u16 remote_fault:1;	// bit 13
+		u16 res1:1;		// bit 12
+		u16 cap_asmpause:1;	// bit 11
+		u16 cap_pause:1;		// bit 10
+		u16 cap_100T4:1;		// bit 9
+		u16 cap_100fdx:1;		// bit 8
+		u16 cap_100hdx:1;		// bit 7
+		u16 cap_10fdx:1;		// bit 6
+		u16 cap_10hdx:1;		// bit 5
+		u16 selector:5;		// bits 0-4
+#else
+		u16 selector:5;		// bits 0-4
+		u16 cap_10hdx:1;		// bit 5
+		u16 cap_10fdx:1;		// bit 6
+		u16 cap_100hdx:1;		// bit 7
+		u16 cap_100fdx:1;		// bit 8
+		u16 cap_100T4:1;		// bit 9
+		u16 cap_pause:1;		// bit 10
+		u16 cap_asmpause:1;	// bit 11
+		u16 res1:1;		// bit 12
+		u16 remote_fault:1;	// bit 13
+		u16 acknowledge:1;		// bit 14
+		u16 np_indication:1;	// bit 15
+#endif
+	} bits;
+} MI_ANLPAR_t, *PMI_ANLPAR_t;
+
+/* MI Register 6: Auto-negotiation expansion register */
+typedef union _MI_ANER_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res:11;	// bits 5-15
+		u16 pdf:1;		// bit 4
+		u16 lp_np_able:1;	// bit 3
+		u16 np_able:1;	// bit 2
+		u16 page_rx:1;	// bit 1
+		u16 lp_an_able:1;	// bit 0
+#else
+		u16 lp_an_able:1;	// bit 0
+		u16 page_rx:1;	// bit 1
+		u16 np_able:1;	// bit 2
+		u16 lp_np_able:1;	// bit 3
+		u16 pdf:1;		// bit 4
+		u16 res:11;	// bits 5-15
+#endif
+	} bits;
+} MI_ANER_t, *PMI_ANER_t;
+
+/* MI Register 7: Auto-negotiation next page transmit reg(0x07) */
+typedef union _MI_ANNPTR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 np:1;		// bit 15
+		u16 res1:1;	// bit 14
+		u16 msg_page:1;	// bit 13
+		u16 ack2:1;	// bit 12
+		u16 toggle:1;	// bit 11
+		u16 msg:11;	// bits 0-10
+#else
+		u16 msg:11;	// bits 0-10
+		u16 toggle:1;	// bit 11
+		u16 ack2:1;	// bit 12
+		u16 msg_page:1;	// bit 13
+		u16 res1:1;	// bit 14
+		u16 np:1;		// bit 15
+#endif
+	} bits;
+} MI_ANNPTR_t, *PMI_ANNPTR_t;
+
+/* MI Register 8: Link Partner Next Page Reg(0x08) */
+typedef union _MI_LPNPR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 np:1;		// bit 15
+		u16 ack:1;		// bit 14
+		u16 msg_page:1;	// bit 13
+		u16 ack2:1;	// bit 12
+		u16 toggle:1;	// bit 11
+		u16 msg:11;	// bits 0-10
+#else
+		u16 msg:11;	// bits 0-10
+		u16 toggle:1;	// bit 11
+		u16 ack2:1;	// bit 12
+		u16 msg_page:1;	// bit 13
+		u16 ack:1;		// bit 14
+		u16 np:1;		// bit 15
+#endif
+	} bits;
+} MI_LPNPR_t, *PMI_LPNPR_t;
+
+/* MI Register 9: 1000BaseT Control Reg(0x09) */
+typedef union _MI_GCR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 test_mode:3;		// bits 13-15
+		u16 ms_config_en:1;	// bit 12
+		u16 ms_value:1;		// bit 11
+		u16 port_type:1;		// bit 10
+		u16 link_1000fdx:1;	// bit 9
+		u16 link_1000hdx:1;	// bit 8
+		u16 res:8;			// bit 0-7
+#else
+		u16 res:8;			// bit 0-7
+		u16 link_1000hdx:1;	// bit 8
+		u16 link_1000fdx:1;	// bit 9
+		u16 port_type:1;		// bit 10
+		u16 ms_value:1;		// bit 11
+		u16 ms_config_en:1;	// bit 12
+		u16 test_mode:3;		// bits 13-15
+#endif
+	} bits;
+} MI_GCR_t, *PMI_GCR_t;
+
+/* MI Register 10: 1000BaseT Status Reg(0x0A) */
+typedef union _MI_GSR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 ms_config_fault:1;	// bit 15
+		u16 ms_resolve:1;		// bit 14
+		u16 local_rx_status:1;	// bit 13
+		u16 remote_rx_status:1;	// bit 12
+		u16 link_1000fdx:1;	// bit 11
+		u16 link_1000hdx:1;	// bit 10
+		u16 res:2;			// bits 8-9
+		u16 idle_err_cnt:8;	// bits 0-7
+#else
+		u16 idle_err_cnt:8;	// bits 0-7
+		u16 res:2;			// bits 8-9
+		u16 link_1000hdx:1;	// bit 10
+		u16 link_1000fdx:1;	// bit 11
+		u16 remote_rx_status:1;	// bit 12
+		u16 local_rx_status:1;	// bit 13
+		u16 ms_resolve:1;		// bit 14
+		u16 ms_config_fault:1;	// bit 15
+#endif
+	} bits;
+} MI_GSR_t, *PMI_GSR_t;
+
+/* MI Register 11 - 14: Reserved Regs(0x0B - 0x0E) */
+typedef union _MI_RES_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res15:1;	// bit 15
+		u16 res14:1;	// bit 14
+		u16 res13:1;	// bit 13
+		u16 res12:1;	// bit 12
+		u16 res11:1;	// bit 11
+		u16 res10:1;	// bit 10
+		u16 res9:1;	// bit 9
+		u16 res8:1;	// bit 8
+		u16 res7:1;	// bit 7
+		u16 res6:1;	// bit 6
+		u16 res5:1;	// bit 5
+		u16 res4:1;	// bit 4
+		u16 res3:1;	// bit 3
+		u16 res2:1;	// bit 2
+		u16 res1:1;	// bit 1
+		u16 res0:1;	// bit 0
+#else
+		u16 res0:1;	// bit 0
+		u16 res1:1;	// bit 1
+		u16 res2:1;	// bit 2
+		u16 res3:1;	// bit 3
+		u16 res4:1;	// bit 4
+		u16 res5:1;	// bit 5
+		u16 res6:1;	// bit 6
+		u16 res7:1;	// bit 7
+		u16 res8:1;	// bit 8
+		u16 res9:1;	// bit 9
+		u16 res10:1;	// bit 10
+		u16 res11:1;	// bit 11
+		u16 res12:1;	// bit 12
+		u16 res13:1;	// bit 13
+		u16 res14:1;	// bit 14
+		u16 res15:1;	// bit 15
+#endif
+	} bits;
+} MI_RES_t, *PMI_RES_t;
+
+/* MI Register 15: Extended status Reg(0x0F) */
+typedef union _MI_ESR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 link_1000Xfdx:1;	// bit 15
+		u16 link_1000Xhdx:1;	// bit 14
+		u16 link_1000fdx:1;	// bit 13
+		u16 link_1000hdx:1;	// bit 12
+		u16 res:12;		// bit 0-11
+#else
+		u16 res:12;		// bit 0-11
+		u16 link_1000hdx:1;	// bit 12
+		u16 link_1000fdx:1;	// bit 13
+		u16 link_1000Xhdx:1;	// bit 14
+		u16 link_1000Xfdx:1;	// bit 15
+#endif
+	} bits;
+} MI_ESR_t, *PMI_ESR_t;
+
+/* MI Register 16 - 18: Reserved Reg(0x10-0x12) */
+
+/* MI Register 19: Loopback Control Reg(0x13) */
+typedef union _MI_LCR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 mii_en:1;		// bit 15
+		u16 pcs_en:1;		// bit 14
+		u16 pmd_en:1;		// bit 13
+		u16 all_digital_en:1;	// bit 12
+		u16 replica_en:1;		// bit 11
+		u16 line_driver_en:1;	// bit 10
+		u16 res:10;		// bit 0-9
+#else
+		u16 res:10;		// bit 0-9
+		u16 line_driver_en:1;	// bit 10
+		u16 replica_en:1;		// bit 11
+		u16 all_digital_en:1;	// bit 12
+		u16 pmd_en:1;		// bit 13
+		u16 pcs_en:1;		// bit 14
+		u16 mii_en:1;		// bit 15
+#endif
+	} bits;
+} MI_LCR_t, *PMI_LCR_t;
+
+/* MI Register 20: Reserved Reg(0x14) */
+
+/* MI Register 21: Management Interface Control Reg(0x15) */
+typedef union _MI_MICR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res1:5;		// bits 11-15
+		u16 mi_error_count:7;	// bits 4-10
+		u16 res2:1;		// bit 3
+		u16 ignore_10g_fr:1;	// bit 2
+		u16 res3:1;		// bit 1
+		u16 preamble_supress_en:1;	// bit 0
+#else
+		u16 preamble_supress_en:1;	// bit 0
+		u16 res3:1;		// bit 1
+		u16 ignore_10g_fr:1;	// bit 2
+		u16 res2:1;		// bit 3
+		u16 mi_error_count:7;	// bits 4-10
+		u16 res1:5;		// bits 11-15
+#endif
+	} bits;
+} MI_MICR_t, *PMI_MICR_t;
+
+/* MI Register 22: PHY Configuration Reg(0x16) */
+typedef union _MI_PHY_CONFIG_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 crs_tx_en:1;		// bit 15
+		u16 res1:1;		// bit 14
+		u16 tx_fifo_depth:2;	// bits 12-13
+		u16 speed_downshift:2;	// bits 10-11
+		u16 pbi_detect:1;		// bit 9
+		u16 tbi_rate:1;		// bit 8
+		u16 alternate_np:1;	// bit 7
+		u16 group_mdio_en:1;	// bit 6
+		u16 tx_clock_en:1;		// bit 5
+		u16 sys_clock_en:1;	// bit 4
+		u16 res2:1;		// bit 3
+		u16 mac_if_mode:3;		// bits 0-2
+#else
+		u16 mac_if_mode:3;		// bits 0-2
+		u16 res2:1;		// bit 3
+		u16 sys_clock_en:1;	// bit 4
+		u16 tx_clock_en:1;		// bit 5
+		u16 group_mdio_en:1;	// bit 6
+		u16 alternate_np:1;	// bit 7
+		u16 tbi_rate:1;		// bit 8
+		u16 pbi_detect:1;		// bit 9
+		u16 speed_downshift:2;	// bits 10-11
+		u16 tx_fifo_depth:2;	// bits 12-13
+		u16 res1:1;		// bit 14
+		u16 crs_tx_en:1;		// bit 15
+#endif
+	} bits;
+} MI_PHY_CONFIG_t, *PMI_PHY_CONFIG_t;
+
+/* MI Register 23: PHY CONTROL Reg(0x17) */
+typedef union _MI_PHY_CONTROL_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res1:1;		// bit 15
+		u16 tdr_en:1;		// bit 14
+		u16 res2:1;		// bit 13
+		u16 downshift_attempts:2;	// bits 11-12
+		u16 res3:5;		// bit 6-10
+		u16 jabber_10baseT:1;	// bit 5
+		u16 sqe_10baseT:1;		// bit 4
+		u16 tp_loopback_10baseT:1;	// bit 3
+		u16 preamble_gen_en:1;	// bit 2
+		u16 res4:1;		// bit 1
+		u16 force_int:1;		// bit 0
+#else
+		u16 force_int:1;		// bit 0
+		u16 res4:1;		// bit 1
+		u16 preamble_gen_en:1;	// bit 2
+		u16 tp_loopback_10baseT:1;	// bit 3
+		u16 sqe_10baseT:1;		// bit 4
+		u16 jabber_10baseT:1;	// bit 5
+		u16 res3:5;		// bit 6-10
+		u16 downshift_attempts:2;	// bits 11-12
+		u16 res2:1;		// bit 13
+		u16 tdr_en:1;		// bit 14
+		u16 res1:1;		// bit 15
+#endif
+	} bits;
+} MI_PHY_CONTROL_t, *PMI_PHY_CONTROL_t;
+
+/* MI Register 24: Interrupt Mask Reg(0x18) */
+typedef union _MI_IMR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res1:6;		// bits 10-15
+		u16 mdio_sync_lost:1;	// bit 9
+		u16 autoneg_status:1;	// bit 8
+		u16 hi_bit_err:1;		// bit 7
+		u16 np_rx:1;		// bit 6
+		u16 err_counter_full:1;	// bit 5
+		u16 fifo_over_underflow:1;	// bit 4
+		u16 rx_status:1;		// bit 3
+		u16 link_status:1;		// bit 2
+		u16 automatic_speed:1;	// bit 1
+		u16 int_en:1;		// bit 0
+#else
+		u16 int_en:1;		// bit 0
+		u16 automatic_speed:1;	// bit 1
+		u16 link_status:1;		// bit 2
+		u16 rx_status:1;		// bit 3
+		u16 fifo_over_underflow:1;	// bit 4
+		u16 err_counter_full:1;	// bit 5
+		u16 np_rx:1;		// bit 6
+		u16 hi_bit_err:1;		// bit 7
+		u16 autoneg_status:1;	// bit 8
+		u16 mdio_sync_lost:1;	// bit 9
+		u16 res1:6;		// bits 10-15
+#endif
+	} bits;
+} MI_IMR_t, *PMI_IMR_t;
+
+/* MI Register 25: Interrupt Status Reg(0x19) */
+typedef union _MI_ISR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res1:6;		// bits 10-15
+		u16 mdio_sync_lost:1;	// bit 9
+		u16 autoneg_status:1;	// bit 8
+		u16 hi_bit_err:1;		// bit 7
+		u16 np_rx:1;		// bit 6
+		u16 err_counter_full:1;	// bit 5
+		u16 fifo_over_underflow:1;	// bit 4
+		u16 rx_status:1;		// bit 3
+		u16 link_status:1;		// bit 2
+		u16 automatic_speed:1;	// bit 1
+		u16 int_en:1;		// bit 0
+#else
+		u16 int_en:1;		// bit 0
+		u16 automatic_speed:1;	// bit 1
+		u16 link_status:1;		// bit 2
+		u16 rx_status:1;		// bit 3
+		u16 fifo_over_underflow:1;	// bit 4
+		u16 err_counter_full:1;	// bit 5
+		u16 np_rx:1;		// bit 6
+		u16 hi_bit_err:1;		// bit 7
+		u16 autoneg_status:1;	// bit 8
+		u16 mdio_sync_lost:1;	// bit 9
+		u16 res1:6;		// bits 10-15
+#endif
+	} bits;
+} MI_ISR_t, *PMI_ISR_t;
+
+/* MI Register 26: PHY Status Reg(0x1A) */
+typedef union _MI_PSR_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res1:1;		// bit 15
+		u16 autoneg_fault:2;	// bit 13-14
+		u16 autoneg_status:1;	// bit 12
+		u16 mdi_x_status:1;	// bit 11
+		u16 polarity_status:1;	// bit 10
+		u16 speed_status:2;	// bits 8-9
+		u16 duplex_status:1;	// bit 7
+		u16 link_status:1;		// bit 6
+		u16 tx_status:1;		// bit 5
+		u16 rx_status:1;		// bit 4
+		u16 collision_status:1;	// bit 3
+		u16 autoneg_en:1;		// bit 2
+		u16 pause_en:1;		// bit 1
+		u16 asymmetric_dir:1;	// bit 0
+#else
+		u16 asymmetric_dir:1;	// bit 0
+		u16 pause_en:1;		// bit 1
+		u16 autoneg_en:1;		// bit 2
+		u16 collision_status:1;	// bit 3
+		u16 rx_status:1;		// bit 4
+		u16 tx_status:1;		// bit 5
+		u16 link_status:1;		// bit 6
+		u16 duplex_status:1;	// bit 7
+		u16 speed_status:2;	// bits 8-9
+		u16 polarity_status:1;	// bit 10
+		u16 mdi_x_status:1;	// bit 11
+		u16 autoneg_status:1;	// bit 12
+		u16 autoneg_fault:2;	// bit 13-14
+		u16 res1:1;		// bit 15
+#endif
+	} bits;
+} MI_PSR_t, *PMI_PSR_t;
+
+/* MI Register 27: LED Control Reg 1(0x1B) */
+typedef union _MI_LCR1_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 res1:2;		// bits 14-15
+		u16 led_dup_indicate:2;	// bits 12-13
+		u16 led_10baseT:2;		// bits 10-11
+		u16 led_collision:2;	// bits 8-9
+		u16 res2:2;		// bits 6-7
+		u16 res3:2;		// bits 4-5
+		u16 pulse_dur:2;		// bits 2-3
+		u16 pulse_stretch1:1;	// bit 1
+		u16 pulse_stretch0:1;	// bit 0
+#else
+		u16 pulse_stretch0:1;	// bit 0
+		u16 pulse_stretch1:1;	// bit 1
+		u16 pulse_dur:2;		// bits 2-3
+		u16 res3:2;		// bits 4-5
+		u16 res2:2;		// bits 6-7
+		u16 led_collision:2;	// bits 8-9
+		u16 led_10baseT:2;		// bits 10-11
+		u16 led_dup_indicate:2;	// bits 12-13
+		u16 res1:2;		// bits 14-15
+#endif
+	} bits;
+} MI_LCR1_t, *PMI_LCR1_t;
+
+/* MI Register 28: LED Control Reg 2(0x1C) */
+typedef union _MI_LCR2_t {
+	u16 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u16 led_link:4;		// bits 12-15
+		u16 led_tx_rx:4;		// bits 8-11
+		u16 led_100BaseTX:4;	// bits 4-7
+		u16 led_1000BaseT:4;	// bits 0-3
+#else
+		u16 led_1000BaseT:4;	// bits 0-3
+		u16 led_100BaseTX:4;	// bits 4-7
+		u16 led_tx_rx:4;		// bits 8-11
+		u16 led_link:4;		// bits 12-15
+#endif
+	} bits;
+} MI_LCR2_t, *PMI_LCR2_t;
+
+/* MI Register 29 - 31: Reserved Reg(0x1D - 0x1E) */
+
+/* TruePHY headers */
+typedef struct _TRUEPHY_ACCESS_MI_REGS_ {
+	TRUEPHY_HANDLE hTruePhy;
+	int32_t nPhyId;
+	u8 bReadWrite;
+	u8 *pbyRegs;
+	u8 *pwData;
+	int32_t nRegCount;
+} TRUEPHY_ACCESS_MI_REGS, *PTRUEPHY_ACCESS_MI_REGS;
+
+/* TruePHY headers */
+typedef struct _TAG_TPAL_ACCESS_MI_REGS_ {
+	u32 nPhyId;
+	u8 bReadWrite;
+	u32 nRegCount;
+	u16 Data[4096];
+	u8 Regs[4096];
+} TPAL_ACCESS_MI_REGS, *PTPAL_ACCESS_MI_REGS;
+
+
+typedef TRUEPHY_HANDLE TPAL_HANDLE;
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+/* OS Specific Functions*/
+void TPAL_SetPhy10HalfDuplex(struct et131x_adapter *adapter);
+void TPAL_SetPhy10FullDuplex(struct et131x_adapter *adapter);
+void TPAL_SetPhy10Force(struct et131x_adapter *pAdapter);
+void TPAL_SetPhy100HalfDuplex(struct et131x_adapter *adapter);
+void TPAL_SetPhy100FullDuplex(struct et131x_adapter *adapter);
+void TPAL_SetPhy100Force(struct et131x_adapter *pAdapter);
+void TPAL_SetPhy1000FullDuplex(struct et131x_adapter *adapter);
+void TPAL_SetPhyAutoNeg(struct et131x_adapter *adapter);
+
+/* Prototypes for ET1310_phy.c */
+int et131x_xcvr_find(struct et131x_adapter *adapter);
+int et131x_setphy_normal(struct et131x_adapter *adapter);
+int32_t PhyMiRead(struct et131x_adapter *adapter,
+	       u8 xcvrAddr, u8 xcvrReg, u16 *value);
+
+/* static inline function does not work because et131x_adapter is not always
+ * defined
+ */
+#define MiRead(adapter, xcvrReg, value) \
+	PhyMiRead((adapter), (adapter)->Stats.xcvr_addr, (xcvrReg), (value))
+
+int32_t MiWrite(struct et131x_adapter *adapter,
+		u8 xcvReg, u16 value);
+void et131x_Mii_check(struct et131x_adapter *pAdapter,
+		      MI_BMSR_t bmsr, MI_BMSR_t bmsr_ints);
+
+/* This last is not strictly required (the driver could call the TPAL
+ * version instead), but this sets the adapter up correctly, and calls the
+ * access routine indirectly.  This protects the driver from changes in TPAL.
+ */
+void SetPhy_10BaseTHalfDuplex(struct et131x_adapter *adapter);
+
+/* Defines for PHY access routines */
+
+// Define bit operation flags
+#define TRUEPHY_BIT_CLEAR               0
+#define TRUEPHY_BIT_SET                 1
+#define TRUEPHY_BIT_READ                2
+
+// Define read/write operation flags
+#ifndef TRUEPHY_READ
+#define TRUEPHY_READ                    0
+#define TRUEPHY_WRITE                   1
+#define TRUEPHY_MASK                    2
+#endif
+
+// Define speeds
+#define TRUEPHY_SPEED_10MBPS            0
+#define TRUEPHY_SPEED_100MBPS           1
+#define TRUEPHY_SPEED_1000MBPS          2
+
+// Define duplex modes
+#define TRUEPHY_DUPLEX_HALF             0
+#define TRUEPHY_DUPLEX_FULL             1
+
+// Define master/slave configuration values
+#define TRUEPHY_CFG_SLAVE               0
+#define TRUEPHY_CFG_MASTER              1
+
+// Define MDI/MDI-X settings
+#define TRUEPHY_MDI                     0
+#define TRUEPHY_MDIX                    1
+#define TRUEPHY_AUTO_MDI_MDIX           2
+
+// Define 10Base-T link polarities
+#define TRUEPHY_POLARITY_NORMAL         0
+#define TRUEPHY_POLARITY_INVERTED       1
+
+// Define auto-negotiation results
+#define TRUEPHY_ANEG_NOT_COMPLETE       0
+#define TRUEPHY_ANEG_COMPLETE           1
+#define TRUEPHY_ANEG_DISABLED           2
+
+/* Define duplex advertisment flags */
+#define TRUEPHY_ADV_DUPLEX_NONE         0x00
+#define TRUEPHY_ADV_DUPLEX_FULL         0x01
+#define TRUEPHY_ADV_DUPLEX_HALF         0x02
+#define TRUEPHY_ADV_DUPLEX_BOTH     \
+    (TRUEPHY_ADV_DUPLEX_FULL | TRUEPHY_ADV_DUPLEX_HALF)
+
+#define PHY_CONTROL                0x00	//#define TRU_MI_CONTROL_REGISTER                 0
+#define PHY_STATUS                 0x01	//#define TRU_MI_STATUS_REGISTER                  1
+#define PHY_ID_1                   0x02	//#define TRU_MI_PHY_IDENTIFIER_1_REGISTER        2
+#define PHY_ID_2                   0x03	//#define TRU_MI_PHY_IDENTIFIER_2_REGISTER        3
+#define PHY_AUTO_ADVERTISEMENT     0x04	//#define TRU_MI_ADVERTISEMENT_REGISTER           4
+#define PHY_AUTO_LINK_PARTNER      0x05	//#define TRU_MI_LINK_PARTNER_ABILITY_REGISTER    5
+#define PHY_AUTO_EXPANSION         0x06	//#define TRU_MI_EXPANSION_REGISTER               6
+#define PHY_AUTO_NEXT_PAGE_TX      0x07	//#define TRU_MI_NEXT_PAGE_TRANSMIT_REGISTER      7
+#define PHY_LINK_PARTNER_NEXT_PAGE 0x08	//#define TRU_MI_LINK_PARTNER_NEXT_PAGE_REGISTER  8
+#define PHY_1000_CONTROL           0x09	//#define TRU_MI_1000BASET_CONTROL_REGISTER       9
+#define PHY_1000_STATUS            0x0A	//#define TRU_MI_1000BASET_STATUS_REGISTER        10
+
+#define PHY_EXTENDED_STATUS        0x0F	//#define TRU_MI_EXTENDED_STATUS_REGISTER         15
+
+// some defines for modem registers that seem to be 'reserved'
+#define PHY_INDEX_REG              0x10
+#define PHY_DATA_REG               0x11
+
+#define PHY_MPHY_CONTROL_REG       0x12	//#define TRU_VMI_MPHY_CONTROL_REGISTER           18
+
+#define PHY_LOOPBACK_CONTROL       0x13	//#define TRU_VMI_LOOPBACK_CONTROL_1_REGISTER     19
+					//#define TRU_VMI_LOOPBACK_CONTROL_2_REGISTER     20
+#define PHY_REGISTER_MGMT_CONTROL  0x15	//#define TRU_VMI_MI_SEQ_CONTROL_REGISTER         21
+#define PHY_CONFIG                 0x16	//#define TRU_VMI_CONFIGURATION_REGISTER          22
+#define PHY_PHY_CONTROL            0x17	//#define TRU_VMI_PHY_CONTROL_REGISTER            23
+#define PHY_INTERRUPT_MASK         0x18	//#define TRU_VMI_INTERRUPT_MASK_REGISTER         24
+#define PHY_INTERRUPT_STATUS       0x19	//#define TRU_VMI_INTERRUPT_STATUS_REGISTER       25
+#define PHY_PHY_STATUS             0x1A	//#define TRU_VMI_PHY_STATUS_REGISTER             26
+#define PHY_LED_1                  0x1B	//#define TRU_VMI_LED_CONTROL_1_REGISTER          27
+#define PHY_LED_2                  0x1C	//#define TRU_VMI_LED_CONTROL_2_REGISTER          28
+					//#define TRU_VMI_LINK_CONTROL_REGISTER           29
+					//#define TRU_VMI_TIMING_CONTROL_REGISTER
+
+/* Prototypes for PHY access routines */
+void ET1310_PhyInit(struct et131x_adapter *adapter);
+void ET1310_PhyReset(struct et131x_adapter *adapter);
+void ET1310_PhyPowerDown(struct et131x_adapter *adapter, bool down);
+void ET1310_PhyAutoNeg(struct et131x_adapter *adapter, bool enable);
+void ET1310_PhyDuplexMode(struct et131x_adapter *adapter, u16 duplex);
+void ET1310_PhySpeedSelect(struct et131x_adapter *adapter, u16 speed);
+void ET1310_PhyAdvertise1000BaseT(struct et131x_adapter *adapter,
+				  u16 duplex);
+void ET1310_PhyAdvertise100BaseT(struct et131x_adapter *adapter,
+				 u16 duplex);
+void ET1310_PhyAdvertise10BaseT(struct et131x_adapter *adapter,
+				u16 duplex);
+void ET1310_PhyLinkStatus(struct et131x_adapter *adapter,
+			  u8 *ucLinkStatus,
+			  u32 *uiAutoNeg,
+			  u32 *uiLinkSpeed,
+			  u32 *uiDuplexMode,
+			  u32 *uiMdiMdix,
+			  u32 *uiMasterSlave, u32 *uiPolarity);
+void ET1310_PhyAndOrReg(struct et131x_adapter *adapter,
+			u16 regnum, u16 andMask, u16 orMask);
+void ET1310_PhyAccessMiBit(struct et131x_adapter *adapter,
+			   u16 action,
+			   u16 regnum, u16 bitnum, u8 *value);
+
+#endif /* _ET1310_PHY_H_ */
diff --git a/drivers/staging/et131x/et1310_pm.c b/drivers/staging/et131x/et1310_pm.c
new file mode 100644
index 0000000..9539bc6
--- /dev/null
+++ b/drivers/staging/et131x/et1310_pm.c
@@ -0,0 +1,207 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_pm.c - All power management related code (not completely implemented)
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+#include "et1310_mac.h"
+#include "et1310_rx.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+/**
+ * EnablePhyComa - called when network cable is unplugged
+ * @pAdapter: pointer to our adapter structure
+ *
+ * driver receive an phy status change interrupt while in D0 and check that
+ * phy_status is down.
+ *
+ *          -- gate off JAGCore;
+ *          -- set gigE PHY in Coma mode
+ *          -- wake on phy_interrupt; Perform software reset JAGCore,
+ *             re-initialize jagcore and gigE PHY
+ *
+ *      Add D0-ASPM-PhyLinkDown Support:
+ *          -- while in D0, when there is a phy_interrupt indicating phy link
+ *             down status, call the MPSetPhyComa routine to enter this active
+ *             state power saving mode
+ *          -- while in D0-ASPM-PhyLinkDown mode, when there is a phy_interrupt
+ *       indicating linkup status, call the MPDisablePhyComa routine to
+ *             restore JAGCore and gigE PHY
+ */
+void EnablePhyComa(struct et131x_adapter *pAdapter)
+{
+	unsigned long lockflags;
+	PM_CSR_t GlobalPmCSR;
+	int32_t LoopCounter = 10;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	GlobalPmCSR.value = readl(&pAdapter->CSRAddress->global.pm_csr.value);
+
+	/* Save the GbE PHY speed and duplex modes. Need to restore this
+	 * when cable is plugged back in
+	 */
+	pAdapter->PoMgmt.PowerDownSpeed = pAdapter->AiForceSpeed;
+	pAdapter->PoMgmt.PowerDownDuplex = pAdapter->AiForceDpx;
+
+	/* Stop sending packets. */
+	spin_lock_irqsave(&pAdapter->SendHWLock, lockflags);
+	MP_SET_FLAG(pAdapter, fMP_ADAPTER_LOWER_POWER);
+	spin_unlock_irqrestore(&pAdapter->SendHWLock, lockflags);
+
+	/* Wait for outstanding Receive packets */
+	while ((MP_GET_RCV_REF(pAdapter) != 0) && (LoopCounter-- > 0)) {
+		mdelay(2);
+	}
+
+	/* Gate off JAGCore 3 clock domains */
+	GlobalPmCSR.bits.pm_sysclk_gate = 0;
+	GlobalPmCSR.bits.pm_txclk_gate = 0;
+	GlobalPmCSR.bits.pm_rxclk_gate = 0;
+	writel(GlobalPmCSR.value, &pAdapter->CSRAddress->global.pm_csr.value);
+
+	/* Program gigE PHY in to Coma mode */
+	GlobalPmCSR.bits.pm_phy_sw_coma = 1;
+	writel(GlobalPmCSR.value, &pAdapter->CSRAddress->global.pm_csr.value);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * DisablePhyComa - Disable the Phy Coma Mode
+ * @pAdapter: pointer to our adapter structure
+ */
+void DisablePhyComa(struct et131x_adapter *pAdapter)
+{
+	PM_CSR_t GlobalPmCSR;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	GlobalPmCSR.value = readl(&pAdapter->CSRAddress->global.pm_csr.value);
+
+	/* Disable phy_sw_coma register and re-enable JAGCore clocks */
+	GlobalPmCSR.bits.pm_sysclk_gate = 1;
+	GlobalPmCSR.bits.pm_txclk_gate = 1;
+	GlobalPmCSR.bits.pm_rxclk_gate = 1;
+	GlobalPmCSR.bits.pm_phy_sw_coma = 0;
+	writel(GlobalPmCSR.value, &pAdapter->CSRAddress->global.pm_csr.value);
+
+	/* Restore the GbE PHY speed and duplex modes;
+	 * Reset JAGCore; re-configure and initialize JAGCore and gigE PHY
+	 */
+	pAdapter->AiForceSpeed = pAdapter->PoMgmt.PowerDownSpeed;
+	pAdapter->AiForceDpx = pAdapter->PoMgmt.PowerDownDuplex;
+
+	/* Re-initialize the send structures */
+	et131x_init_send(pAdapter);
+
+	/* Reset the RFD list and re-start RU  */
+	et131x_reset_recv(pAdapter);
+
+	/* Bring the device back to the state it was during init prior to
+         * autonegotiation being complete.  This way, when we get the auto-neg
+         * complete interrupt, we can complete init by calling ConfigMacREGS2.
+         */
+	et131x_soft_reset(pAdapter);
+
+	/* setup et1310 as per the documentation ?? */
+	et131x_adapter_setup(pAdapter);
+
+	/* Allow Tx to restart */
+	MP_CLEAR_FLAG(pAdapter, fMP_ADAPTER_LOWER_POWER);
+
+	/* Need to re-enable Rx. */
+	et131x_rx_dma_enable(pAdapter);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
diff --git a/drivers/staging/et131x/et1310_pm.h b/drivers/staging/et131x/et1310_pm.h
new file mode 100644
index 0000000..6802338
--- /dev/null
+++ b/drivers/staging/et131x/et1310_pm.h
@@ -0,0 +1,125 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_pm.h - Defines, structs, enums, prototypes, etc. pertaining to power
+ *               management.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef _ET1310_PM_H_
+#define _ET1310_PM_H_
+
+#include "et1310_address_map.h"
+
+#define MAX_WOL_PACKET_SIZE    0x80
+#define MAX_WOL_MASK_SIZE      ( MAX_WOL_PACKET_SIZE / 8 )
+#define NUM_WOL_PATTERNS       0x5
+#define CRC16_POLY             0x1021
+
+/* Definition of NDIS_DEVICE_POWER_STATE */
+typedef enum {
+	NdisDeviceStateUnspecified = 0,
+	NdisDeviceStateD0,
+	NdisDeviceStateD1,
+	NdisDeviceStateD2,
+	NdisDeviceStateD3
+} NDIS_DEVICE_POWER_STATE;
+
+typedef struct _MP_POWER_MGMT {
+	/* variable putting the phy into coma mode when boot up with no cable
+	 * plugged in after 5 seconds
+	 */
+	u8 TransPhyComaModeOnBoot;
+
+	/* Array holding the five CRC values that the device is currently
+	 * using for WOL.  This will be queried when a pattern is to be
+	 * removed.
+	 */
+	u32 localWolAndCrc0;
+	u16 WOLPatternList[NUM_WOL_PATTERNS];
+	u8 WOLMaskList[NUM_WOL_PATTERNS][MAX_WOL_MASK_SIZE];
+	u32 WOLMaskSize[NUM_WOL_PATTERNS];
+
+	/* IP address */
+	union {
+		u32 u32;
+		u8 u8[4];
+	} IPAddress;
+
+	/* Current Power state of the adapter. */
+	NDIS_DEVICE_POWER_STATE PowerState;
+	bool WOLState;
+	bool WOLEnabled;
+	bool Failed10Half;
+	bool bFailedStateTransition;
+
+	/* Next two used to save power information at power down. This
+	 * information will be used during power up to set up parts of Power
+	 * Management in JAGCore
+	 */
+	u32 tx_en;
+	u32 rx_en;
+	u16 PowerDownSpeed;
+	u8 PowerDownDuplex;
+} MP_POWER_MGMT, *PMP_POWER_MGMT;
+
+/* Forward declaration of the private adapter structure
+ * ( IS THERE A WAY TO DO THIS WITH A TYPEDEF??? )
+ */
+struct et131x_adapter;
+
+u16 CalculateCCITCRC16(u8 *Pattern, u8 *Mask, u32 MaskSize);
+void EnablePhyComa(struct et131x_adapter *adapter);
+void DisablePhyComa(struct et131x_adapter *adapter);
+
+#endif /* _ET1310_PM_H_ */
diff --git a/drivers/staging/et131x/et1310_rx.c b/drivers/staging/et131x/et1310_rx.c
new file mode 100644
index 0000000..ec98da5
--- /dev/null
+++ b/drivers/staging/et131x/et1310_rx.c
@@ -0,0 +1,1391 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_rx.c - Routines used to perform data reception
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+
+#include "et1310_rx.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+
+void nic_return_rfd(struct et131x_adapter *pAdapter, PMP_RFD pMpRfd);
+
+/**
+ * et131x_rx_dma_memory_alloc
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success and errno on failure (as defined in errno.h)
+ *
+ * Allocates Free buffer ring 1 for sure, free buffer ring 0 if required,
+ * and the Packet Status Ring.
+ */
+int et131x_rx_dma_memory_alloc(struct et131x_adapter *adapter)
+{
+	uint32_t OuterLoop, InnerLoop;
+	uint32_t bufsize;
+	uint32_t pktStatRingSize, FBRChunkSize;
+	RX_RING_t *rx_ring;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Setup some convenience pointers */
+	rx_ring = (RX_RING_t *) & adapter->RxRing;
+
+	/* Alloc memory for the lookup table */
+#ifdef USE_FBR0
+	rx_ring->Fbr[0] = kmalloc(sizeof(FBRLOOKUPTABLE), GFP_KERNEL);
+#endif
+
+	rx_ring->Fbr[1] = kmalloc(sizeof(FBRLOOKUPTABLE), GFP_KERNEL);
+
+	/* The first thing we will do is configure the sizes of the buffer
+	 * rings. These will change based on jumbo packet support.  Larger
+	 * jumbo packets increases the size of each entry in FBR0, and the
+	 * number of entries in FBR0, while at the same time decreasing the
+	 * number of entries in FBR1.
+	 *
+	 * FBR1 holds "large" frames, FBR0 holds "small" frames.  If FBR1
+	 * entries are huge in order to accomodate a "jumbo" frame, then it
+	 * will have less entries.  Conversely, FBR1 will now be relied upon
+	 * to carry more "normal" frames, thus it's entry size also increases
+	 * and the number of entries goes up too (since it now carries
+	 * "small" + "regular" packets.
+	 *
+	 * In this scheme, we try to maintain 512 entries between the two
+	 * rings. Also, FBR1 remains a constant size - when it's size doubles
+	 * the number of entries halves.  FBR0 increases in size, however.
+	 */
+
+	if (adapter->RegistryJumboPacket < 2048) {
+#ifdef USE_FBR0
+		rx_ring->Fbr0BufferSize = 256;
+		rx_ring->Fbr0NumEntries = 512;
+#endif
+		rx_ring->Fbr1BufferSize = 2048;
+		rx_ring->Fbr1NumEntries = 512;
+	} else if (adapter->RegistryJumboPacket < 4096) {
+#ifdef USE_FBR0
+		rx_ring->Fbr0BufferSize = 512;
+		rx_ring->Fbr0NumEntries = 1024;
+#endif
+		rx_ring->Fbr1BufferSize = 4096;
+		rx_ring->Fbr1NumEntries = 512;
+	} else {
+#ifdef USE_FBR0
+		rx_ring->Fbr0BufferSize = 1024;
+		rx_ring->Fbr0NumEntries = 768;
+#endif
+		rx_ring->Fbr1BufferSize = 16384;
+		rx_ring->Fbr1NumEntries = 128;
+	}
+
+#ifdef USE_FBR0
+	adapter->RxRing.PsrNumEntries = adapter->RxRing.Fbr0NumEntries +
+	    adapter->RxRing.Fbr1NumEntries;
+#else
+	adapter->RxRing.PsrNumEntries = adapter->RxRing.Fbr1NumEntries;
+#endif
+
+	/* Allocate an area of memory for Free Buffer Ring 1 */
+	bufsize = (sizeof(FBR_DESC_t) * rx_ring->Fbr1NumEntries) + 0xfff;
+	rx_ring->pFbr1RingVa = pci_alloc_consistent(adapter->pdev,
+						    bufsize,
+						    &rx_ring->pFbr1RingPa);
+	if (!rx_ring->pFbr1RingVa) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot alloc memory for Free Buffer Ring 1\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Save physical address
+	 *
+	 * NOTE: pci_alloc_consistent(), used above to alloc DMA regions,
+	 * ALWAYS returns SAC (32-bit) addresses. If DAC (64-bit) addresses
+	 * are ever returned, make sure the high part is retrieved here
+	 * before storing the adjusted address.
+	 */
+	rx_ring->Fbr1Realpa = rx_ring->pFbr1RingPa;
+
+	/* Align Free Buffer Ring 1 on a 4K boundary */
+	et131x_align_allocated_memory(adapter,
+				      &rx_ring->Fbr1Realpa,
+				      &rx_ring->Fbr1offset, 0x0FFF);
+
+	rx_ring->pFbr1RingVa = (void *)((uint8_t *) rx_ring->pFbr1RingVa +
+					rx_ring->Fbr1offset);
+
+#ifdef USE_FBR0
+	/* Allocate an area of memory for Free Buffer Ring 0 */
+	bufsize = (sizeof(FBR_DESC_t) * rx_ring->Fbr0NumEntries) + 0xfff;
+	rx_ring->pFbr0RingVa = pci_alloc_consistent(adapter->pdev,
+						    bufsize,
+						    &rx_ring->pFbr0RingPa);
+	if (!rx_ring->pFbr0RingVa) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot alloc memory for Free Buffer Ring 0\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Save physical address
+	 *
+	 * NOTE: pci_alloc_consistent(), used above to alloc DMA regions,
+	 * ALWAYS returns SAC (32-bit) addresses. If DAC (64-bit) addresses
+	 * are ever returned, make sure the high part is retrieved here before
+	 * storing the adjusted address.
+	 */
+	rx_ring->Fbr0Realpa = rx_ring->pFbr0RingPa;
+
+	/* Align Free Buffer Ring 0 on a 4K boundary */
+	et131x_align_allocated_memory(adapter,
+				      &rx_ring->Fbr0Realpa,
+				      &rx_ring->Fbr0offset, 0x0FFF);
+
+	rx_ring->pFbr0RingVa = (void *)((uint8_t *) rx_ring->pFbr0RingVa +
+					rx_ring->Fbr0offset);
+#endif
+
+	for (OuterLoop = 0; OuterLoop < (rx_ring->Fbr1NumEntries / FBR_CHUNKS);
+	     OuterLoop++) {
+		uint64_t Fbr1Offset;
+		uint64_t Fbr1TempPa;
+		uint32_t Fbr1Align;
+
+		/* This code allocates an area of memory big enough for N
+		 * free buffers + (buffer_size - 1) so that the buffers can
+		 * be aligned on 4k boundaries.  If each buffer were aligned
+		 * to a buffer_size boundary, the effect would be to double
+		 * the size of FBR0.  By allocating N buffers at once, we
+		 * reduce this overhead.
+		 */
+		if (rx_ring->Fbr1BufferSize > 4096) {
+			Fbr1Align = 4096;
+		} else {
+			Fbr1Align = rx_ring->Fbr1BufferSize;
+		}
+
+		FBRChunkSize =
+		    (FBR_CHUNKS * rx_ring->Fbr1BufferSize) + Fbr1Align - 1;
+		rx_ring->Fbr1MemVa[OuterLoop] =
+		    pci_alloc_consistent(adapter->pdev, FBRChunkSize,
+					 &rx_ring->Fbr1MemPa[OuterLoop]);
+
+		if (!rx_ring->Fbr1MemVa[OuterLoop]) {
+			DBG_ERROR(et131x_dbginfo, "Could not alloc memory\n");
+			DBG_LEAVE(et131x_dbginfo);
+			return -ENOMEM;
+		}
+
+		/* See NOTE in "Save Physical Address" comment above */
+		Fbr1TempPa = rx_ring->Fbr1MemPa[OuterLoop];
+
+		et131x_align_allocated_memory(adapter,
+					      &Fbr1TempPa,
+					      &Fbr1Offset, (Fbr1Align - 1));
+
+		for (InnerLoop = 0; InnerLoop < FBR_CHUNKS; InnerLoop++) {
+			uint32_t index = (OuterLoop * FBR_CHUNKS) + InnerLoop;
+
+			/* Save the Virtual address of this index for quick
+			 * access later
+			 */
+			rx_ring->Fbr[1]->Va[index] =
+			    (uint8_t *) rx_ring->Fbr1MemVa[OuterLoop] +
+			    (InnerLoop * rx_ring->Fbr1BufferSize) + Fbr1Offset;
+
+			/* now store the physical address in the descriptor
+			 * so the device can access it
+			 */
+			rx_ring->Fbr[1]->PAHigh[index] =
+			    (uint32_t) (Fbr1TempPa >> 32);
+			rx_ring->Fbr[1]->PALow[index] = (uint32_t) Fbr1TempPa;
+
+			Fbr1TempPa += rx_ring->Fbr1BufferSize;
+
+			rx_ring->Fbr[1]->Buffer1[index] =
+			    rx_ring->Fbr[1]->Va[index];
+			rx_ring->Fbr[1]->Buffer2[index] =
+			    rx_ring->Fbr[1]->Va[index] - 4;
+		}
+	}
+
+#ifdef USE_FBR0
+	/* Same for FBR0 (if in use) */
+	for (OuterLoop = 0; OuterLoop < (rx_ring->Fbr0NumEntries / FBR_CHUNKS);
+	     OuterLoop++) {
+		uint64_t Fbr0Offset;
+		uint64_t Fbr0TempPa;
+
+		FBRChunkSize = ((FBR_CHUNKS + 1) * rx_ring->Fbr0BufferSize) - 1;
+		rx_ring->Fbr0MemVa[OuterLoop] =
+		    pci_alloc_consistent(adapter->pdev, FBRChunkSize,
+					 &rx_ring->Fbr0MemPa[OuterLoop]);
+
+		if (!rx_ring->Fbr0MemVa[OuterLoop]) {
+			DBG_ERROR(et131x_dbginfo, "Could not alloc memory\n");
+			DBG_LEAVE(et131x_dbginfo);
+			return -ENOMEM;
+		}
+
+		/* See NOTE in "Save Physical Address" comment above */
+		Fbr0TempPa = rx_ring->Fbr0MemPa[OuterLoop];
+
+		et131x_align_allocated_memory(adapter,
+					      &Fbr0TempPa,
+					      &Fbr0Offset,
+					      rx_ring->Fbr0BufferSize - 1);
+
+		for (InnerLoop = 0; InnerLoop < FBR_CHUNKS; InnerLoop++) {
+			uint32_t index = (OuterLoop * FBR_CHUNKS) + InnerLoop;
+
+			rx_ring->Fbr[0]->Va[index] =
+			    (uint8_t *) rx_ring->Fbr0MemVa[OuterLoop] +
+			    (InnerLoop * rx_ring->Fbr0BufferSize) + Fbr0Offset;
+
+			rx_ring->Fbr[0]->PAHigh[index] =
+			    (uint32_t) (Fbr0TempPa >> 32);
+			rx_ring->Fbr[0]->PALow[index] = (uint32_t) Fbr0TempPa;
+
+			Fbr0TempPa += rx_ring->Fbr0BufferSize;
+
+			rx_ring->Fbr[0]->Buffer1[index] =
+			    rx_ring->Fbr[0]->Va[index];
+			rx_ring->Fbr[0]->Buffer2[index] =
+			    rx_ring->Fbr[0]->Va[index] - 4;
+		}
+	}
+#endif
+
+	/* Allocate an area of memory for FIFO of Packet Status ring entries */
+	pktStatRingSize =
+	    sizeof(PKT_STAT_DESC_t) * adapter->RxRing.PsrNumEntries;
+
+	rx_ring->pPSRingVa = pci_alloc_consistent(adapter->pdev,
+						  pktStatRingSize + 0x0fff,
+						  &rx_ring->pPSRingPa);
+
+	if (!rx_ring->pPSRingVa) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot alloc memory for Packet Status Ring\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Save physical address
+	 *
+	 * NOTE : pci_alloc_consistent(), used above to alloc DMA regions,
+	 * ALWAYS returns SAC (32-bit) addresses. If DAC (64-bit) addresses
+	 * are ever returned, make sure the high part is retrieved here before
+	 * storing the adjusted address.
+	 */
+	rx_ring->pPSRingRealPa = rx_ring->pPSRingPa;
+
+	/* Align Packet Status Ring on a 4K boundary */
+	et131x_align_allocated_memory(adapter,
+				      &rx_ring->pPSRingRealPa,
+				      &rx_ring->pPSRingOffset, 0x0FFF);
+
+	rx_ring->pPSRingVa = (void *)((uint8_t *) rx_ring->pPSRingVa +
+				      rx_ring->pPSRingOffset);
+
+	/* Allocate an area of memory for writeback of status information */
+	rx_ring->pRxStatusVa = pci_alloc_consistent(adapter->pdev,
+						    sizeof(RX_STATUS_BLOCK_t) +
+						    0x7, &rx_ring->pRxStatusPa);
+	if (!rx_ring->pRxStatusVa) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot alloc memory for Status Block\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Save physical address */
+	rx_ring->RxStatusRealPA = rx_ring->pRxStatusPa;
+
+	/* Align write back on an 8 byte boundary */
+	et131x_align_allocated_memory(adapter,
+				      &rx_ring->RxStatusRealPA,
+				      &rx_ring->RxStatusOffset, 0x07);
+
+	rx_ring->pRxStatusVa = (void *)((uint8_t *) rx_ring->pRxStatusVa +
+					rx_ring->RxStatusOffset);
+	rx_ring->NumRfd = NIC_DEFAULT_NUM_RFD;
+
+	/* Recv
+	 * pci_pool_create initializes a lookaside list. After successful
+	 * creation, nonpaged fixed-size blocks can be allocated from and
+	 * freed to the lookaside list.
+	 * RFDs will be allocated from this pool.
+	 */
+	rx_ring->RecvLookaside = kmem_cache_create(adapter->netdev->name,
+						   sizeof(MP_RFD),
+						   0,
+						   SLAB_CACHE_DMA |
+						   SLAB_HWCACHE_ALIGN,
+						   NULL);
+
+	MP_SET_FLAG(adapter, fMP_ADAPTER_RECV_LOOKASIDE);
+
+	/* The RFDs are going to be put on lists later on, so initialize the
+	 * lists now.
+	 */
+	INIT_LIST_HEAD(&rx_ring->RecvList);
+	INIT_LIST_HEAD(&rx_ring->RecvPendingList);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+/**
+ * et131x_rx_dma_memory_free - Free all memory allocated within this module.
+ * @adapter: pointer to our private adapter structure
+ */
+void et131x_rx_dma_memory_free(struct et131x_adapter *adapter)
+{
+	uint32_t index;
+	uint32_t bufsize;
+	uint32_t pktStatRingSize;
+	PMP_RFD pMpRfd;
+	RX_RING_t *rx_ring;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Setup some convenience pointers */
+	rx_ring = (RX_RING_t *) & adapter->RxRing;
+
+	/* Free RFDs and associated packet descriptors */
+	DBG_ASSERT(rx_ring->nReadyRecv == rx_ring->NumRfd);
+
+	while (!list_empty(&rx_ring->RecvList)) {
+		pMpRfd = (MP_RFD *) list_entry(rx_ring->RecvList.next,
+					       MP_RFD, list_node);
+
+		list_del(&pMpRfd->list_node);
+		et131x_rfd_resources_free(adapter, pMpRfd);
+	}
+
+	while (!list_empty(&rx_ring->RecvPendingList)) {
+		pMpRfd = (MP_RFD *) list_entry(rx_ring->RecvPendingList.next,
+					       MP_RFD, list_node);
+		list_del(&pMpRfd->list_node);
+		et131x_rfd_resources_free(adapter, pMpRfd);
+	}
+
+	/* Free Free Buffer Ring 1 */
+	if (rx_ring->pFbr1RingVa) {
+		/* First the packet memory */
+		for (index = 0; index <
+		     (rx_ring->Fbr1NumEntries / FBR_CHUNKS); index++) {
+			if (rx_ring->Fbr1MemVa[index]) {
+				uint32_t Fbr1Align;
+
+				if (rx_ring->Fbr1BufferSize > 4096) {
+					Fbr1Align = 4096;
+				} else {
+					Fbr1Align = rx_ring->Fbr1BufferSize;
+				}
+
+				bufsize =
+				    (rx_ring->Fbr1BufferSize * FBR_CHUNKS) +
+				    Fbr1Align - 1;
+
+				pci_free_consistent(adapter->pdev,
+						    bufsize,
+						    rx_ring->Fbr1MemVa[index],
+						    rx_ring->Fbr1MemPa[index]);
+
+				rx_ring->Fbr1MemVa[index] = NULL;
+			}
+		}
+
+		/* Now the FIFO itself */
+		rx_ring->pFbr1RingVa = (void *)((uint8_t *) rx_ring->pFbr1RingVa -
+						rx_ring->Fbr1offset);
+
+		bufsize =
+		    (sizeof(FBR_DESC_t) * rx_ring->Fbr1NumEntries) + 0xfff;
+
+		pci_free_consistent(adapter->pdev,
+				    bufsize,
+				    rx_ring->pFbr1RingVa, rx_ring->pFbr1RingPa);
+
+		rx_ring->pFbr1RingVa = NULL;
+	}
+
+#ifdef USE_FBR0
+	/* Now the same for Free Buffer Ring 0 */
+	if (rx_ring->pFbr0RingVa) {
+		/* First the packet memory */
+		for (index = 0; index <
+		     (rx_ring->Fbr0NumEntries / FBR_CHUNKS); index++) {
+			if (rx_ring->Fbr0MemVa[index]) {
+				bufsize =
+				    (rx_ring->Fbr0BufferSize *
+				     (FBR_CHUNKS + 1)) - 1;
+
+				pci_free_consistent(adapter->pdev,
+						    bufsize,
+						    rx_ring->Fbr0MemVa[index],
+						    rx_ring->Fbr0MemPa[index]);
+
+				rx_ring->Fbr0MemVa[index] = NULL;
+			}
+		}
+
+		/* Now the FIFO itself */
+		rx_ring->pFbr0RingVa = (void *)((uint8_t *) rx_ring->pFbr0RingVa -
+						rx_ring->Fbr0offset);
+
+		bufsize =
+		    (sizeof(FBR_DESC_t) * rx_ring->Fbr0NumEntries) + 0xfff;
+
+		pci_free_consistent(adapter->pdev,
+				    bufsize,
+				    rx_ring->pFbr0RingVa, rx_ring->pFbr0RingPa);
+
+		rx_ring->pFbr0RingVa = NULL;
+	}
+#endif
+
+	/* Free Packet Status Ring */
+	if (rx_ring->pPSRingVa) {
+		rx_ring->pPSRingVa = (void *)((uint8_t *) rx_ring->pPSRingVa -
+					      rx_ring->pPSRingOffset);
+
+		pktStatRingSize =
+		    sizeof(PKT_STAT_DESC_t) * adapter->RxRing.PsrNumEntries;
+
+		pci_free_consistent(adapter->pdev,
+				    pktStatRingSize + 0x0fff,
+				    rx_ring->pPSRingVa, rx_ring->pPSRingPa);
+
+		rx_ring->pPSRingVa = NULL;
+	}
+
+	/* Free area of memory for the writeback of status information */
+	if (rx_ring->pRxStatusVa) {
+		rx_ring->pRxStatusVa = (void *)((uint8_t *) rx_ring->pRxStatusVa -
+						rx_ring->RxStatusOffset);
+
+		pci_free_consistent(adapter->pdev,
+				    sizeof(RX_STATUS_BLOCK_t) + 0x7,
+				    rx_ring->pRxStatusVa, rx_ring->pRxStatusPa);
+
+		rx_ring->pRxStatusVa = NULL;
+	}
+
+	/* Free receive buffer pool */
+
+	/* Free receive packet pool */
+
+	/* Destroy the lookaside (RFD) pool */
+	if (MP_TEST_FLAG(adapter, fMP_ADAPTER_RECV_LOOKASIDE)) {
+		kmem_cache_destroy(rx_ring->RecvLookaside);
+		MP_CLEAR_FLAG(adapter, fMP_ADAPTER_RECV_LOOKASIDE);
+	}
+
+	/* Free the FBR Lookup Table */
+#ifdef USE_FBR0
+	kfree(rx_ring->Fbr[0]);
+#endif
+
+	kfree(rx_ring->Fbr[1]);
+
+	/* Reset Counters */
+	rx_ring->nReadyRecv = 0;
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_init_recv - Initialize receive data structures.
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success and errno on failure (as defined in errno.h)
+ */
+int et131x_init_recv(struct et131x_adapter *adapter)
+{
+	int status = -ENOMEM;
+	PMP_RFD pMpRfd = NULL;
+	uint32_t RfdCount;
+	uint32_t TotalNumRfd = 0;
+	RX_RING_t *rx_ring = NULL;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Setup some convenience pointers */
+	rx_ring = (RX_RING_t *) & adapter->RxRing;
+
+	/* Setup each RFD */
+	for (RfdCount = 0; RfdCount < rx_ring->NumRfd; RfdCount++) {
+		pMpRfd = (MP_RFD *) kmem_cache_alloc(rx_ring->RecvLookaside,
+						     GFP_ATOMIC | GFP_DMA);
+
+		if (!pMpRfd) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Couldn't alloc RFD out of kmem_cache\n");
+			status = -ENOMEM;
+			continue;
+		}
+
+		status = et131x_rfd_resources_alloc(adapter, pMpRfd);
+		if (status != 0) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Couldn't alloc packet for RFD\n");
+			kmem_cache_free(rx_ring->RecvLookaside, pMpRfd);
+			continue;
+		}
+
+		/* Add this RFD to the RecvList */
+		list_add_tail(&pMpRfd->list_node, &rx_ring->RecvList);
+
+		/* Increment both the available RFD's, and the total RFD's. */
+		rx_ring->nReadyRecv++;
+		TotalNumRfd++;
+	}
+
+	if (TotalNumRfd > NIC_MIN_NUM_RFD) {
+		status = 0;
+	}
+
+	rx_ring->NumRfd = TotalNumRfd;
+
+	if (status != 0) {
+		kmem_cache_free(rx_ring->RecvLookaside, pMpRfd);
+		DBG_ERROR(et131x_dbginfo,
+			  "Allocation problems in et131x_init_recv\n");
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_rfd_resources_alloc
+ * @adapter: pointer to our private adapter structure
+ * @pMpRfd: pointer to a RFD
+ *
+ * Returns 0 on success and errno on failure (as defined in errno.h)
+ */
+int et131x_rfd_resources_alloc(struct et131x_adapter *adapter, MP_RFD *pMpRfd)
+{
+	pMpRfd->Packet = NULL;
+
+	return 0;
+}
+
+/**
+ * et131x_rfd_resources_free - Free the packet allocated for the given RFD
+ * @adapter: pointer to our private adapter structure
+ * @pMpRfd: pointer to a RFD
+ */
+void et131x_rfd_resources_free(struct et131x_adapter *adapter, MP_RFD *pMpRfd)
+{
+	pMpRfd->Packet = NULL;
+	kmem_cache_free(adapter->RxRing.RecvLookaside, pMpRfd);
+}
+
+/**
+ * ConfigRxDmaRegs - Start of Rx_DMA init sequence
+ * @pAdapter: pointer to our adapter structure
+ */
+void ConfigRxDmaRegs(struct et131x_adapter *pAdapter)
+{
+	struct _RXDMA_t __iomem *pRxDma = &pAdapter->CSRAddress->rxdma;
+	struct _rx_ring_t *pRxLocal = &pAdapter->RxRing;
+	PFBR_DESC_t pFbrEntry;
+	uint32_t iEntry;
+	RXDMA_PSR_NUM_DES_t psr_num_des;
+	unsigned long lockflags;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Halt RXDMA to perform the reconfigure.  */
+	et131x_rx_dma_disable(pAdapter);
+
+	/* Load the completion writeback physical address
+	 *
+	 * NOTE : pci_alloc_consistent(), used above to alloc DMA regions,
+	 * ALWAYS returns SAC (32-bit) addresses. If DAC (64-bit) addresses
+	 * are ever returned, make sure the high part is retrieved here
+	 * before storing the adjusted address.
+	 */
+	writel((uint32_t) (pRxLocal->RxStatusRealPA >> 32),
+	       &pRxDma->dma_wb_base_hi);
+	writel((uint32_t) pRxLocal->RxStatusRealPA, &pRxDma->dma_wb_base_lo);
+
+	memset(pRxLocal->pRxStatusVa, 0, sizeof(RX_STATUS_BLOCK_t));
+
+	/* Set the address and parameters of the packet status ring into the
+	 * 1310's registers
+	 */
+	writel((uint32_t) (pRxLocal->pPSRingRealPa >> 32),
+	       &pRxDma->psr_base_hi);
+	writel((uint32_t) pRxLocal->pPSRingRealPa, &pRxDma->psr_base_lo);
+	writel(pRxLocal->PsrNumEntries - 1, &pRxDma->psr_num_des.value);
+	writel(0, &pRxDma->psr_full_offset.value);
+
+	psr_num_des.value = readl(&pRxDma->psr_num_des.value);
+	writel((psr_num_des.bits.psr_ndes * LO_MARK_PERCENT_FOR_PSR) / 100,
+	       &pRxDma->psr_min_des.value);
+
+	spin_lock_irqsave(&pAdapter->RcvLock, lockflags);
+
+	/* These local variables track the PSR in the adapter structure */
+	pRxLocal->local_psr_full.bits.psr_full = 0;
+	pRxLocal->local_psr_full.bits.psr_full_wrap = 0;
+
+	/* Now's the best time to initialize FBR1 contents */
+	pFbrEntry = (PFBR_DESC_t) pRxLocal->pFbr1RingVa;
+	for (iEntry = 0; iEntry < pRxLocal->Fbr1NumEntries; iEntry++) {
+		pFbrEntry->addr_hi = pRxLocal->Fbr[1]->PAHigh[iEntry];
+		pFbrEntry->addr_lo = pRxLocal->Fbr[1]->PALow[iEntry];
+		pFbrEntry->word2.bits.bi = iEntry;
+		pFbrEntry++;
+	}
+
+	/* Set the address and parameters of Free buffer ring 1 (and 0 if
+	 * required) into the 1310's registers
+	 */
+	writel((uint32_t) (pRxLocal->Fbr1Realpa >> 32), &pRxDma->fbr1_base_hi);
+	writel((uint32_t) pRxLocal->Fbr1Realpa, &pRxDma->fbr1_base_lo);
+	writel(pRxLocal->Fbr1NumEntries - 1, &pRxDma->fbr1_num_des.value);
+
+	{
+		DMA10W_t fbr1_full = { 0 };
+
+		fbr1_full.bits.val = 0;
+		fbr1_full.bits.wrap = 1;
+		writel(fbr1_full.value, &pRxDma->fbr1_full_offset.value);
+	}
+
+	/* This variable tracks the free buffer ring 1 full position, so it
+	 * has to match the above.
+	 */
+	pRxLocal->local_Fbr1_full.bits.val = 0;
+	pRxLocal->local_Fbr1_full.bits.wrap = 1;
+	writel(((pRxLocal->Fbr1NumEntries * LO_MARK_PERCENT_FOR_RX) / 100) - 1,
+	       &pRxDma->fbr1_min_des.value);
+
+#ifdef USE_FBR0
+	/* Now's the best time to initialize FBR0 contents */
+	pFbrEntry = (PFBR_DESC_t) pRxLocal->pFbr0RingVa;
+	for (iEntry = 0; iEntry < pRxLocal->Fbr0NumEntries; iEntry++) {
+		pFbrEntry->addr_hi = pRxLocal->Fbr[0]->PAHigh[iEntry];
+		pFbrEntry->addr_lo = pRxLocal->Fbr[0]->PALow[iEntry];
+		pFbrEntry->word2.bits.bi = iEntry;
+		pFbrEntry++;
+	}
+
+	writel((uint32_t) (pRxLocal->Fbr0Realpa >> 32), &pRxDma->fbr0_base_hi);
+	writel((uint32_t) pRxLocal->Fbr0Realpa, &pRxDma->fbr0_base_lo);
+	writel(pRxLocal->Fbr0NumEntries - 1, &pRxDma->fbr0_num_des.value);
+
+	{
+		DMA10W_t fbr0_full = { 0 };
+
+		fbr0_full.bits.val = 0;
+		fbr0_full.bits.wrap = 1;
+		writel(fbr0_full.value, &pRxDma->fbr0_full_offset.value);
+	}
+
+	/* This variable tracks the free buffer ring 0 full position, so it
+	 * has to match the above.
+	 */
+	pRxLocal->local_Fbr0_full.bits.val = 0;
+	pRxLocal->local_Fbr0_full.bits.wrap = 1;
+	writel(((pRxLocal->Fbr0NumEntries * LO_MARK_PERCENT_FOR_RX) / 100) - 1,
+	       &pRxDma->fbr0_min_des.value);
+#endif
+
+	/* Program the number of packets we will receive before generating an
+	 * interrupt.
+	 * For version B silicon, this value gets updated once autoneg is
+	 *complete.
+	 */
+	writel(pAdapter->RegistryRxNumBuffers, &pRxDma->num_pkt_done.value);
+
+	/* The "time_done" is not working correctly to coalesce interrupts
+	 * after a given time period, but rather is giving us an interrupt
+	 * regardless of whether we have received packets.
+	 * This value gets updated once autoneg is complete.
+	 */
+	writel(pAdapter->RegistryRxTimeInterval, &pRxDma->max_pkt_time.value);
+
+	spin_unlock_irqrestore(&pAdapter->RcvLock, lockflags);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * SetRxDmaTimer - Set the heartbeat timer according to line rate.
+ * @pAdapter: pointer to our adapter structure
+ */
+void SetRxDmaTimer(struct et131x_adapter *pAdapter)
+{
+	/* For version B silicon, we do not use the RxDMA timer for 10 and 100
+	 * Mbits/s line rates. We do not enable and RxDMA interrupt coalescing.
+	 */
+	if ((pAdapter->uiLinkSpeed == TRUEPHY_SPEED_100MBPS) ||
+	    (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_10MBPS)) {
+		writel(0, &pAdapter->CSRAddress->rxdma.max_pkt_time.value);
+		writel(1, &pAdapter->CSRAddress->rxdma.num_pkt_done.value);
+	}
+}
+
+/**
+ * et131x_rx_dma_disable - Stop of Rx_DMA on the ET1310
+ * @pAdapter: pointer to our adapter structure
+ */
+void et131x_rx_dma_disable(struct et131x_adapter *pAdapter)
+{
+	RXDMA_CSR_t csr;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Setup the receive dma configuration register */
+	writel(0x00002001, &pAdapter->CSRAddress->rxdma.csr.value);
+	csr.value = readl(&pAdapter->CSRAddress->rxdma.csr.value);
+	if (csr.bits.halt_status != 1) {
+		udelay(5);
+		csr.value = readl(&pAdapter->CSRAddress->rxdma.csr.value);
+		if (csr.bits.halt_status != 1) {
+			DBG_ERROR(et131x_dbginfo,
+				  "RX Dma failed to enter halt state. CSR 0x%08x\n",
+				  csr.value);
+		}
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_rx_dma_enable - re-start of Rx_DMA on the ET1310.
+ * @pAdapter: pointer to our adapter structure
+ */
+void et131x_rx_dma_enable(struct et131x_adapter *pAdapter)
+{
+	DBG_RX_ENTER(et131x_dbginfo);
+
+	if (pAdapter->RegistryPhyLoopbk) {
+	/* RxDMA is disabled for loopback operation. */
+		writel(0x1, &pAdapter->CSRAddress->rxdma.csr.value);
+	} else {
+	/* Setup the receive dma configuration register for normal operation */
+		RXDMA_CSR_t csr = { 0 };
+
+		csr.bits.fbr1_enable = 1;
+		if (pAdapter->RxRing.Fbr1BufferSize == 4096) {
+			csr.bits.fbr1_size = 1;
+		} else if (pAdapter->RxRing.Fbr1BufferSize == 8192) {
+			csr.bits.fbr1_size = 2;
+		} else if (pAdapter->RxRing.Fbr1BufferSize == 16384) {
+			csr.bits.fbr1_size = 3;
+		}
+#ifdef USE_FBR0
+		csr.bits.fbr0_enable = 1;
+		if (pAdapter->RxRing.Fbr0BufferSize == 256) {
+			csr.bits.fbr0_size = 1;
+		} else if (pAdapter->RxRing.Fbr0BufferSize == 512) {
+			csr.bits.fbr0_size = 2;
+		} else if (pAdapter->RxRing.Fbr0BufferSize == 1024) {
+			csr.bits.fbr0_size = 3;
+		}
+#endif
+		writel(csr.value, &pAdapter->CSRAddress->rxdma.csr.value);
+
+		csr.value = readl(&pAdapter->CSRAddress->rxdma.csr.value);
+		if (csr.bits.halt_status != 0) {
+			udelay(5);
+			csr.value = readl(&pAdapter->CSRAddress->rxdma.csr.value);
+			if (csr.bits.halt_status != 0) {
+				DBG_ERROR(et131x_dbginfo,
+					  "RX Dma failed to exit halt state.  CSR 0x%08x\n",
+					  csr.value);
+			}
+		}
+	}
+
+	DBG_RX_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * nic_rx_pkts - Checks the hardware for available packets
+ * @pAdapter: pointer to our adapter
+ *
+ * Returns pMpRfd, a pointer to our MPRFD.
+ *
+ * Checks the hardware for available packets, using completion ring
+ * If packets are available, it gets an RFD from the RecvList, attaches
+ * the packet to it, puts the RFD in the RecvPendList, and also returns
+ * the pointer to the RFD.
+ */
+PMP_RFD nic_rx_pkts(struct et131x_adapter *pAdapter)
+{
+	struct _rx_ring_t *pRxLocal = &pAdapter->RxRing;
+	PRX_STATUS_BLOCK_t pRxStatusBlock;
+	PPKT_STAT_DESC_t pPSREntry;
+	PMP_RFD pMpRfd;
+	uint32_t nIndex;
+	uint8_t *pBufVa;
+	unsigned long lockflags;
+	struct list_head *element;
+	uint8_t ringIndex;
+	uint16_t bufferIndex;
+	uint32_t localLen;
+	PKT_STAT_DESC_WORD0_t Word0;
+
+
+	DBG_RX_ENTER(et131x_dbginfo);
+
+	/* RX Status block is written by the DMA engine prior to every
+	 * interrupt. It contains the next to be used entry in the Packet
+	 * Status Ring, and also the two Free Buffer rings.
+	 */
+	pRxStatusBlock = (PRX_STATUS_BLOCK_t) pRxLocal->pRxStatusVa;
+
+	if (pRxStatusBlock->Word1.bits.PSRoffset ==
+			pRxLocal->local_psr_full.bits.psr_full &&
+	    pRxStatusBlock->Word1.bits.PSRwrap ==
+	    		pRxLocal->local_psr_full.bits.psr_full_wrap) {
+		/* Looks like this ring is not updated yet */
+		DBG_RX(et131x_dbginfo, "(0)\n");
+		DBG_RX_LEAVE(et131x_dbginfo);
+		return NULL;
+	}
+
+	/* The packet status ring indicates that data is available. */
+	pPSREntry = (PPKT_STAT_DESC_t) (pRxLocal->pPSRingVa) +
+			pRxLocal->local_psr_full.bits.psr_full;
+
+	/* Grab any information that is required once the PSR is
+	 * advanced, since we can no longer rely on the memory being
+	 * accurate
+	 */
+	localLen = pPSREntry->word1.bits.length;
+	ringIndex = (uint8_t) pPSREntry->word1.bits.ri;
+	bufferIndex = (uint16_t) pPSREntry->word1.bits.bi;
+	Word0 = pPSREntry->word0;
+
+	DBG_RX(et131x_dbginfo, "RX PACKET STATUS\n");
+	DBG_RX(et131x_dbginfo, "\tlength      : %d\n", localLen);
+	DBG_RX(et131x_dbginfo, "\tringIndex   : %d\n", ringIndex);
+	DBG_RX(et131x_dbginfo, "\tbufferIndex : %d\n", bufferIndex);
+	DBG_RX(et131x_dbginfo, "\tword0       : 0x%08x\n", Word0.value);
+
+#if 0
+	/* Check the Status Word that the MAC has appended to the PSR
+	 * entry in case the MAC has detected errors.
+	 */
+	if (Word0.value & ALCATEL_BAD_STATUS) {
+		DBG_ERROR(et131x_dbginfo,
+			  "NICRxPkts >> Alcatel Status Word error."
+			  "Value 0x%08x\n", pPSREntry->word0.value);
+	}
+#endif
+
+	/* Indicate that we have used this PSR entry. */
+	if (++pRxLocal->local_psr_full.bits.psr_full >
+	    pRxLocal->PsrNumEntries - 1) {
+		pRxLocal->local_psr_full.bits.psr_full = 0;
+		pRxLocal->local_psr_full.bits.psr_full_wrap ^= 1;
+	}
+
+	writel(pRxLocal->local_psr_full.value,
+	       &pAdapter->CSRAddress->rxdma.psr_full_offset.value);
+
+#ifndef USE_FBR0
+	if (ringIndex != 1) {
+		DBG_ERROR(et131x_dbginfo,
+			  "NICRxPkts PSR Entry %d indicates "
+			  "Buffer Ring 0 in use\n",
+			  pRxLocal->local_psr_full.bits.psr_full);
+		DBG_RX_LEAVE(et131x_dbginfo);
+		return NULL;
+	}
+#endif
+
+#ifdef USE_FBR0
+	if (ringIndex > 1 ||
+	    (ringIndex == 0 &&
+	     bufferIndex > pRxLocal->Fbr0NumEntries - 1) ||
+	    (ringIndex == 1 &&
+	     bufferIndex > pRxLocal->Fbr1NumEntries - 1))
+#else
+	if (ringIndex != 1 ||
+	    bufferIndex > pRxLocal->Fbr1NumEntries - 1)
+#endif
+	{
+		/* Illegal buffer or ring index cannot be used by S/W*/
+		DBG_ERROR(et131x_dbginfo,
+			  "NICRxPkts PSR Entry %d indicates "
+			  "length of %d and/or bad bi(%d)\n",
+			  pRxLocal->local_psr_full.bits.psr_full,
+			  localLen, bufferIndex);
+		DBG_RX_LEAVE(et131x_dbginfo);
+		return NULL;
+	}
+
+	/* Get and fill the RFD. */
+	spin_lock_irqsave(&pAdapter->RcvLock, lockflags);
+
+	pMpRfd = NULL;
+	element = pRxLocal->RecvList.next;
+	pMpRfd = (PMP_RFD) list_entry(element, MP_RFD, list_node);
+
+	if (pMpRfd == NULL) {
+		DBG_RX(et131x_dbginfo,
+		       "NULL RFD returned from RecvList via list_entry()\n");
+		DBG_RX_LEAVE(et131x_dbginfo);
+		spin_unlock_irqrestore(&pAdapter->RcvLock, lockflags);
+		return NULL;
+	}
+
+	list_del(&pMpRfd->list_node);
+	pRxLocal->nReadyRecv--;
+
+	spin_unlock_irqrestore(&pAdapter->RcvLock, lockflags);
+
+	pMpRfd->iBufferIndex = bufferIndex;
+	pMpRfd->iRingIndex = ringIndex;
+
+	/* In V1 silicon, there is a bug which screws up filtering of
+	 * runt packets.  Therefore runt packet filtering is disabled
+	 * in the MAC and the packets are dropped here.  They are
+	 * also counted here.
+	 */
+	if (localLen < (NIC_MIN_PACKET_SIZE + 4)) {
+		pAdapter->Stats.other_errors++;
+		localLen = 0;
+	}
+
+	if (localLen) {
+		if (pAdapter->ReplicaPhyLoopbk == 1) {
+			pBufVa = pRxLocal->Fbr[ringIndex]->Va[bufferIndex];
+
+			if (memcmp(&pBufVa[6], &pAdapter->CurrentAddress[0],
+				   ETH_ALEN) == 0) {
+				if (memcmp(&pBufVa[42], "Replica packet",
+					   ETH_HLEN)) {
+					pAdapter->ReplicaPhyLoopbkPF = 1;
+				}
+			}
+			DBG_WARNING(et131x_dbginfo,
+				    "pBufVa:\t%02x:%02x:%02x:%02x:%02x:%02x\n",
+				    pBufVa[6], pBufVa[7], pBufVa[8],
+				    pBufVa[9], pBufVa[10], pBufVa[11]);
+
+			DBG_WARNING(et131x_dbginfo,
+				    "CurrentAddr:\t%02x:%02x:%02x:%02x:%02x:%02x\n",
+				    pAdapter->CurrentAddress[0],
+				    pAdapter->CurrentAddress[1],
+				    pAdapter->CurrentAddress[2],
+				    pAdapter->CurrentAddress[3],
+				    pAdapter->CurrentAddress[4],
+				    pAdapter->CurrentAddress[5]);
+		}
+
+		/* Determine if this is a multicast packet coming in */
+		if ((Word0.value & ALCATEL_MULTICAST_PKT) &&
+		    !(Word0.value & ALCATEL_BROADCAST_PKT)) {
+			/* Promiscuous mode and Multicast mode are
+			 * not mutually exclusive as was first
+			 * thought.  I guess Promiscuous is just
+			 * considered a super-set of the other
+			 * filters. Generally filter is 0x2b when in
+			 * promiscuous mode.
+			 */
+			if ((pAdapter->PacketFilter & ET131X_PACKET_TYPE_MULTICAST)
+			    && !(pAdapter->PacketFilter & ET131X_PACKET_TYPE_PROMISCUOUS)
+			    && !(pAdapter->PacketFilter & ET131X_PACKET_TYPE_ALL_MULTICAST)) {
+				pBufVa = pRxLocal->Fbr[ringIndex]->
+						Va[bufferIndex];
+
+				/* Loop through our list to see if the
+				 * destination address of this packet
+				 * matches one in our list.
+				 */
+				for (nIndex = 0;
+				     nIndex < pAdapter->MCAddressCount;
+				     nIndex++) {
+					if (pBufVa[0] ==
+					    pAdapter->MCList[nIndex][0]
+					    && pBufVa[1] ==
+					    pAdapter->MCList[nIndex][1]
+					    && pBufVa[2] ==
+					    pAdapter->MCList[nIndex][2]
+					    && pBufVa[3] ==
+					    pAdapter->MCList[nIndex][3]
+					    && pBufVa[4] ==
+					    pAdapter->MCList[nIndex][4]
+					    && pBufVa[5] ==
+					    pAdapter->MCList[nIndex][5]) {
+						break;
+					}
+				}
+
+				/* If our index is equal to the number
+				 * of Multicast address we have, then
+				 * this means we did not find this
+				 * packet's matching address in our
+				 * list.  Set the PacketSize to zero,
+				 * so we free our RFD when we return
+				 * from this function.
+				 */
+				if (nIndex == pAdapter->MCAddressCount) {
+					localLen = 0;
+				}
+			}
+
+			if (localLen > 0) {
+				pAdapter->Stats.multircv++;
+			}
+		} else if (Word0.value & ALCATEL_BROADCAST_PKT) {
+			pAdapter->Stats.brdcstrcv++;
+		} else {
+			/* Not sure what this counter measures in
+			 * promiscuous mode. Perhaps we should check
+			 * the MAC address to see if it is directed
+			 * to us in promiscuous mode.
+			 */
+			pAdapter->Stats.unircv++;
+		}
+	}
+
+	if (localLen > 0) {
+		struct sk_buff *skb = NULL;
+
+		//pMpRfd->PacketSize = localLen - 4;
+		pMpRfd->PacketSize = localLen;
+
+		skb = dev_alloc_skb(pMpRfd->PacketSize + 2);
+		if (!skb) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Couldn't alloc an SKB for Rx\n");
+			DBG_RX_LEAVE(et131x_dbginfo);
+			return NULL;
+		}
+
+		pAdapter->net_stats.rx_bytes += pMpRfd->PacketSize;
+
+		memcpy(skb_put(skb, pMpRfd->PacketSize),
+		       pRxLocal->Fbr[ringIndex]->Va[bufferIndex],
+		       pMpRfd->PacketSize);
+
+		skb->dev = pAdapter->netdev;
+		skb->protocol = eth_type_trans(skb, pAdapter->netdev);
+		skb->ip_summed = CHECKSUM_NONE;
+
+		netif_rx(skb);
+	} else {
+		pMpRfd->PacketSize = 0;
+	}
+
+	nic_return_rfd(pAdapter, pMpRfd);
+
+	DBG_RX(et131x_dbginfo, "(1)\n");
+	DBG_RX_LEAVE(et131x_dbginfo);
+	return pMpRfd;
+}
+
+/**
+ * et131x_reset_recv - Reset the receive list
+ * @pAdapter: pointer to our adapter
+ *
+ * Assumption, Rcv spinlock has been acquired.
+ */
+void et131x_reset_recv(struct et131x_adapter *pAdapter)
+{
+	PMP_RFD pMpRfd;
+	struct list_head *element;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	DBG_ASSERT(!list_empty(&pAdapter->RxRing.RecvList));
+
+	/* Take all the RFD's from the pending list, and stick them on the
+	 * RecvList.
+	 */
+	while (!list_empty(&pAdapter->RxRing.RecvPendingList)) {
+		element = pAdapter->RxRing.RecvPendingList.next;
+
+		pMpRfd = (PMP_RFD) list_entry(element, MP_RFD, list_node);
+
+		list_del(&pMpRfd->list_node);
+		list_add_tail(&pMpRfd->list_node, &pAdapter->RxRing.RecvList);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_handle_recv_interrupt - Interrupt handler for receive processing
+ * @pAdapter: pointer to our adapter
+ *
+ * Assumption, Rcv spinlock has been acquired.
+ */
+void et131x_handle_recv_interrupt(struct et131x_adapter *pAdapter)
+{
+	PMP_RFD pMpRfd = NULL;
+	struct sk_buff *PacketArray[NUM_PACKETS_HANDLED];
+	PMP_RFD RFDFreeArray[NUM_PACKETS_HANDLED];
+	uint32_t PacketArrayCount = 0;
+	uint32_t PacketsToHandle;
+	uint32_t PacketFreeCount = 0;
+	bool TempUnfinishedRec = false;
+
+	DBG_RX_ENTER(et131x_dbginfo);
+
+	PacketsToHandle = NUM_PACKETS_HANDLED;
+
+	/* Process up to available RFD's */
+	while (PacketArrayCount < PacketsToHandle) {
+		if (list_empty(&pAdapter->RxRing.RecvList)) {
+			DBG_ASSERT(pAdapter->RxRing.nReadyRecv == 0);
+			DBG_ERROR(et131x_dbginfo, "NO RFD's !!!!!!!!!!!!!\n");
+			TempUnfinishedRec = true;
+			break;
+		}
+
+		pMpRfd = nic_rx_pkts(pAdapter);
+
+		if (pMpRfd == NULL) {
+			break;
+		}
+
+		/* Do not receive any packets until a filter has been set.
+		 * Do not receive any packets until we are at D0.
+		 * Do not receive any packets until we have link.
+		 * If length is zero, return the RFD in order to advance the
+		 * Free buffer ring.
+		 */
+		if ((!pAdapter->PacketFilter) ||
+		    (pAdapter->PoMgmt.PowerState != NdisDeviceStateD0) ||
+		    (!MP_LINK_DETECTED(pAdapter)) ||
+		    (pMpRfd->PacketSize == 0)) {
+			continue;
+		}
+
+		/* Increment the number of packets we received */
+		pAdapter->Stats.ipackets++;
+
+		/* Set the status on the packet, either resources or success */
+		if (pAdapter->RxRing.nReadyRecv >= RFD_LOW_WATER_MARK) {
+			/* Put this RFD on the pending list
+			 *
+			 * NOTE: nic_rx_pkts() above is already returning the
+			 * RFD to the RecvList, so don't additionally do that
+			 * here.
+			 * Besides, we don't really need (at this point) the
+			 * pending list anyway.
+			 */
+			//spin_lock_irqsave( &pAdapter->RcvPendLock, lockflags );
+			//list_add_tail( &pMpRfd->list_node, &pAdapter->RxRing.RecvPendingList );
+			//spin_unlock_irqrestore( &pAdapter->RcvPendLock, lockflags );
+
+			/* Update the number of outstanding Recvs */
+			//MP_INC_RCV_REF( pAdapter );
+		} else {
+			RFDFreeArray[PacketFreeCount] = pMpRfd;
+			PacketFreeCount++;
+
+			DBG_WARNING(et131x_dbginfo,
+				    "RFD's are running out !!!!!!!!!!!!!\n");
+		}
+
+		PacketArray[PacketArrayCount] = pMpRfd->Packet;
+		PacketArrayCount++;
+	}
+
+	if ((PacketArrayCount == NUM_PACKETS_HANDLED) || TempUnfinishedRec) {
+		pAdapter->RxRing.UnfinishedReceives = true;
+		writel(pAdapter->RegistryTxTimeInterval * NANO_IN_A_MICRO,
+		       &pAdapter->CSRAddress->global.watchdog_timer);
+	} else {
+		/* Watchdog timer will disable itself if appropriate. */
+		pAdapter->RxRing.UnfinishedReceives = false;
+	}
+
+	DBG_RX_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * NICReturnRFD - Recycle a RFD and put it back onto the receive list
+ * @pAdapter: pointer to our adapter
+ * @pMpRfd: pointer to the RFD
+ */
+void nic_return_rfd(struct et131x_adapter *pAdapter, PMP_RFD pMpRfd)
+{
+	struct _rx_ring_t *pRxLocal = &pAdapter->RxRing;
+	struct _RXDMA_t __iomem *pRxDma = &pAdapter->CSRAddress->rxdma;
+	uint16_t bi = pMpRfd->iBufferIndex;
+	uint8_t ri = pMpRfd->iRingIndex;
+	unsigned long lockflags;
+
+	DBG_RX_ENTER(et131x_dbginfo);
+
+	/* We don't use any of the OOB data besides status. Otherwise, we
+	 * need to clean up OOB data
+	 */
+	if (
+#ifdef USE_FBR0
+	    (ri == 0 && bi < pRxLocal->Fbr0NumEntries) ||
+#endif
+	    (ri == 1 && bi < pRxLocal->Fbr1NumEntries)) {
+		spin_lock_irqsave(&pAdapter->FbrLock, lockflags);
+
+		if (ri == 1) {
+			PFBR_DESC_t pNextDesc =
+			    (PFBR_DESC_t) (pRxLocal->pFbr1RingVa) +
+			    pRxLocal->local_Fbr1_full.bits.val;
+
+			/* Handle the Free Buffer Ring advancement here. Write
+			 * the PA / Buffer Index for the returned buffer into
+			 * the oldest (next to be freed)FBR entry
+			 */
+			pNextDesc->addr_hi = pRxLocal->Fbr[1]->PAHigh[bi];
+			pNextDesc->addr_lo = pRxLocal->Fbr[1]->PALow[bi];
+			pNextDesc->word2.value = bi;
+
+			if (++pRxLocal->local_Fbr1_full.bits.val >
+			    (pRxLocal->Fbr1NumEntries - 1)) {
+				pRxLocal->local_Fbr1_full.bits.val = 0;
+				pRxLocal->local_Fbr1_full.bits.wrap ^= 1;
+			}
+
+			writel(pRxLocal->local_Fbr1_full.value,
+			       &pRxDma->fbr1_full_offset.value);
+		}
+#ifdef USE_FBR0
+		else {
+			PFBR_DESC_t pNextDesc =
+			    (PFBR_DESC_t) pRxLocal->pFbr0RingVa +
+			    pRxLocal->local_Fbr0_full.bits.val;
+
+			/* Handle the Free Buffer Ring advancement here. Write
+			 * the PA / Buffer Index for the returned buffer into
+			 * the oldest (next to be freed) FBR entry
+			 */
+			pNextDesc->addr_hi = pRxLocal->Fbr[0]->PAHigh[bi];
+			pNextDesc->addr_lo = pRxLocal->Fbr[0]->PALow[bi];
+			pNextDesc->word2.value = bi;
+
+			if (++pRxLocal->local_Fbr0_full.bits.val >
+			    (pRxLocal->Fbr0NumEntries - 1)) {
+				pRxLocal->local_Fbr0_full.bits.val = 0;
+				pRxLocal->local_Fbr0_full.bits.wrap ^= 1;
+			}
+
+			writel(pRxLocal->local_Fbr0_full.value,
+			       &pRxDma->fbr0_full_offset.value);
+		}
+#endif
+		spin_unlock_irqrestore(&pAdapter->FbrLock, lockflags);
+	} else {
+		DBG_ERROR(et131x_dbginfo,
+			  "NICReturnRFD illegal Buffer Index returned\n");
+	}
+
+	/* The processing on this RFD is done, so put it back on the tail of
+	 * our list
+	 */
+	spin_lock_irqsave(&pAdapter->RcvLock, lockflags);
+	list_add_tail(&pMpRfd->list_node, &pRxLocal->RecvList);
+	pRxLocal->nReadyRecv++;
+	spin_unlock_irqrestore(&pAdapter->RcvLock, lockflags);
+
+	DBG_ASSERT(pRxLocal->nReadyRecv <= pRxLocal->NumRfd);
+	DBG_RX_LEAVE(et131x_dbginfo);
+}
diff --git a/drivers/staging/et131x/et1310_rx.h b/drivers/staging/et131x/et1310_rx.h
new file mode 100644
index 0000000..ea66dbc
--- /dev/null
+++ b/drivers/staging/et131x/et1310_rx.h
@@ -0,0 +1,373 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_rx.h - Defines, structs, enums, prototypes, etc. pertaining to data
+ *               reception.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET1310_RX_H__
+#define __ET1310_RX_H__
+
+#include "et1310_address_map.h"
+
+#define USE_FBR0 true
+
+#ifdef USE_FBR0
+//#define FBR0_BUFFER_SIZE 256
+#endif
+
+//#define FBR1_BUFFER_SIZE 2048
+
+#define FBR_CHUNKS 32
+
+#define MAX_DESC_PER_RING_RX         1024
+
+/* number of RFDs - default and min */
+#ifdef USE_FBR0
+#define RFD_LOW_WATER_MARK	40
+#define NIC_MIN_NUM_RFD		64
+#define NIC_DEFAULT_NUM_RFD	1024
+#else
+#define RFD_LOW_WATER_MARK	20
+#define NIC_MIN_NUM_RFD		64
+#define NIC_DEFAULT_NUM_RFD	256
+#endif
+
+#define NUM_PACKETS_HANDLED	256
+
+#define ALCATEL_BAD_STATUS	0xe47f0000
+#define ALCATEL_MULTICAST_PKT	0x01000000
+#define ALCATEL_BROADCAST_PKT	0x02000000
+
+/* typedefs for Free Buffer Descriptors */
+typedef union _FBR_WORD2_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 reserved:22;	// bits 10-31
+		u32 bi:10;		// bits 0-9(Buffer Index)
+#else
+		u32 bi:10;		// bits 0-9(Buffer Index)
+		u32 reserved:22;	// bit 10-31
+#endif
+	} bits;
+} FBR_WORD2_t, *PFBR_WORD2_t;
+
+typedef struct _FBR_DESC_t {
+	u32 addr_lo;
+	u32 addr_hi;
+	FBR_WORD2_t word2;
+} FBR_DESC_t, *PFBR_DESC_t;
+
+/* Typedefs for Packet Status Ring Descriptors */
+typedef union _PKT_STAT_DESC_WORD0_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		// top 16 bits are from the Alcatel Status Word as enumerated in
+		// PE-MCXMAC Data Sheet IPD DS54 0210-1 (also IPD-DS80 0205-2)
+#if 0
+		u32 asw_trunc:1;		// bit 31(Rx frame truncated)
+#endif
+		u32 asw_long_evt:1;	// bit 31(Rx long event)
+		u32 asw_VLAN_tag:1;	// bit 30(VLAN tag detected)
+		u32 asw_unsupported_op:1;	// bit 29(unsupported OP code)
+		u32 asw_pause_frame:1;	// bit 28(is a pause frame)
+		u32 asw_control_frame:1;	// bit 27(is a control frame)
+		u32 asw_dribble_nibble:1;	// bit 26(spurious bits after EOP)
+		u32 asw_broadcast:1;	// bit 25(has a broadcast address)
+		u32 asw_multicast:1;	// bit 24(has a multicast address)
+		u32 asw_OK:1;		// bit 23(valid CRC + no code error)
+		u32 asw_too_long:1;	// bit 22(frame length > 1518 bytes)
+		u32 asw_len_chk_err:1;	// bit 21(frame length field incorrect)
+		u32 asw_CRC_err:1;		// bit 20(CRC error)
+		u32 asw_code_err:1;	// bit 19(one or more nibbles signalled as errors)
+		u32 asw_false_carrier_event:1;	// bit 18(bad carrier since last good packet)
+		u32 asw_RX_DV_event:1;	// bit 17(short receive event detected)
+		u32 asw_prev_pkt_dropped:1;// bit 16(e.g. IFG too small on previous)
+		u32 unused:5;		// bits 11-15
+		u32 vp:1;			// bit 10(VLAN Packet)
+		u32 jp:1;			// bit 9(Jumbo Packet)
+		u32 ft:1;			// bit 8(Frame Truncated)
+		u32 drop:1;		// bit 7(Drop packet)
+		u32 rxmac_error:1;		// bit 6(RXMAC Error Indicator)
+		u32 wol:1;			// bit 5(WOL Event)
+		u32 tcpp:1;		// bit 4(TCP checksum pass)
+		u32 tcpa:1;		// bit 3(TCP checksum assist)
+		u32 ipp:1;			// bit 2(IP checksum pass)
+		u32 ipa:1;			// bit 1(IP checksum assist)
+		u32 hp:1;			// bit 0(hash pass)
+#else
+		u32 hp:1;			// bit 0(hash pass)
+		u32 ipa:1;			// bit 1(IP checksum assist)
+		u32 ipp:1;			// bit 2(IP checksum pass)
+		u32 tcpa:1;		// bit 3(TCP checksum assist)
+		u32 tcpp:1;		// bit 4(TCP checksum pass)
+		u32 wol:1;			// bit 5(WOL Event)
+		u32 rxmac_error:1;		// bit 6(RXMAC Error Indicator)
+		u32 drop:1;		// bit 7(Drop packet)
+		u32 ft:1;			// bit 8(Frame Truncated)
+		u32 jp:1;			// bit 9(Jumbo Packet)
+		u32 vp:1;			// bit 10(VLAN Packet)
+		u32 unused:5;		// bits 11-15
+		u32 asw_prev_pkt_dropped:1;// bit 16(e.g. IFG too small on previous)
+		u32 asw_RX_DV_event:1;	// bit 17(short receive event detected)
+		u32 asw_false_carrier_event:1;	// bit 18(bad carrier since last good packet)
+		u32 asw_code_err:1;	// bit 19(one or more nibbles signalled as errors)
+		u32 asw_CRC_err:1;		// bit 20(CRC error)
+		u32 asw_len_chk_err:1;	// bit 21(frame length field incorrect)
+		u32 asw_too_long:1;	// bit 22(frame length > 1518 bytes)
+		u32 asw_OK:1;		// bit 23(valid CRC + no code error)
+		u32 asw_multicast:1;	// bit 24(has a multicast address)
+		u32 asw_broadcast:1;	// bit 25(has a broadcast address)
+		u32 asw_dribble_nibble:1;	// bit 26(spurious bits after EOP)
+		u32 asw_control_frame:1;	// bit 27(is a control frame)
+		u32 asw_pause_frame:1;	// bit 28(is a pause frame)
+		u32 asw_unsupported_op:1;	// bit 29(unsupported OP code)
+		u32 asw_VLAN_tag:1;	// bit 30(VLAN tag detected)
+		u32 asw_long_evt:1;	// bit 31(Rx long event)
+#if 0
+		u32 asw_trunc:1;		// bit 31(Rx frame truncated)
+#endif
+#endif
+	} bits;
+} PKT_STAT_DESC_WORD0_t, *PPKT_STAT_WORD0_t;
+
+typedef union _PKT_STAT_DESC_WORD1_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:4;	// bits 28-31
+		u32 ri:2;		// bits 26-27(Ring Index)
+		u32 bi:10;		// bits 16-25(Buffer Index)
+		u32 length:16;	// bit 0-15(length in bytes)
+#else
+		u32 length:16;	// bit 0-15(length in bytes)
+		u32 bi:10;		// bits 16-25(Buffer Index)
+		u32 ri:2;		// bits 26-27(Ring Index)
+		u32 unused:4;	// bits 28-31
+#endif
+	} bits;
+} PKT_STAT_DESC_WORD1_t, *PPKT_STAT_WORD1_t;
+
+typedef struct _PKT_STAT_DESC_t {
+	PKT_STAT_DESC_WORD0_t word0;
+	PKT_STAT_DESC_WORD1_t word1;
+} PKT_STAT_DESC_t, *PPKT_STAT_DESC_t;
+
+/* Typedefs for the RX DMA status word */
+
+/*
+ * RXSTAT_WORD0_t structure holds part of the status bits of the Rx DMA engine
+ * that get copied out to memory by the ET-1310.  Word 0 is a 32 bit word
+ * whichcontains Free Buffer ring 0 and 1 available offset.
+ */
+typedef union _rxstat_word0_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 FBR1unused:5;	// bits 27-31
+		u32 FBR1wrap:1;	// bit 26
+		u32 FBR1offset:10;	// bits 16-25
+		u32 FBR0unused:5;	// bits 11-15
+		u32 FBR0wrap:1;	// bit 10
+		u32 FBR0offset:10;	// bits 0-9
+#else
+		u32 FBR0offset:10;	// bits 0-9
+		u32 FBR0wrap:1;	// bit 10
+		u32 FBR0unused:5;	// bits 11-15
+		u32 FBR1offset:10;	// bits 16-25
+		u32 FBR1wrap:1;	// bit 26
+		u32 FBR1unused:5;	// bits 27-31
+#endif
+	} bits;
+} RXSTAT_WORD0_t, *PRXSTAT_WORD0_t;
+
+/*
+ * RXSTAT_WORD1_t structure holds part of the status bits of the Rx DMA engine
+ * that get copied out to memory by the ET-1310.  Word 3 is a 32 bit word
+ * which contains the Packet Status Ring available offset.
+ */
+typedef union _rxstat_word1_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 PSRunused:3;	// bits 29-31
+		u32 PSRwrap:1;	// bit 28
+		u32 PSRoffset:12;	// bits 16-27
+		u32 reserved:16;	// bits 0-15
+#else
+		u32 reserved:16;	// bits 0-15
+		u32 PSRoffset:12;	// bits 16-27
+		u32 PSRwrap:1;	// bit 28
+		u32 PSRunused:3;	// bits 29-31
+#endif
+	} bits;
+} RXSTAT_WORD1_t, *PRXSTAT_WORD1_t;
+
+/*
+ * RX_STATUS_BLOCK_t is sructure representing the status of the Rx DMA engine
+ * it sits in free memory, and is pointed to by 0x101c / 0x1020
+ */
+typedef struct _rx_status_block_t {
+	RXSTAT_WORD0_t Word0;
+	RXSTAT_WORD1_t Word1;
+} RX_STATUS_BLOCK_t, *PRX_STATUS_BLOCK_t;
+
+/*
+ * Structure for look-up table holding free buffer ring pointers
+ */
+typedef struct _FbrLookupTable {
+	void *Va[MAX_DESC_PER_RING_RX];
+	void *Buffer1[MAX_DESC_PER_RING_RX];
+	void *Buffer2[MAX_DESC_PER_RING_RX];
+	u32 PAHigh[MAX_DESC_PER_RING_RX];
+	u32 PALow[MAX_DESC_PER_RING_RX];
+} FBRLOOKUPTABLE, *PFBRLOOKUPTABLE;
+
+typedef enum {
+	ONE_PACKET_INTERRUPT,
+	FOUR_PACKET_INTERRUPT
+} eRX_INTERRUPT_STATE_t, *PeRX_INTERRUPT_STATE_t;
+
+/*
+ * Structure to hold the skb's in a list
+ */
+typedef struct rx_skb_list_elem {
+	struct list_head skb_list_elem;
+	dma_addr_t dma_addr;
+	struct sk_buff *skb;
+} RX_SKB_LIST_ELEM, *PRX_SKB_LIST_ELEM;
+
+/*
+ * RX_RING_t is sructure representing the adaptor's local reference(s) to the
+ * rings
+ */
+typedef struct _rx_ring_t {
+#ifdef USE_FBR0
+	void *pFbr0RingVa;
+	dma_addr_t pFbr0RingPa;
+	void *Fbr0MemVa[MAX_DESC_PER_RING_RX / FBR_CHUNKS];
+	dma_addr_t Fbr0MemPa[MAX_DESC_PER_RING_RX / FBR_CHUNKS];
+	uint64_t Fbr0Realpa;
+	uint64_t Fbr0offset;
+	DMA10W_t local_Fbr0_full;
+	u32 Fbr0NumEntries;
+	u32 Fbr0BufferSize;
+#endif
+	void *pFbr1RingVa;
+	dma_addr_t pFbr1RingPa;
+	void *Fbr1MemVa[MAX_DESC_PER_RING_RX / FBR_CHUNKS];
+	dma_addr_t Fbr1MemPa[MAX_DESC_PER_RING_RX / FBR_CHUNKS];
+	uint64_t Fbr1Realpa;
+	uint64_t Fbr1offset;
+	FBRLOOKUPTABLE *Fbr[2];
+	DMA10W_t local_Fbr1_full;
+	u32 Fbr1NumEntries;
+	u32 Fbr1BufferSize;
+
+	void *pPSRingVa;
+	dma_addr_t pPSRingPa;
+	uint64_t pPSRingRealPa;
+	uint64_t pPSRingOffset;
+	RXDMA_PSR_FULL_OFFSET_t local_psr_full;
+	u32 PsrNumEntries;
+
+	void *pRxStatusVa;
+	dma_addr_t pRxStatusPa;
+	uint64_t RxStatusRealPA;
+	uint64_t RxStatusOffset;
+
+	struct list_head RecvBufferPool;
+
+	/* RECV */
+	struct list_head RecvList;
+	struct list_head RecvPendingList;
+	u32 nReadyRecv;
+
+	u32 NumRfd;
+
+	bool UnfinishedReceives;
+
+	struct list_head RecvPacketPool;
+
+	/* lookaside lists */
+	struct kmem_cache *RecvLookaside;
+} RX_RING_t, *PRX_RING_t;
+
+/* Forward reference of RFD */
+struct _MP_RFD;
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+/* PROTOTYPES for Initialization */
+int et131x_rx_dma_memory_alloc(struct et131x_adapter *adapter);
+void et131x_rx_dma_memory_free(struct et131x_adapter *adapter);
+int et131x_rfd_resources_alloc(struct et131x_adapter *adapter,
+			       struct _MP_RFD *pMpRfd);
+void et131x_rfd_resources_free(struct et131x_adapter *adapter,
+			       struct _MP_RFD *pMpRfd);
+int et131x_init_recv(struct et131x_adapter *adapter);
+
+void ConfigRxDmaRegs(struct et131x_adapter *adapter);
+void SetRxDmaTimer(struct et131x_adapter *adapter);
+void et131x_rx_dma_disable(struct et131x_adapter *adapter);
+void et131x_rx_dma_enable(struct et131x_adapter *adapter);
+
+void et131x_reset_recv(struct et131x_adapter *adapter);
+
+void et131x_handle_recv_interrupt(struct et131x_adapter *adapter);
+
+#endif /* __ET1310_RX_H__ */
diff --git a/drivers/staging/et131x/et1310_tx.c b/drivers/staging/et131x/et1310_tx.c
new file mode 100644
index 0000000..a95c260
--- /dev/null
+++ b/drivers/staging/et131x/et1310_tx.c
@@ -0,0 +1,1525 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_tx.c - Routines used to perform data transmission.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+#include "et131x_isr.h"
+
+#include "et1310_tx.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+static void et131x_update_tcb_list(struct et131x_adapter *pAdapter);
+static void et131x_check_send_wait_list(struct et131x_adapter *pAdapter);
+static inline void et131x_free_send_packet(struct et131x_adapter *pAdapter,
+					   PMP_TCB pMpTcb);
+static int et131x_send_packet(struct sk_buff *skb,
+			      struct et131x_adapter *pAdapter);
+static int nic_send_packet(struct et131x_adapter *pAdapter, PMP_TCB pMpTcb);
+
+/**
+ * et131x_tx_dma_memory_alloc
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success and errno on failure (as defined in errno.h).
+ *
+ * Allocates memory that will be visible both to the device and to the CPU.
+ * The OS will pass us packets, pointers to which we will insert in the Tx
+ * Descriptor queue. The device will read this queue to find the packets in
+ * memory. The device will update the "status" in memory each time it xmits a
+ * packet.
+ */
+int et131x_tx_dma_memory_alloc(struct et131x_adapter *adapter)
+{
+	int desc_size = 0;
+	TX_RING_t *tx_ring = &adapter->TxRing;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Allocate memory for the TCB's (Transmit Control Block) */
+	adapter->TxRing.MpTcbMem = (MP_TCB *) kcalloc(NUM_TCB, sizeof(MP_TCB),
+						      GFP_ATOMIC | GFP_DMA);
+	if (!adapter->TxRing.MpTcbMem) {
+		DBG_ERROR(et131x_dbginfo, "Cannot alloc memory for TCBs\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Allocate enough memory for the Tx descriptor ring, and allocate
+	 * some extra so that the ring can be aligned on a 4k boundary.
+	 */
+	desc_size = (sizeof(TX_DESC_ENTRY_t) * NUM_DESC_PER_RING_TX) + 4096 - 1;
+	tx_ring->pTxDescRingVa =
+	    (PTX_DESC_ENTRY_t) pci_alloc_consistent(adapter->pdev, desc_size,
+						    &tx_ring->pTxDescRingPa);
+	if (!adapter->TxRing.pTxDescRingVa) {
+		DBG_ERROR(et131x_dbginfo, "Cannot alloc memory for Tx Ring\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Save physical address
+	 *
+	 * NOTE: pci_alloc_consistent(), used above to alloc DMA regions,
+	 * ALWAYS returns SAC (32-bit) addresses. If DAC (64-bit) addresses
+	 * are ever returned, make sure the high part is retrieved here before
+	 * storing the adjusted address.
+	 */
+	tx_ring->pTxDescRingAdjustedPa = tx_ring->pTxDescRingPa;
+
+	/* Align Tx Descriptor Ring on a 4k (0x1000) byte boundary */
+	et131x_align_allocated_memory(adapter,
+				      &tx_ring->pTxDescRingAdjustedPa,
+				      &tx_ring->TxDescOffset, 0x0FFF);
+
+	tx_ring->pTxDescRingVa += tx_ring->TxDescOffset;
+
+	/* Allocate memory for the Tx status block */
+	tx_ring->pTxStatusVa = pci_alloc_consistent(adapter->pdev,
+						    sizeof(TX_STATUS_BLOCK_t),
+						    &tx_ring->pTxStatusPa);
+	if (!adapter->TxRing.pTxStatusPa) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot alloc memory for Tx status block\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	/* Allocate memory for a dummy buffer */
+	tx_ring->pTxDummyBlkVa = pci_alloc_consistent(adapter->pdev,
+						      NIC_MIN_PACKET_SIZE,
+						      &tx_ring->pTxDummyBlkPa);
+	if (!adapter->TxRing.pTxDummyBlkPa) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot alloc memory for Tx dummy buffer\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+/**
+ * et131x_tx_dma_memory_free - Free all memory allocated within this module
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success and errno on failure (as defined in errno.h).
+ */
+void et131x_tx_dma_memory_free(struct et131x_adapter *adapter)
+{
+	int desc_size = 0;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	if (adapter->TxRing.pTxDescRingVa) {
+		/* Free memory relating to Tx rings here */
+		adapter->TxRing.pTxDescRingVa -= adapter->TxRing.TxDescOffset;
+
+		desc_size =
+		    (sizeof(TX_DESC_ENTRY_t) * NUM_DESC_PER_RING_TX) + 4096 - 1;
+
+		pci_free_consistent(adapter->pdev,
+				    desc_size,
+				    adapter->TxRing.pTxDescRingVa,
+				    adapter->TxRing.pTxDescRingPa);
+
+		adapter->TxRing.pTxDescRingVa = NULL;
+	}
+
+	/* Free memory for the Tx status block */
+	if (adapter->TxRing.pTxStatusVa) {
+		pci_free_consistent(adapter->pdev,
+				    sizeof(TX_STATUS_BLOCK_t),
+				    adapter->TxRing.pTxStatusVa,
+				    adapter->TxRing.pTxStatusPa);
+
+		adapter->TxRing.pTxStatusVa = NULL;
+	}
+
+	/* Free memory for the dummy buffer */
+	if (adapter->TxRing.pTxDummyBlkVa) {
+		pci_free_consistent(adapter->pdev,
+				    NIC_MIN_PACKET_SIZE,
+				    adapter->TxRing.pTxDummyBlkVa,
+				    adapter->TxRing.pTxDummyBlkPa);
+
+		adapter->TxRing.pTxDummyBlkVa = NULL;
+	}
+
+	/* Free the memory for MP_TCB structures */
+	if (adapter->TxRing.MpTcbMem) {
+		kfree(adapter->TxRing.MpTcbMem);
+		adapter->TxRing.MpTcbMem = NULL;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * ConfigTxDmaRegs - Set up the tx dma section of the JAGCore.
+ * @adapter: pointer to our private adapter structure
+ */
+void ConfigTxDmaRegs(struct et131x_adapter *pAdapter)
+{
+	struct _TXDMA_t __iomem *pTxDma = &pAdapter->CSRAddress->txdma;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Load the hardware with the start of the transmit descriptor ring. */
+	writel((uint32_t) (pAdapter->TxRing.pTxDescRingAdjustedPa >> 32),
+	       &pTxDma->pr_base_hi);
+	writel((uint32_t) pAdapter->TxRing.pTxDescRingAdjustedPa,
+	       &pTxDma->pr_base_lo);
+
+	/* Initialise the transmit DMA engine */
+	writel(NUM_DESC_PER_RING_TX - 1, &pTxDma->pr_num_des.value);
+
+	/* Load the completion writeback physical address
+	 *
+	 * NOTE: pci_alloc_consistent(), used above to alloc DMA regions,
+	 * ALWAYS returns SAC (32-bit) addresses. If DAC (64-bit) addresses
+	 * are ever returned, make sure the high part is retrieved here before
+	 * storing the adjusted address.
+	 */
+	writel(0, &pTxDma->dma_wb_base_hi);
+	writel(pAdapter->TxRing.pTxStatusPa, &pTxDma->dma_wb_base_lo);
+
+	memset(pAdapter->TxRing.pTxStatusVa, 0, sizeof(TX_STATUS_BLOCK_t));
+
+	writel(0, &pTxDma->service_request.value);
+	pAdapter->TxRing.txDmaReadyToSend.value = 0;
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_tx_dma_disable - Stop of Tx_DMA on the ET1310
+ * @pAdapter: pointer to our adapter structure
+ */
+void et131x_tx_dma_disable(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Setup the tramsmit dma configuration register */
+	writel(0x101, &pAdapter->CSRAddress->txdma.csr.value);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_tx_dma_enable - re-start of Tx_DMA on the ET1310.
+ * @pAdapter: pointer to our adapter structure
+ *
+ * Mainly used after a return to the D0 (full-power) state from a lower state.
+ */
+void et131x_tx_dma_enable(struct et131x_adapter *pAdapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	if (pAdapter->RegistryPhyLoopbk) {
+	/* TxDMA is disabled for loopback operation. */
+		writel(0x101, &pAdapter->CSRAddress->txdma.csr.value);
+	} else {
+		TXDMA_CSR_t csr = { 0 };
+
+		/* Setup the transmit dma configuration register for normal
+		 * operation
+		 */
+		csr.bits.sngl_epkt_mode = 1;
+		csr.bits.halt = 0;
+		csr.bits.cache_thrshld = pAdapter->RegistryDMACache;
+		writel(csr.value, &pAdapter->CSRAddress->txdma.csr.value);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_init_send - Initialize send data structures
+ * @adapter: pointer to our private adapter structure
+ */
+void et131x_init_send(struct et131x_adapter *adapter)
+{
+	PMP_TCB pMpTcb;
+	uint32_t TcbCount;
+	TX_RING_t *tx_ring;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Setup some convenience pointers */
+	tx_ring = &adapter->TxRing;
+	pMpTcb = adapter->TxRing.MpTcbMem;
+
+	tx_ring->TCBReadyQueueHead = pMpTcb;
+
+	/* Go through and set up each TCB */
+	for (TcbCount = 0; TcbCount < NUM_TCB; TcbCount++) {
+		memset(pMpTcb, 0, sizeof(MP_TCB));
+
+		/* Set the link pointer in HW TCB to the next TCB in the
+		 * chain.  If this is the last TCB in the chain, also set the
+		 * tail pointer.
+		 */
+		if (TcbCount < NUM_TCB - 1) {
+			pMpTcb->Next = pMpTcb + 1;
+		} else {
+			tx_ring->TCBReadyQueueTail = pMpTcb;
+			pMpTcb->Next = (PMP_TCB) NULL;
+		}
+
+		pMpTcb++;
+	}
+
+	/* Curr send queue should now be empty */
+	tx_ring->CurrSendHead = (PMP_TCB) NULL;
+	tx_ring->CurrSendTail = (PMP_TCB) NULL;
+
+	INIT_LIST_HEAD(&adapter->TxRing.SendWaitQueue);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_send_packets - This function is called by the OS to send packets
+ * @skb: the packet(s) to send
+ * @netdev:device on which to TX the above packet(s)
+ *
+ * Return 0 in almost all cases; non-zero value in extreme hard failure only
+ */
+int et131x_send_packets(struct sk_buff *skb, struct net_device *netdev)
+{
+	int status = 0;
+	struct et131x_adapter *pAdapter = NULL;
+
+	DBG_TX_ENTER(et131x_dbginfo);
+
+	pAdapter = netdev_priv(netdev);
+
+	/* Send these packets
+	 *
+	 * NOTE: The Linux Tx entry point is only given one packet at a time
+	 * to Tx, so the PacketCount and it's array used makes no sense here
+	 */
+
+	/* Queue is not empty or TCB is not available */
+	if (!list_empty(&pAdapter->TxRing.SendWaitQueue) ||
+	    MP_TCB_RESOURCES_NOT_AVAILABLE(pAdapter)) {
+		/* NOTE: If there's an error on send, no need to queue the
+		 * packet under Linux; if we just send an error up to the
+		 * netif layer, it will resend the skb to us.
+		 */
+		DBG_VERBOSE(et131x_dbginfo, "TCB Resources Not Available\n");
+		status = -ENOMEM;
+	} else {
+		/* We need to see if the link is up; if it's not, make the
+		 * netif layer think we're good and drop the packet
+		 */
+		//if( MP_SHOULD_FAIL_SEND( pAdapter ) || pAdapter->DriverNoPhyAccess )
+		if (MP_SHOULD_FAIL_SEND(pAdapter) || pAdapter->DriverNoPhyAccess
+		    || !netif_carrier_ok(netdev)) {
+			DBG_VERBOSE(et131x_dbginfo,
+				    "Can't Tx, Link is DOWN; drop the packet\n");
+
+			dev_kfree_skb_any(skb);
+			skb = NULL;
+
+			pAdapter->net_stats.tx_dropped++;
+		} else {
+			status = et131x_send_packet(skb, pAdapter);
+
+			if (status == -ENOMEM) {
+
+				/* NOTE: If there's an error on send, no need
+				 * to queue the packet under Linux; if we just
+				 * send an error up to the netif layer, it
+				 * will resend the skb to us.
+				 */
+				DBG_WARNING(et131x_dbginfo,
+					    "Resources problem, Queue tx packet\n");
+			} else if (status != 0) {
+				/* On any other error, make netif think we're
+				 * OK and drop the packet
+				 */
+				DBG_WARNING(et131x_dbginfo,
+					    "General error, drop packet\n");
+
+				dev_kfree_skb_any(skb);
+				skb = NULL;
+
+				pAdapter->net_stats.tx_dropped++;
+			}
+		}
+	}
+
+	DBG_TX_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_send_packet - Do the work to send a packet
+ * @skb: the packet(s) to send
+ * @pAdapter: a pointer to the device's private adapter structure
+ *
+ * Return 0 in almost all cases; non-zero value in extreme hard failure only.
+ *
+ * Assumption: Send spinlock has been acquired
+ */
+static int et131x_send_packet(struct sk_buff *skb,
+			      struct et131x_adapter *pAdapter)
+{
+	int status = 0;
+	PMP_TCB pMpTcb = NULL;
+	uint16_t *pShBufVa;
+	unsigned long lockflags;
+
+	DBG_TX_ENTER(et131x_dbginfo);
+
+	/* Is our buffer scattered, or continuous? */
+	if (skb_shinfo(skb)->nr_frags == 0) {
+		DBG_TX(et131x_dbginfo, "Scattered buffer: NO\n");
+	} else {
+		DBG_TX(et131x_dbginfo, "Scattered buffer: YES, Num Frags: %d\n",
+		       skb_shinfo(skb)->nr_frags);
+	}
+
+	/* All packets must have at least a MAC address and a protocol type */
+	if (skb->len < ETH_HLEN) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Packet size < ETH_HLEN (14 bytes)\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	/* Get a TCB for this packet */
+	spin_lock_irqsave(&pAdapter->TCBReadyQLock, lockflags);
+
+	pMpTcb = pAdapter->TxRing.TCBReadyQueueHead;
+
+	if (pMpTcb == NULL) {
+		spin_unlock_irqrestore(&pAdapter->TCBReadyQLock, lockflags);
+
+		DBG_WARNING(et131x_dbginfo, "Can't obtain a TCB\n");
+		DBG_TX_LEAVE(et131x_dbginfo);
+		return -ENOMEM;
+	}
+
+	pAdapter->TxRing.TCBReadyQueueHead = pMpTcb->Next;
+
+	if (pAdapter->TxRing.TCBReadyQueueHead == NULL) {
+		pAdapter->TxRing.TCBReadyQueueTail = NULL;
+	}
+
+	spin_unlock_irqrestore(&pAdapter->TCBReadyQLock, lockflags);
+
+	pMpTcb->PacketLength = skb->len;
+	pMpTcb->Packet = skb;
+
+	if ((skb->data != NULL) && ((skb->len - skb->data_len) >= 6)) {
+		pShBufVa = (uint16_t *) skb->data;
+
+		if ((pShBufVa[0] == 0xffff) &&
+		    (pShBufVa[1] == 0xffff) && (pShBufVa[2] == 0xffff)) {
+			MP_SET_FLAG(pMpTcb, fMP_DEST_BROAD);
+		} else if ((pShBufVa[0] & 0x3) == 0x0001) {
+			MP_SET_FLAG(pMpTcb, fMP_DEST_MULTI);
+		}
+	}
+
+	pMpTcb->Next = NULL;
+
+	/* Call the NIC specific send handler. */
+	if (status == 0) {
+		status = nic_send_packet(pAdapter, pMpTcb);
+	}
+
+	if (status != 0) {
+		spin_lock_irqsave(&pAdapter->TCBReadyQLock, lockflags);
+
+		if (pAdapter->TxRing.TCBReadyQueueTail) {
+			pAdapter->TxRing.TCBReadyQueueTail->Next = pMpTcb;
+		} else {
+			/* Apparently ready Q is empty. */
+			pAdapter->TxRing.TCBReadyQueueHead = pMpTcb;
+		}
+
+		pAdapter->TxRing.TCBReadyQueueTail = pMpTcb;
+
+		spin_unlock_irqrestore(&pAdapter->TCBReadyQLock, lockflags);
+
+		DBG_TX_LEAVE(et131x_dbginfo);
+		return status;
+	}
+
+	DBG_ASSERT(pAdapter->TxRing.nBusySend <= NUM_TCB);
+
+	DBG_TX_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+/**
+ * nic_send_packet - NIC specific send handler for version B silicon.
+ * @pAdapter: pointer to our adapter
+ * @pMpTcb: pointer to MP_TCB
+ *
+ * Returns 0 or errno.
+ */
+static int nic_send_packet(struct et131x_adapter *pAdapter, PMP_TCB pMpTcb)
+{
+	uint32_t loopIndex;
+	TX_DESC_ENTRY_t CurDesc[24];
+	uint32_t FragmentNumber = 0;
+	uint32_t iThisCopy, iRemainder;
+	struct sk_buff *pPacket = pMpTcb->Packet;
+	uint32_t FragListCount = skb_shinfo(pPacket)->nr_frags + 1;
+	struct skb_frag_struct *pFragList = &skb_shinfo(pPacket)->frags[0];
+	unsigned long lockflags1, lockflags2;
+
+	DBG_TX_ENTER(et131x_dbginfo);
+
+	/* Part of the optimizations of this send routine restrict us to
+	 * sending 24 fragments at a pass.  In practice we should never see
+	 * more than 5 fragments.
+	 *
+	 * NOTE: The older version of this function (below) can handle any
+	 * number of fragments. If needed, we can call this function,
+	 * although it is less efficient.
+	 */
+	if (FragListCount > 23) {
+		DBG_TX_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	memset(CurDesc, 0, sizeof(TX_DESC_ENTRY_t) * (FragListCount + 1));
+
+	for (loopIndex = 0; loopIndex < FragListCount; loopIndex++) {
+		/* If there is something in this element, lets get a
+		 * descriptor from the ring and get the necessary data
+		 */
+		if (loopIndex == 0) {
+			/* If the fragments are smaller than a standard MTU,
+			 * then map them to a single descriptor in the Tx
+			 * Desc ring. However, if they're larger, as is
+			 * possible with support for jumbo packets, then
+			 * split them each across 2 descriptors.
+			 *
+			 * This will work until we determine why the hardware
+			 * doesn't seem to like large fragments.
+			 */
+			if ((pPacket->len - pPacket->data_len) <= 1514) {
+				DBG_TX(et131x_dbginfo,
+				       "Got packet of length %d, "
+				       "filling desc entry %d, "
+				       "TCB: 0x%p\n",
+				       (pPacket->len - pPacket->data_len),
+				       pAdapter->TxRing.txDmaReadyToSend.bits.
+				       val, pMpTcb);
+
+				CurDesc[FragmentNumber].DataBufferPtrHigh = 0;
+
+				CurDesc[FragmentNumber].word2.bits.
+				    length_in_bytes =
+				    pPacket->len - pPacket->data_len;
+
+				/* NOTE: Here, the dma_addr_t returned from
+				 * pci_map_single() is implicitly cast as a
+				 * uint32_t. Although dma_addr_t can be
+				 * 64-bit, the address returned by
+				 * pci_map_single() is always 32-bit
+				 * addressable (as defined by the pci/dma
+				 * subsystem)
+				 */
+				CurDesc[FragmentNumber++].DataBufferPtrLow =
+				    pci_map_single(pAdapter->pdev,
+						   pPacket->data,
+						   pPacket->len -
+						   pPacket->data_len,
+						   PCI_DMA_TODEVICE);
+			} else {
+				DBG_TX(et131x_dbginfo,
+				       "Got packet of length %d, "
+				       "filling desc entry %d, "
+				       "TCB: 0x%p\n",
+				       (pPacket->len - pPacket->data_len),
+				       pAdapter->TxRing.txDmaReadyToSend.bits.
+				       val, pMpTcb);
+
+				CurDesc[FragmentNumber].DataBufferPtrHigh = 0;
+
+				CurDesc[FragmentNumber].word2.bits.
+				    length_in_bytes =
+				    ((pPacket->len - pPacket->data_len) / 2);
+
+				/* NOTE: Here, the dma_addr_t returned from
+				 * pci_map_single() is implicitly cast as a
+				 * uint32_t. Although dma_addr_t can be
+				 * 64-bit, the address returned by
+				 * pci_map_single() is always 32-bit
+				 * addressable (as defined by the pci/dma
+				 * subsystem)
+				 */
+				CurDesc[FragmentNumber++].DataBufferPtrLow =
+				    pci_map_single(pAdapter->pdev,
+						   pPacket->data,
+						   ((pPacket->len -
+						     pPacket->data_len) / 2),
+						   PCI_DMA_TODEVICE);
+				CurDesc[FragmentNumber].DataBufferPtrHigh = 0;
+
+				CurDesc[FragmentNumber].word2.bits.
+				    length_in_bytes =
+				    ((pPacket->len - pPacket->data_len) / 2);
+
+				/* NOTE: Here, the dma_addr_t returned from
+				 * pci_map_single() is implicitly cast as a
+				 * uint32_t. Although dma_addr_t can be
+				 * 64-bit, the address returned by
+				 * pci_map_single() is always 32-bit
+				 * addressable (as defined by the pci/dma
+				 * subsystem)
+				 */
+				CurDesc[FragmentNumber++].DataBufferPtrLow =
+				    pci_map_single(pAdapter->pdev,
+						   pPacket->data +
+						   ((pPacket->len -
+						     pPacket->data_len) / 2),
+						   ((pPacket->len -
+						     pPacket->data_len) / 2),
+						   PCI_DMA_TODEVICE);
+			}
+		} else {
+			DBG_TX(et131x_dbginfo,
+			       "Got packet of length %d,"
+			       "filling desc entry %d\n"
+			       "TCB: 0x%p\n",
+			       pFragList[loopIndex].size,
+			       pAdapter->TxRing.txDmaReadyToSend.bits.val,
+			       pMpTcb);
+
+			CurDesc[FragmentNumber].DataBufferPtrHigh = 0;
+
+			CurDesc[FragmentNumber].word2.bits.length_in_bytes =
+			    pFragList[loopIndex - 1].size;
+
+			/* NOTE: Here, the dma_addr_t returned from
+			 * pci_map_page() is implicitly cast as a uint32_t.
+			 * Although dma_addr_t can be 64-bit, the address
+			 * returned by pci_map_page() is always 32-bit
+			 * addressable (as defined by the pci/dma subsystem)
+			 */
+			CurDesc[FragmentNumber++].DataBufferPtrLow =
+			    pci_map_page(pAdapter->pdev,
+					 pFragList[loopIndex - 1].page,
+					 pFragList[loopIndex - 1].page_offset,
+					 pFragList[loopIndex - 1].size,
+					 PCI_DMA_TODEVICE);
+		}
+	}
+
+	if (FragmentNumber == 0) {
+		DBG_WARNING(et131x_dbginfo, "No. frags is 0\n");
+		return -EIO;
+	}
+
+	if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) {
+		if (++pAdapter->TxRing.TxPacketsSinceLastinterrupt ==
+		    pAdapter->RegistryTxNumBuffers) {
+			CurDesc[FragmentNumber - 1].word3.value = 0x5;
+			pAdapter->TxRing.TxPacketsSinceLastinterrupt = 0;
+		} else {
+			CurDesc[FragmentNumber - 1].word3.value = 0x1;
+		}
+	} else {
+		CurDesc[FragmentNumber - 1].word3.value = 0x5;
+	}
+
+	CurDesc[0].word3.bits.f = 1;
+
+	pMpTcb->WrIndexStart = pAdapter->TxRing.txDmaReadyToSend;
+	pMpTcb->PacketStaleCount = 0;
+
+	spin_lock_irqsave(&pAdapter->SendHWLock, lockflags1);
+
+	iThisCopy =
+	    NUM_DESC_PER_RING_TX - pAdapter->TxRing.txDmaReadyToSend.bits.val;
+
+	if (iThisCopy >= FragmentNumber) {
+		iRemainder = 0;
+		iThisCopy = FragmentNumber;
+	} else {
+		iRemainder = FragmentNumber - iThisCopy;
+	}
+
+	memcpy(pAdapter->TxRing.pTxDescRingVa +
+	       pAdapter->TxRing.txDmaReadyToSend.bits.val, CurDesc,
+	       sizeof(TX_DESC_ENTRY_t) * iThisCopy);
+
+	pAdapter->TxRing.txDmaReadyToSend.bits.val += iThisCopy;
+
+	if ((pAdapter->TxRing.txDmaReadyToSend.bits.val == 0) ||
+	    (pAdapter->TxRing.txDmaReadyToSend.bits.val ==
+	     NUM_DESC_PER_RING_TX)) {
+		if (pAdapter->TxRing.txDmaReadyToSend.bits.wrap) {
+			pAdapter->TxRing.txDmaReadyToSend.value = 0;
+		} else {
+			pAdapter->TxRing.txDmaReadyToSend.value = 0x400;
+		}
+	}
+
+	if (iRemainder) {
+		memcpy(pAdapter->TxRing.pTxDescRingVa,
+		       CurDesc + iThisCopy,
+		       sizeof(TX_DESC_ENTRY_t) * iRemainder);
+
+		pAdapter->TxRing.txDmaReadyToSend.bits.val += iRemainder;
+	}
+
+	if (pAdapter->TxRing.txDmaReadyToSend.bits.val == 0) {
+		if (pAdapter->TxRing.txDmaReadyToSend.value) {
+			pMpTcb->WrIndex.value = NUM_DESC_PER_RING_TX - 1;
+		} else {
+			pMpTcb->WrIndex.value =
+			    0x400 | (NUM_DESC_PER_RING_TX - 1);
+		}
+	} else {
+		pMpTcb->WrIndex.value =
+		    pAdapter->TxRing.txDmaReadyToSend.value - 1;
+	}
+
+	spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags2);
+
+	if (pAdapter->TxRing.CurrSendTail) {
+		pAdapter->TxRing.CurrSendTail->Next = pMpTcb;
+	} else {
+		pAdapter->TxRing.CurrSendHead = pMpTcb;
+	}
+
+	pAdapter->TxRing.CurrSendTail = pMpTcb;
+
+	DBG_ASSERT(pMpTcb->Next == NULL);
+
+	pAdapter->TxRing.nBusySend++;
+
+	spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags2);
+
+	/* Write the new write pointer back to the device. */
+	writel(pAdapter->TxRing.txDmaReadyToSend.value,
+	       &pAdapter->CSRAddress->txdma.service_request.value);
+
+	/* For Gig only, we use Tx Interrupt coalescing.  Enable the software
+	 * timer to wake us up if this packet isn't followed by N more.
+	 */
+	if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) {
+		writel(pAdapter->RegistryTxTimeInterval * NANO_IN_A_MICRO,
+		       &pAdapter->CSRAddress->global.watchdog_timer);
+	}
+
+	spin_unlock_irqrestore(&pAdapter->SendHWLock, lockflags1);
+
+	DBG_TX_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+/*
+ * NOTE: For now, keep this older version of NICSendPacket around for
+ * reference, even though it's not used
+ */
+#if 0
+
+/**
+ * NICSendPacket - NIC specific send handler.
+ * @pAdapter: pointer to our adapter
+ * @pMpTcb: pointer to MP_TCB
+ *
+ * Returns 0 on succes, errno on failure.
+ *
+ * This version of the send routine is designed for version A silicon.
+ * Assumption - Send spinlock has been acquired.
+ */
+static int nic_send_packet(struct et131x_adapter *pAdapter, PMP_TCB pMpTcb)
+{
+	uint32_t loopIndex, fragIndex, loopEnd;
+	uint32_t iSplitFirstElement = 0;
+	uint32_t SegmentSize = 0;
+	TX_DESC_ENTRY_t CurDesc;
+	TX_DESC_ENTRY_t *CurDescPostCopy = NULL;
+	uint32_t SlotsAvailable;
+	DMA10W_t ServiceComplete;
+	unsigned int lockflags1, lockflags2;
+	struct sk_buff *pPacket = pMpTcb->Packet;
+	uint32_t FragListCount = skb_shinfo(pPacket)->nr_frags + 1;
+	struct skb_frag_struct *pFragList = &skb_shinfo(pPacket)->frags[0];
+
+	DBG_TX_ENTER(et131x_dbginfo);
+
+	ServiceComplete.value =
+		readl(&pAdapter->CSRAddress->txdma.NewServiceComplete.value);
+
+	/*
+	 * Attempt to fix TWO hardware bugs:
+	 * 1)  NEVER write an odd number of descriptors.
+	 * 2)  If packet length is less than NIC_MIN_PACKET_SIZE, then pad the
+	 *     packet to NIC_MIN_PACKET_SIZE bytes by adding a new last
+	 *     descriptor IN HALF DUPLEX MODE ONLY
+	 * NOTE that (2) interacts with (1).  If the packet is less than
+	 * NIC_MIN_PACKET_SIZE bytes then we will append a descriptor.
+	 * Therefore if it is even now, it will eventually end up odd, and
+	 * so will need adjusting.
+	 *
+	 * VLAN tags get involved since VLAN tags add another one or two
+	 * segments.
+	 */
+	DBG_TX(et131x_dbginfo,
+	       "pMpTcb->PacketLength: %d\n", pMpTcb->PacketLength);
+
+	if ((pAdapter->uiDuplexMode == 0)
+	    && (pMpTcb->PacketLength < NIC_MIN_PACKET_SIZE)) {
+		DBG_TX(et131x_dbginfo,
+		       "HALF DUPLEX mode AND len < MIN_PKT_SIZE\n");
+		if ((FragListCount & 0x1) == 0) {
+			DBG_TX(et131x_dbginfo,
+			       "Even number of descs, split 1st elem\n");
+			iSplitFirstElement = 1;
+			//SegmentSize = pFragList[0].size / 2;
+			SegmentSize = (pPacket->len - pPacket->data_len) / 2;
+		}
+	} else if (FragListCount & 0x1) {
+		DBG_TX(et131x_dbginfo, "Odd number of descs, split 1st elem\n");
+
+		iSplitFirstElement = 1;
+		//SegmentSize = pFragList[0].size / 2;
+		SegmentSize = (pPacket->len - pPacket->data_len) / 2;
+	}
+
+	spin_lock_irqsave(&pAdapter->SendHWLock, lockflags1);
+
+	if (pAdapter->TxRing.txDmaReadyToSend.bits.serv_req_wrap ==
+	    ServiceComplete.bits.serv_cpl_wrap) {
+		/* The ring hasn't wrapped.  Slots available should be
+		 * (RING_SIZE) -  the difference between the two pointers.
+		 */
+		SlotsAvailable = NUM_DESC_PER_RING_TX -
+		    (pAdapter->TxRing.txDmaReadyToSend.bits.serv_req -
+		     ServiceComplete.bits.serv_cpl);
+	} else {
+		/* The ring has wrapped.  Slots available should be the
+		 * difference between the two pointers.
+		 */
+		SlotsAvailable = ServiceComplete.bits.serv_cpl -
+		    pAdapter->TxRing.txDmaReadyToSend.bits.serv_req;
+	}
+
+	if ((FragListCount + iSplitFirstElement) > SlotsAvailable) {
+		DBG_WARNING(et131x_dbginfo,
+			    "Not Enough Space in Tx Desc Ring\n");
+		spin_unlock_irqrestore(&pAdapter->SendHWLock, lockflags1);
+		return -ENOMEM;
+	}
+
+	loopEnd = (FragListCount) + iSplitFirstElement;
+	fragIndex = 0;
+
+	DBG_TX(et131x_dbginfo,
+	       "TCB           : 0x%p\n"
+	       "Packet (SKB)  : 0x%p\t Packet->len: %d\t Packet->data_len: %d\n"
+	       "FragListCount : %d\t iSplitFirstElement: %d\t loopEnd:%d\n",
+	       pMpTcb,
+	       pPacket, pPacket->len, pPacket->data_len,
+	       FragListCount, iSplitFirstElement, loopEnd);
+
+	for (loopIndex = 0; loopIndex < loopEnd; loopIndex++) {
+		if (loopIndex > iSplitFirstElement) {
+			fragIndex++;
+		}
+
+		DBG_TX(et131x_dbginfo,
+		       "In loop, loopIndex: %d\t fragIndex: %d\n", loopIndex,
+		       fragIndex);
+
+		/* If there is something in this element, let's get a
+		 * descriptor from the ring and get the necessary data
+		 */
+		DBG_TX(et131x_dbginfo,
+		       "Packet Length %d,"
+		       "filling desc entry %d\n",
+		       pPacket->len,
+		       pAdapter->TxRing.txDmaReadyToSend.bits.serv_req);
+
+		// NOTE - Should we do a paranoia check here to make sure the fragment
+		// actually has a length? It's HIGHLY unlikely the fragment would
+		// contain no data...
+		if (1) {
+			// NOTE - Currently always getting 32-bit addrs, and dma_addr_t is
+			//        only 32-bit, so leave "high" ptr value out for now
+			CurDesc.DataBufferPtrHigh = 0;
+
+			CurDesc.word2.value = 0;
+			CurDesc.word3.value = 0;
+
+			if (fragIndex == 0) {
+				if (iSplitFirstElement) {
+					DBG_TX(et131x_dbginfo,
+					       "Split first element: YES\n");
+
+					if (loopIndex == 0) {
+						DBG_TX(et131x_dbginfo,
+						       "Got fragment of length %d, fragIndex: %d\n",
+						       pPacket->len -
+						       pPacket->data_len,
+						       fragIndex);
+						DBG_TX(et131x_dbginfo,
+						       "SegmentSize: %d\n",
+						       SegmentSize);
+
+						CurDesc.word2.bits.
+						    length_in_bytes =
+						    SegmentSize;
+						CurDesc.DataBufferPtrLow =
+						    pci_map_single(pAdapter->
+								   pdev,
+								   pPacket->
+								   data,
+								   SegmentSize,
+								   PCI_DMA_TODEVICE);
+						DBG_TX(et131x_dbginfo,
+						       "pci_map_single() returns: 0x%08x\n",
+						       CurDesc.
+						       DataBufferPtrLow);
+					} else {
+						DBG_TX(et131x_dbginfo,
+						       "Got fragment of length %d, fragIndex: %d\n",
+						       pPacket->len -
+						       pPacket->data_len,
+						       fragIndex);
+						DBG_TX(et131x_dbginfo,
+						       "Leftover Size: %d\n",
+						       (pPacket->len -
+							pPacket->data_len -
+							SegmentSize));
+
+						CurDesc.word2.bits.
+						    length_in_bytes =
+						    ((pPacket->len -
+						      pPacket->data_len) -
+						     SegmentSize);
+						CurDesc.DataBufferPtrLow =
+						    pci_map_single(pAdapter->
+								   pdev,
+								   (pPacket->
+								    data +
+								    SegmentSize),
+								   (pPacket->
+								    len -
+								    pPacket->
+								    data_len -
+								    SegmentSize),
+								   PCI_DMA_TODEVICE);
+						DBG_TX(et131x_dbginfo,
+						       "pci_map_single() returns: 0x%08x\n",
+						       CurDesc.
+						       DataBufferPtrLow);
+					}
+				} else {
+					DBG_TX(et131x_dbginfo,
+					       "Split first element: NO\n");
+
+					CurDesc.word2.bits.length_in_bytes =
+					    pPacket->len - pPacket->data_len;
+
+					CurDesc.DataBufferPtrLow =
+					    pci_map_single(pAdapter->pdev,
+							   pPacket->data,
+							   (pPacket->len -
+							    pPacket->data_len),
+							   PCI_DMA_TODEVICE);
+					DBG_TX(et131x_dbginfo,
+					       "pci_map_single() returns: 0x%08x\n",
+					       CurDesc.DataBufferPtrLow);
+				}
+			} else {
+
+				CurDesc.word2.bits.length_in_bytes =
+				    pFragList[fragIndex - 1].size;
+				CurDesc.DataBufferPtrLow =
+				    pci_map_page(pAdapter->pdev,
+						 pFragList[fragIndex - 1].page,
+						 pFragList[fragIndex -
+							   1].page_offset,
+						 pFragList[fragIndex - 1].size,
+						 PCI_DMA_TODEVICE);
+				DBG_TX(et131x_dbginfo,
+				       "pci_map_page() returns: 0x%08x\n",
+				       CurDesc.DataBufferPtrLow);
+			}
+
+			if (loopIndex == 0) {
+				/* This is the first descriptor of the packet
+				 *
+				 * Set the "f" bit to indicate this is the
+				 * first descriptor in the packet.
+				 */
+				DBG_TX(et131x_dbginfo,
+				       "This is our FIRST descriptor\n");
+				CurDesc.word3.bits.f = 1;
+
+				pMpTcb->WrIndexStart =
+				    pAdapter->TxRing.txDmaReadyToSend;
+			}
+
+			if ((loopIndex == (loopEnd - 1)) &&
+			    (pAdapter->uiDuplexMode ||
+			     (pMpTcb->PacketLength >= NIC_MIN_PACKET_SIZE))) {
+				/* This is the Last descriptor of the packet */
+				DBG_TX(et131x_dbginfo,
+				       "THIS is our LAST descriptor\n");
+
+				if (pAdapter->uiLinkSpeed ==
+				    TRUEPHY_SPEED_1000MBPS) {
+					if (++pAdapter->TxRing.
+					    TxPacketsSinceLastinterrupt >=
+					    pAdapter->RegistryTxNumBuffers) {
+						CurDesc.word3.value = 0x5;
+						pAdapter->TxRing.
+						    TxPacketsSinceLastinterrupt
+						    = 0;
+					} else {
+						CurDesc.word3.value = 0x1;
+					}
+				} else {
+					CurDesc.word3.value = 0x5;
+				}
+
+				/* Following index will be used during freeing
+				 * of packet
+				 */
+				pMpTcb->WrIndex =
+				    pAdapter->TxRing.txDmaReadyToSend;
+				pMpTcb->PacketStaleCount = 0;
+			}
+
+			/* Copy the descriptor (filled above) into the
+			 * descriptor ring at the next free entry.  Advance
+			 * the "next free entry" variable
+			 */
+			memcpy(pAdapter->TxRing.pTxDescRingVa +
+			       pAdapter->TxRing.txDmaReadyToSend.bits.serv_req,
+			       &CurDesc, sizeof(TX_DESC_ENTRY_t));
+
+			CurDescPostCopy =
+			    pAdapter->TxRing.pTxDescRingVa +
+			    pAdapter->TxRing.txDmaReadyToSend.bits.serv_req;
+
+			DBG_TX(et131x_dbginfo,
+			       "CURRENT DESCRIPTOR\n"
+			       "\tAddress           : 0x%p\n"
+			       "\tDataBufferPtrHigh : 0x%08x\n"
+			       "\tDataBufferPtrLow  : 0x%08x\n"
+			       "\tword2             : 0x%08x\n"
+			       "\tword3             : 0x%08x\n",
+			       CurDescPostCopy,
+			       CurDescPostCopy->DataBufferPtrHigh,
+			       CurDescPostCopy->DataBufferPtrLow,
+			       CurDescPostCopy->word2.value,
+			       CurDescPostCopy->word3.value);
+
+			if (++pAdapter->TxRing.txDmaReadyToSend.bits.serv_req >=
+			    NUM_DESC_PER_RING_TX) {
+				if (pAdapter->TxRing.txDmaReadyToSend.bits.
+				    serv_req_wrap) {
+					pAdapter->TxRing.txDmaReadyToSend.
+					    value = 0;
+				} else {
+					pAdapter->TxRing.txDmaReadyToSend.
+					    value = 0x400;
+				}
+			}
+		}
+	}
+
+	if (pAdapter->uiDuplexMode == 0 &&
+	    pMpTcb->PacketLength < NIC_MIN_PACKET_SIZE) {
+		// NOTE - Same 32/64-bit issue as above...
+		CurDesc.DataBufferPtrHigh = 0x0;
+		CurDesc.DataBufferPtrLow = pAdapter->TxRing.pTxDummyBlkPa;
+		CurDesc.word2.value = 0;
+
+		if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) {
+			if (++pAdapter->TxRing.TxPacketsSinceLastinterrupt >=
+			    pAdapter->RegistryTxNumBuffers) {
+				CurDesc.word3.value = 0x5;
+				pAdapter->TxRing.TxPacketsSinceLastinterrupt =
+				    0;
+			} else {
+				CurDesc.word3.value = 0x1;
+			}
+		} else {
+			CurDesc.word3.value = 0x5;
+		}
+
+		CurDesc.word2.bits.length_in_bytes =
+		    NIC_MIN_PACKET_SIZE - pMpTcb->PacketLength;
+
+		pMpTcb->WrIndex = pAdapter->TxRing.txDmaReadyToSend;
+
+		memcpy(pAdapter->TxRing.pTxDescRingVa +
+		       pAdapter->TxRing.txDmaReadyToSend.bits.serv_req,
+		       &CurDesc, sizeof(TX_DESC_ENTRY_t));
+
+		CurDescPostCopy =
+		    pAdapter->TxRing.pTxDescRingVa +
+		    pAdapter->TxRing.txDmaReadyToSend.bits.serv_req;
+
+		DBG_TX(et131x_dbginfo,
+		       "CURRENT DESCRIPTOR\n"
+		       "\tAddress           : 0x%p\n"
+		       "\tDataBufferPtrHigh : 0x%08x\n"
+		       "\tDataBufferPtrLow  : 0x%08x\n"
+		       "\tword2             : 0x%08x\n"
+		       "\tword3             : 0x%08x\n",
+		       CurDescPostCopy,
+		       CurDescPostCopy->DataBufferPtrHigh,
+		       CurDescPostCopy->DataBufferPtrLow,
+		       CurDescPostCopy->word2.value,
+		       CurDescPostCopy->word3.value);
+
+		if (++pAdapter->TxRing.txDmaReadyToSend.bits.serv_req >=
+		    NUM_DESC_PER_RING_TX) {
+			if (pAdapter->TxRing.txDmaReadyToSend.bits.
+			    serv_req_wrap) {
+				pAdapter->TxRing.txDmaReadyToSend.value = 0;
+			} else {
+				pAdapter->TxRing.txDmaReadyToSend.value = 0x400;
+			}
+		}
+
+		DBG_TX(et131x_dbginfo, "Padding descriptor %d by %d bytes\n",
+		       //pAdapter->TxRing.txDmaReadyToSend.value,
+		       pAdapter->TxRing.txDmaReadyToSend.bits.serv_req,
+		       NIC_MIN_PACKET_SIZE - pMpTcb->PacketLength);
+	}
+
+	spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags2);
+
+	if (pAdapter->TxRing.CurrSendTail) {
+		pAdapter->TxRing.CurrSendTail->Next = pMpTcb;
+	} else {
+		pAdapter->TxRing.CurrSendHead = pMpTcb;
+	}
+
+	pAdapter->TxRing.CurrSendTail = pMpTcb;
+
+	DBG_ASSERT(pMpTcb->Next == NULL);
+
+	pAdapter->TxRing.nBusySend++;
+
+	spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags2);
+
+	/* Write the new write pointer back to the device. */
+	writel(pAdapter->TxRing.txDmaReadyToSend.value,
+	       &pAdapter->CSRAddress->txdma.service_request.value);
+
+#ifdef CONFIG_ET131X_DEBUG
+	DumpDeviceBlock(DBG_TX_ON, pAdapter, 1);
+#endif
+
+	/* For Gig only, we use Tx Interrupt coalescing.  Enable the software
+	 * timer to wake us up if this packet isn't followed by N more.
+	 */
+	if (pAdapter->uiLinkSpeed == TRUEPHY_SPEED_1000MBPS) {
+		writel(pAdapter->RegistryTxTimeInterval * NANO_IN_A_MICRO,
+		       &pAdapter->CSRAddress->global.watchdog_timer);
+	}
+
+	spin_unlock_irqrestore(&pAdapter->SendHWLock, lockflags1);
+
+	DBG_TX_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+#endif
+
+/**
+ * et131x_free_send_packet - Recycle a MP_TCB, complete the packet if necessary
+ * @pAdapter: pointer to our adapter
+ * @pMpTcb: pointer to MP_TCB
+ *
+ * Assumption - Send spinlock has been acquired
+ */
+__inline void et131x_free_send_packet(struct et131x_adapter *pAdapter, PMP_TCB pMpTcb)
+{
+	unsigned long lockflags;
+	TX_DESC_ENTRY_t *desc = NULL;
+	struct net_device_stats *stats = &pAdapter->net_stats;
+
+	if (MP_TEST_FLAG(pMpTcb, fMP_DEST_BROAD)) {
+		atomic_inc(&pAdapter->Stats.brdcstxmt);
+	} else if (MP_TEST_FLAG(pMpTcb, fMP_DEST_MULTI)) {
+		atomic_inc(&pAdapter->Stats.multixmt);
+	} else {
+		atomic_inc(&pAdapter->Stats.unixmt);
+	}
+
+	if (pMpTcb->Packet) {
+		stats->tx_bytes += pMpTcb->Packet->len;
+
+		/* Iterate through the TX descriptors on the ring
+		 * corresponding to this packet and umap the fragments
+		 * they point to
+		 */
+		DBG_TX(et131x_dbginfo,
+		       "Unmap descriptors Here\n"
+		       "TCB                  : 0x%p\n"
+		       "TCB Next             : 0x%p\n"
+		       "TCB PacketLength     : %d\n"
+		       "TCB WrIndex.value    : 0x%08x\n"
+		       "TCB WrIndex.bits.val : %d\n"
+		       "TCB WrIndex.value    : 0x%08x\n"
+		       "TCB WrIndex.bits.val : %d\n",
+		       pMpTcb,
+		       pMpTcb->Next,
+		       pMpTcb->PacketLength,
+		       pMpTcb->WrIndexStart.value,
+		       pMpTcb->WrIndexStart.bits.val,
+		       pMpTcb->WrIndex.value,
+		       pMpTcb->WrIndex.bits.val);
+
+		do {
+			desc =
+			    (TX_DESC_ENTRY_t *) (pAdapter->TxRing.
+						 pTxDescRingVa +
+						 pMpTcb->WrIndexStart.bits.val);
+
+			DBG_TX(et131x_dbginfo,
+			       "CURRENT DESCRIPTOR\n"
+			       "\tAddress           : 0x%p\n"
+			       "\tDataBufferPtrHigh : 0x%08x\n"
+			       "\tDataBufferPtrLow  : 0x%08x\n"
+			       "\tword2             : 0x%08x\n"
+			       "\tword3             : 0x%08x\n",
+			       desc,
+			       desc->DataBufferPtrHigh,
+			       desc->DataBufferPtrLow,
+			       desc->word2.value,
+			       desc->word3.value);
+
+			pci_unmap_single(pAdapter->pdev,
+					 desc->DataBufferPtrLow,
+					 desc->word2.value, PCI_DMA_TODEVICE);
+
+			if (++pMpTcb->WrIndexStart.bits.val >=
+			    NUM_DESC_PER_RING_TX) {
+				if (pMpTcb->WrIndexStart.bits.wrap) {
+					pMpTcb->WrIndexStart.value = 0;
+				} else {
+					pMpTcb->WrIndexStart.value = 0x400;
+				}
+			}
+		}
+		while (desc != (pAdapter->TxRing.pTxDescRingVa +
+				pMpTcb->WrIndex.bits.val));
+
+		DBG_TX(et131x_dbginfo,
+		       "Free Packet (SKB)   : 0x%p\n", pMpTcb->Packet);
+
+		dev_kfree_skb_any(pMpTcb->Packet);
+	}
+
+	memset(pMpTcb, 0, sizeof(MP_TCB));
+
+	/* Add the TCB to the Ready Q */
+	spin_lock_irqsave(&pAdapter->TCBReadyQLock, lockflags);
+
+	pAdapter->Stats.opackets++;
+
+	if (pAdapter->TxRing.TCBReadyQueueTail) {
+		pAdapter->TxRing.TCBReadyQueueTail->Next = pMpTcb;
+	} else {
+		/* Apparently ready Q is empty. */
+		pAdapter->TxRing.TCBReadyQueueHead = pMpTcb;
+	}
+
+	pAdapter->TxRing.TCBReadyQueueTail = pMpTcb;
+
+	spin_unlock_irqrestore(&pAdapter->TCBReadyQLock, lockflags);
+
+	DBG_ASSERT(pAdapter->TxRing.nBusySend >= 0);
+}
+
+/**
+ * et131x_free_busy_send_packets - Free and complete the stopped active sends
+ * @pAdapter: pointer to our adapter
+ *
+ * Assumption - Send spinlock has been acquired
+ */
+void et131x_free_busy_send_packets(struct et131x_adapter *pAdapter)
+{
+	PMP_TCB pMpTcb;
+	struct list_head *pEntry;
+	struct sk_buff *pPacket = NULL;
+	unsigned long lockflags;
+	uint32_t FreeCounter = 0;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	while (!list_empty(&pAdapter->TxRing.SendWaitQueue)) {
+		spin_lock_irqsave(&pAdapter->SendWaitLock, lockflags);
+
+		pAdapter->TxRing.nWaitSend--;
+		spin_unlock_irqrestore(&pAdapter->SendWaitLock, lockflags);
+
+		pEntry = pAdapter->TxRing.SendWaitQueue.next;
+
+		pPacket = NULL;
+	}
+
+	pAdapter->TxRing.nWaitSend = 0;
+
+	/* Any packets being sent? Check the first TCB on the send list */
+	spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags);
+
+	pMpTcb = pAdapter->TxRing.CurrSendHead;
+
+	while ((pMpTcb != NULL) && (FreeCounter < NUM_TCB)) {
+		PMP_TCB pNext = pMpTcb->Next;
+
+		pAdapter->TxRing.CurrSendHead = pNext;
+
+		if (pNext == NULL) {
+			pAdapter->TxRing.CurrSendTail = NULL;
+		}
+
+		pAdapter->TxRing.nBusySend--;
+
+		spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags);
+
+		DBG_VERBOSE(et131x_dbginfo, "pMpTcb = 0x%p\n", pMpTcb);
+
+		FreeCounter++;
+		MP_FREE_SEND_PACKET_FUN(pAdapter, pMpTcb);
+
+		spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags);
+
+		pMpTcb = pAdapter->TxRing.CurrSendHead;
+	}
+
+	if (FreeCounter == NUM_TCB) {
+		DBG_ERROR(et131x_dbginfo,
+			  "MpFreeBusySendPackets exitted loop for a bad reason\n");
+		BUG();
+	}
+
+	spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags);
+
+	pAdapter->TxRing.nBusySend = 0;
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_handle_send_interrupt - Interrupt handler for sending processing
+ * @pAdapter: pointer to our adapter
+ *
+ * Re-claim the send resources, complete sends and get more to send from
+ * the send wait queue.
+ *
+ * Assumption - Send spinlock has been acquired
+ */
+void et131x_handle_send_interrupt(struct et131x_adapter *pAdapter)
+{
+	DBG_TX_ENTER(et131x_dbginfo);
+
+	/* Mark as completed any packets which have been sent by the device. */
+	et131x_update_tcb_list(pAdapter);
+
+	/* If we queued any transmits because we didn't have any TCBs earlier,
+	 * dequeue and send those packets now, as long as we have free TCBs.
+	 */
+	et131x_check_send_wait_list(pAdapter);
+
+	DBG_TX_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_update_tcb_list - Helper routine for Send Interrupt handler
+ * @pAdapter: pointer to our adapter
+ *
+ * Re-claims the send resources and completes sends.  Can also be called as
+ * part of the NIC send routine when the "ServiceComplete" indication has
+ * wrapped.
+ */
+static void et131x_update_tcb_list(struct et131x_adapter *pAdapter)
+{
+	unsigned long lockflags;
+	DMA10W_t ServiceComplete;
+	PMP_TCB pMpTcb;
+
+	ServiceComplete.value =
+	    readl(&pAdapter->CSRAddress->txdma.NewServiceComplete.value);
+
+	/* Has the ring wrapped?  Process any descriptors that do not have
+	 * the same "wrap" indicator as the current completion indicator
+	 */
+	spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags);
+
+	pMpTcb = pAdapter->TxRing.CurrSendHead;
+	while (pMpTcb &&
+	       ServiceComplete.bits.wrap != pMpTcb->WrIndex.bits.wrap  &&
+	       ServiceComplete.bits.val < pMpTcb->WrIndex.bits.val) {
+		pAdapter->TxRing.nBusySend--;
+		pAdapter->TxRing.CurrSendHead = pMpTcb->Next;
+		if (pMpTcb->Next == NULL) {
+			pAdapter->TxRing.CurrSendTail = NULL;
+		}
+
+		spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags);
+		MP_FREE_SEND_PACKET_FUN(pAdapter, pMpTcb);
+		spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags);
+
+		/* Goto the next packet */
+		pMpTcb = pAdapter->TxRing.CurrSendHead;
+	}
+	while (pMpTcb &&
+	       ServiceComplete.bits.wrap == pMpTcb->WrIndex.bits.wrap &&
+	       ServiceComplete.bits.val > pMpTcb->WrIndex.bits.val) {
+		pAdapter->TxRing.nBusySend--;
+		pAdapter->TxRing.CurrSendHead = pMpTcb->Next;
+		if (pMpTcb->Next == NULL) {
+			pAdapter->TxRing.CurrSendTail = NULL;
+		}
+
+		spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags);
+		MP_FREE_SEND_PACKET_FUN(pAdapter, pMpTcb);
+		spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags);
+
+		/* Goto the next packet */
+		pMpTcb = pAdapter->TxRing.CurrSendHead;
+	}
+
+	/* Wake up the queue when we hit a low-water mark */
+	if (pAdapter->TxRing.nBusySend <= (NUM_TCB / 3)) {
+		netif_wake_queue(pAdapter->netdev);
+	}
+
+	spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags);
+}
+
+/**
+ * et131x_check_send_wait_list - Helper routine for the interrupt handler
+ * @pAdapter: pointer to our adapter
+ *
+ * Takes packets from the send wait queue and posts them to the device (if
+ * room available).
+ */
+static void et131x_check_send_wait_list(struct et131x_adapter *pAdapter)
+{
+	unsigned long lockflags;
+
+	spin_lock_irqsave(&pAdapter->SendWaitLock, lockflags);
+
+	while (!list_empty(&pAdapter->TxRing.SendWaitQueue) &&
+	       MP_TCB_RESOURCES_AVAILABLE(pAdapter)) {
+		struct list_head *pEntry;
+
+		DBG_VERBOSE(et131x_dbginfo, "Tx packets on the wait queue\n");
+
+		pEntry = pAdapter->TxRing.SendWaitQueue.next;
+
+		pAdapter->TxRing.nWaitSend--;
+
+		DBG_WARNING(et131x_dbginfo,
+			    "MpHandleSendInterrupt - sent a queued pkt. Waiting %d\n",
+			    pAdapter->TxRing.nWaitSend);
+	}
+
+	spin_unlock_irqrestore(&pAdapter->SendWaitLock, lockflags);
+}
diff --git a/drivers/staging/et131x/et1310_tx.h b/drivers/staging/et131x/et1310_tx.h
new file mode 100644
index 0000000..2819c78
--- /dev/null
+++ b/drivers/staging/et131x/et1310_tx.h
@@ -0,0 +1,242 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et1310_tx.h - Defines, structs, enums, prototypes, etc. pertaining to data
+ *               transmission.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET1310_TX_H__
+#define __ET1310_TX_H__
+
+
+/* Typedefs for Tx Descriptor Ring */
+
+/*
+ * TXDESC_WORD2_t structure holds part of the control bits in the Tx Descriptor
+ * ring for the ET-1310
+ */
+typedef union _txdesc_word2_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 vlan_prio:3;		// bits 29-31(VLAN priority)
+		u32 vlan_cfi:1;		// bit 28(cfi)
+		u32 vlan_tag:12;		// bits 16-27(VLAN tag)
+		u32 length_in_bytes:16;	// bits  0-15(packet length)
+#else
+		u32 length_in_bytes:16;	// bits  0-15(packet length)
+		u32 vlan_tag:12;		// bits 16-27(VLAN tag)
+		u32 vlan_cfi:1;		// bit 28(cfi)
+		u32 vlan_prio:3;		// bits 29-31(VLAN priority)
+#endif	/* _BIT_FIELDS_HTOL */
+	} bits;
+} TXDESC_WORD2_t, *PTXDESC_WORD2_t;
+
+/*
+ * TXDESC_WORD3_t structure holds part of the control bits in the Tx Descriptor
+ * ring for the ET-1310
+ */
+typedef union _txdesc_word3_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:17;	// bits 15-31
+		u32 udpa:1;	// bit 14(UDP checksum assist)
+		u32 tcpa:1;	// bit 13(TCP checksum assist)
+		u32 ipa:1;		// bit 12(IP checksum assist)
+		u32 vlan:1;	// bit 11(append VLAN tag)
+		u32 hp:1;		// bit 10(Packet is a Huge packet)
+		u32 pp:1;		// bit  9(pad packet)
+		u32 mac:1;		// bit  8(MAC override)
+		u32 crc:1;		// bit  7(append CRC)
+		u32 e:1;		// bit  6(Tx frame has error)
+		u32 pf:1;		// bit  5(send pause frame)
+		u32 bp:1;		// bit  4(Issue half-duplex backpressure (XON/XOFF)
+		u32 cw:1;		// bit  3(Control word - no packet data)
+		u32 ir:1;		// bit  2(interrupt the processor when this pkt sent)
+		u32 f:1;		// bit  1(first packet in the sequence)
+		u32 l:1;		// bit  0(last packet in the sequence)
+#else
+		u32 l:1;		// bit  0(last packet in the sequence)
+		u32 f:1;		// bit  1(first packet in the sequence)
+		u32 ir:1;		// bit  2(interrupt the processor when this pkt sent)
+		u32 cw:1;		// bit  3(Control word - no packet data)
+		u32 bp:1;		// bit  4(Issue half-duplex backpressure (XON/XOFF)
+		u32 pf:1;		// bit  5(send pause frame)
+		u32 e:1;		// bit  6(Tx frame has error)
+		u32 crc:1;		// bit  7(append CRC)
+		u32 mac:1;		// bit  8(MAC override)
+		u32 pp:1;		// bit  9(pad packet)
+		u32 hp:1;		// bit 10(Packet is a Huge packet)
+		u32 vlan:1;	// bit 11(append VLAN tag)
+		u32 ipa:1;		// bit 12(IP checksum assist)
+		u32 tcpa:1;	// bit 13(TCP checksum assist)
+		u32 udpa:1;	// bit 14(UDP checksum assist)
+		u32 unused:17;	// bits 15-31
+#endif	/* _BIT_FIELDS_HTOL */
+	} bits;
+} TXDESC_WORD3_t, *PTXDESC_WORD3_t;
+
+/* TX_DESC_ENTRY_t is sructure representing each descriptor on the ring */
+typedef struct _tx_desc_entry_t {
+	u32 DataBufferPtrHigh;
+	u32 DataBufferPtrLow;
+	TXDESC_WORD2_t word2;	// control words how to xmit the
+	TXDESC_WORD3_t word3;	// data (detailed above)
+} TX_DESC_ENTRY_t, *PTX_DESC_ENTRY_t;
+
+
+/* Typedefs for Tx DMA engine status writeback */
+
+/*
+ * TX_STATUS_BLOCK_t is sructure representing the status of the Tx DMA engine
+ * it sits in free memory, and is pointed to by 0x101c / 0x1020
+ */
+typedef union _tx_status_block_t {
+	u32 value;
+	struct {
+#ifdef _BIT_FIELDS_HTOL
+		u32 unused:21;		// bits 11-31
+		u32 serv_cpl_wrap:1;	// bit 10
+		u32 serv_cpl:10;		// bits 0-9
+#else
+		u32 serv_cpl:10;		// bits 0-9
+		u32 serv_cpl_wrap:1;	// bit 10
+		u32 unused:21;		// bits 11-31
+#endif
+	} bits;
+} TX_STATUS_BLOCK_t, *PTX_STATUS_BLOCK_t;
+
+/* TCB (Transmit Control Block) */
+typedef struct _MP_TCB {
+	struct _MP_TCB *Next;
+	u32 Flags;
+	u32 Count;
+	u32 PacketStaleCount;
+	struct sk_buff *Packet;
+	u32 PacketLength;
+	DMA10W_t WrIndex;
+	DMA10W_t WrIndexStart;
+} MP_TCB, *PMP_TCB;
+
+/* Structure to hold the skb's in a list */
+typedef struct tx_skb_list_elem {
+	struct list_head skb_list_elem;
+	struct sk_buff *skb;
+} TX_SKB_LIST_ELEM, *PTX_SKB_LIST_ELEM;
+
+/* TX_RING_t is sructure representing our local reference(s) to the ring */
+typedef struct _tx_ring_t {
+	/* TCB (Transmit Control Block) memory and lists */
+	PMP_TCB MpTcbMem;
+
+	/* List of TCBs that are ready to be used */
+	PMP_TCB TCBReadyQueueHead;
+	PMP_TCB TCBReadyQueueTail;
+
+	/* list of TCBs that are currently being sent.  NOTE that access to all
+	 * three of these (including nBusySend) are controlled via the
+	 * TCBSendQLock.  This lock should be secured prior to incementing /
+	 * decrementing nBusySend, or any queue manipulation on CurrSendHead /
+	 * Tail
+	 */
+	PMP_TCB CurrSendHead;
+	PMP_TCB CurrSendTail;
+	int32_t nBusySend;
+
+	/* List of packets (not TCBs) that were queued for lack of resources */
+	struct list_head SendWaitQueue;
+	int32_t nWaitSend;
+
+	/* The actual descriptor ring */
+	PTX_DESC_ENTRY_t pTxDescRingVa;
+	dma_addr_t pTxDescRingPa;
+	uint64_t pTxDescRingAdjustedPa;
+	uint64_t TxDescOffset;
+
+	/* ReadyToSend indicates where we last wrote to in the descriptor ring. */
+	DMA10W_t txDmaReadyToSend;
+
+	/* The location of the write-back status block */
+	PTX_STATUS_BLOCK_t pTxStatusVa;
+	dma_addr_t pTxStatusPa;
+
+	/* A Block of zeroes used to pad packets that are less than 60 bytes */
+	void *pTxDummyBlkVa;
+	dma_addr_t pTxDummyBlkPa;
+
+	TXMAC_ERR_t TxMacErr;
+
+	/* Variables to track the Tx interrupt coalescing features */
+	int32_t TxPacketsSinceLastinterrupt;
+} TX_RING_t, *PTX_RING_t;
+
+/* Forward declaration of the frag-list for the following prototypes */
+typedef struct _MP_FRAG_LIST MP_FRAG_LIST, *PMP_FRAG_LIST;
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+/* PROTOTYPES for et1310_tx.c */
+int et131x_tx_dma_memory_alloc(struct et131x_adapter *adapter);
+void et131x_tx_dma_memory_free(struct et131x_adapter *adapter);
+void ConfigTxDmaRegs(struct et131x_adapter *pAdapter);
+void et131x_init_send(struct et131x_adapter *adapter);
+void et131x_tx_dma_disable(struct et131x_adapter *pAdapter);
+void et131x_tx_dma_enable(struct et131x_adapter *pAdapter);
+void et131x_handle_send_interrupt(struct et131x_adapter *pAdapter);
+void et131x_free_busy_send_packets(struct et131x_adapter *pAdapter);
+int et131x_send_packets(struct sk_buff *skb, struct net_device *netdev);
+
+#endif /* __ET1310_TX_H__ */
diff --git a/drivers/staging/et131x/et131x_adapter.h b/drivers/staging/et131x/et131x_adapter.h
new file mode 100644
index 0000000..36e61a4
--- /dev/null
+++ b/drivers/staging/et131x/et131x_adapter.h
@@ -0,0 +1,347 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_adapter.h - Header which includes the private adapter structure, along
+ *                    with related support structures, macros, definitions, etc.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_ADAPTER_H__
+#define __ET131X_ADAPTER_H__
+
+#include "et1310_address_map.h"
+#include "et1310_tx.h"
+#include "et1310_rx.h"
+
+/*
+ * Do not change these values: if changed, then change also in respective
+ * TXdma and Rxdma engines
+ */
+#define NUM_DESC_PER_RING_TX         512	// TX Do not change these values
+#define NUM_TCB                      64
+
+/*
+ * These values are all superseded by registry entries to facilitate tuning.
+ * Once the desired performance has been achieved, the optimal registry values
+ * should be re-populated to these #defines:
+ */
+#define NUM_TRAFFIC_CLASSES          1
+
+/*
+ * There are three ways of counting errors - if there are more than X errors
+ * in Y packets (represented by the "SAMPLE" macros), if there are more than
+ * N errors in a S mSec time period (the "PERIOD" macros), or if there are
+ * consecutive packets with errors (CONSEC_ERRORED_THRESH).  This last covers
+ * for "Bursty" errors, and the errored packets may well not be contiguous,
+ * but several errors where the packet counter has changed by less than a
+ * small amount will cause this count to increment.
+ */
+#define TX_PACKETS_IN_SAMPLE        10000
+#define TX_MAX_ERRORS_IN_SAMPLE     50
+
+#define TX_ERROR_PERIOD             1000
+#define TX_MAX_ERRORS_IN_PERIOD     10
+
+#define LINK_DETECTION_TIMER        5000
+
+#define TX_CONSEC_RANGE             5
+#define TX_CONSEC_ERRORED_THRESH    10
+
+#define LO_MARK_PERCENT_FOR_PSR     15
+#define LO_MARK_PERCENT_FOR_RX      15
+
+/* Macros for flag and ref count operations        */
+#define MP_SET_FLAG(_M, _F)         ((_M)->Flags |= (_F))
+#define MP_CLEAR_FLAG(_M, _F)       ((_M)->Flags &= ~(_F))
+#define MP_CLEAR_FLAGS(_M)          ((_M)->Flags = 0)
+#define MP_TEST_FLAG(_M, _F)        (((_M)->Flags & (_F)) != 0)
+#define MP_TEST_FLAGS(_M, _F)       (((_M)->Flags & (_F)) == (_F))
+#define MP_IS_FLAG_CLEAR(_M, _F)    (((_M)->Flags & (_F)) == 0)
+
+#define MP_INC_RCV_REF(_A)          atomic_inc(&(_A)->RcvRefCount)
+#define MP_DEC_RCV_REF(_A)          atomic_dec(&(_A)->RcvRefCount)
+#define MP_GET_RCV_REF(_A)          atomic_read(&(_A)->RcvRefCount)
+
+/* Macros specific to the private adapter structure */
+#define MP_TCB_RESOURCES_AVAILABLE(_M) ((_M)->TxRing.nBusySend < NUM_TCB)
+#define MP_TCB_RESOURCES_NOT_AVAILABLE(_M) ((_M)->TxRing.nBusySend >= NUM_TCB)
+
+#define MP_SHOULD_FAIL_SEND(_M)   ((_M)->Flags & fMP_ADAPTER_FAIL_SEND_MASK)
+#define MP_IS_NOT_READY(_M)       ((_M)->Flags & fMP_ADAPTER_NOT_READY_MASK)
+#define MP_IS_READY(_M)           !((_M)->Flags & fMP_ADAPTER_NOT_READY_MASK)
+
+#define MP_HAS_CABLE(_M)           !((_M)->Flags & fMP_ADAPTER_NO_CABLE)
+#define MP_LINK_DETECTED(_M)       !((_M)->Flags & fMP_ADAPTER_LINK_DETECTION)
+
+/* Counters for error rate monitoring */
+typedef struct _MP_ERR_COUNTERS {
+	u32 PktCountTxPackets;
+	u32 PktCountTxErrors;
+	u32 TimerBasedTxErrors;
+	u32 PktCountLastError;
+	u32 ErredConsecPackets;
+} MP_ERR_COUNTERS, *PMP_ERR_COUNTERS;
+
+/* RFD (Receive Frame Descriptor) */
+typedef struct _MP_RFD {
+	struct list_head list_node;
+	struct sk_buff *Packet;
+	u32 PacketSize;	// total size of receive frame
+	u16 iBufferIndex;
+	u8 iRingIndex;
+} MP_RFD, *PMP_RFD;
+
+/* Enum for Flow Control */
+typedef enum _eflow_control_t {
+	Both = 0,
+	TxOnly = 1,
+	RxOnly = 2,
+	None = 3
+} eFLOW_CONTROL_t, *PeFLOW_CONTROL_t;
+
+/* Struct to define some device statistics */
+typedef struct _ce_stats_t {
+	/* Link Input/Output stats */
+	uint64_t ipackets;	// # of in packets
+	uint64_t opackets;	// # of out packets
+
+	/* MIB II variables
+	 *
+	 * NOTE: atomic_t types are only guaranteed to store 24-bits; if we
+	 * MUST have 32, then we'll need another way to perform atomic
+	 * operations
+	 */
+	u32 unircv;	// # multicast packets received
+	atomic_t unixmt;	// # multicast packets for Tx
+	u32 multircv;	// # multicast packets received
+	atomic_t multixmt;	// # multicast packets for Tx
+	u32 brdcstrcv;	// # broadcast packets received
+	atomic_t brdcstxmt;	// # broadcast packets for Tx
+	u32 norcvbuf;	// # Rx packets discarded
+	u32 noxmtbuf;	// # Tx packets discarded
+
+	/* Transciever state informations. */
+	u8 xcvr_addr;
+	u32 xcvr_id;
+
+	/* Tx Statistics. */
+	u32 tx_uflo;		// Tx Underruns
+
+	u32 collisions;
+	u32 excessive_collisions;
+	u32 first_collision;
+	u32 late_collisions;
+	u32 max_pkt_error;
+	u32 tx_deferred;
+
+	/* Rx Statistics. */
+	u32 rx_ov_flow;	// Rx Over Flow
+
+	u32 length_err;
+	u32 alignment_err;
+	u32 crc_err;
+	u32 code_violations;
+	u32 other_errors;
+
+#ifdef CONFIG_ET131X_DEBUG
+	u32 UnhandledInterruptsPerSec;
+	u32 RxDmaInterruptsPerSec;
+	u32 TxDmaInterruptsPerSec;
+	u32 WatchDogInterruptsPerSec;
+#endif	/* CONFIG_ET131X_DEBUG */
+
+	u32 SynchrounousIterations;
+	INTERRUPT_t InterruptStatus;
+} CE_STATS_t, *PCE_STATS_t;
+
+/* The private adapter structure */
+struct et131x_adapter {
+	struct net_device *netdev;
+	struct pci_dev *pdev;
+
+	struct work_struct task;
+
+	/* Flags that indicate current state of the adapter */
+	u32 Flags;
+	u32 HwErrCount;
+
+	/* Configuration  */
+	u8 PermanentAddress[ETH_ALEN];
+	u8 CurrentAddress[ETH_ALEN];
+	bool bOverrideAddress;
+	bool bEepromPresent;
+	u8 eepromData[2];
+
+	/* Spinlocks */
+	spinlock_t Lock;
+
+	spinlock_t TCBSendQLock;
+	spinlock_t TCBReadyQLock;
+	spinlock_t SendHWLock;
+	spinlock_t SendWaitLock;
+
+	spinlock_t RcvLock;
+	spinlock_t RcvPendLock;
+	spinlock_t FbrLock;
+
+	spinlock_t PHYLock;
+
+	/* Packet Filter and look ahead size */
+	u32 PacketFilter;
+	u32 ulLookAhead;
+	u32 uiLinkSpeed;
+	u32 uiDuplexMode;
+	u32 uiAutoNegStatus;
+	u8 ucLinkStatus;
+
+	/* multicast list */
+	u32 MCAddressCount;
+	u8 MCList[NIC_MAX_MCAST_LIST][ETH_ALEN];
+
+	/* MAC test */
+	TXMAC_TXTEST_t TxMacTest;
+
+	/* Pointer to the device's PCI register space */
+	ADDRESS_MAP_t __iomem *CSRAddress;
+
+	/* PCI config space info, for debug purposes only. */
+	u8 RevisionID;
+	u16 VendorID;
+	u16 DeviceID;
+	u16 SubVendorID;
+	u16 SubSystemID;
+	u32 CacheFillSize;
+	u16 PciXDevCtl;
+	u8 pci_lat_timer;
+	u8 pci_hdr_type;
+	u8 pci_bist;
+	u32 pci_cfg_state[64 / sizeof(u32)];
+
+	/* Registry parameters */
+	u8 SpeedDuplex;		// speed/duplex
+	eFLOW_CONTROL_t RegistryFlowControl;	// for 802.3x flow control
+	u8 RegistryWOLMatch;	// Enable WOL pattern-matching
+	u8 RegistryWOLLink;	// Link state change is independant
+	u8 RegistryPhyComa;	// Phy Coma mode enable/disable
+
+	u32 RegistryRxMemEnd;	// Size of internal rx memory
+	u8 RegistryMACStat;	// If set, read MACSTAT, else don't
+	u32 RegistryVlanTag;	// 802.1q Vlan TAG
+	u32 RegistryJumboPacket;	// Max supported ethernet packet size
+
+	u32 RegistryTxNumBuffers;
+	u32 RegistryTxTimeInterval;
+
+	u32 RegistryRxNumBuffers;
+	u32 RegistryRxTimeInterval;
+
+	/* Validation helpers */
+	u8 RegistryPMWOL;
+	u8 RegistryNMIDisable;
+	u32 RegistryDMACache;
+	u32 RegistrySCGain;
+	u8 RegistryPhyLoopbk;	// Enable Phy loopback
+
+	/* Derived from the registry: */
+	u8 AiForceDpx;		// duplex setting
+	u16 AiForceSpeed;		// 'Speed', user over-ride of line speed
+	eFLOW_CONTROL_t FlowControl;	// flow control validated by the far-end
+	enum {
+		NETIF_STATUS_INVALID = 0,
+		NETIF_STATUS_MEDIA_CONNECT,
+		NETIF_STATUS_MEDIA_DISCONNECT,
+		NETIF_STATUS_MAX
+	} MediaState;
+	u8 DriverNoPhyAccess;
+
+	/* Minimize init-time */
+	bool bQueryPending;
+	bool bSetPending;
+	bool bResetPending;
+	struct timer_list ErrorTimer;
+	bool bLinkTimerActive;
+	MP_POWER_MGMT PoMgmt;
+	INTERRUPT_t CachedMaskValue;
+
+	atomic_t RcvRefCount;	// Num packets not yet returned
+
+	/* Xcvr status at last poll */
+	MI_BMSR_t Bmsr;
+
+	/* Tx Memory Variables */
+	TX_RING_t TxRing;
+
+	/* Rx Memory Variables */
+	RX_RING_t RxRing;
+
+	/* ET1310 register Access */
+	JAGCORE_ACCESS_REGS JagCoreRegs;
+	PCI_CFG_SPACE_REGS PciCfgRegs;
+
+	/* Loopback specifics */
+	u8 ReplicaPhyLoopbk;	// Replica Enable
+	u8 ReplicaPhyLoopbkPF;	// Replica Enable Pass/Fail
+
+	/* Stats */
+	CE_STATS_t Stats;
+
+	struct net_device_stats net_stats;
+	struct net_device_stats net_stats_prev;
+};
+
+#define MPSendPacketsHandler  MPSendPackets
+#define MP_FREE_SEND_PACKET_FUN(Adapter, pMpTcb) \
+	et131x_free_send_packet(Adapter, pMpTcb)
+#define MpSendPacketFun(Adapter, Packet) MpSendPacket(Adapter, Packet)
+
+#endif /* __ET131X_ADAPTER_H__ */
diff --git a/drivers/staging/et131x/et131x_config.c b/drivers/staging/et131x/et131x_config.c
new file mode 100644
index 0000000..0adbaa6
--- /dev/null
+++ b/drivers/staging/et131x/et131x_config.c
@@ -0,0 +1,325 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_config.c - Handles parsing of configuration data during
+ *                   initialization.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_initpci.h"
+#include "et131x_config.h"
+
+#include "et1310_tx.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+/* Defines for Parameter Default/Min/Max vaules */
+#define PARM_SPEED_DUPLEX_DEF   0
+#define PARM_SPEED_DUPLEX_MIN   0
+#define PARM_SPEED_DUPLEX_MAX   5
+
+#define PARM_VLAN_TAG_DEF       0
+#define PARM_VLAN_TAG_MIN       0
+#define PARM_VLAN_TAG_MAX       4095
+
+#define PARM_FLOW_CTL_DEF       0
+#define PARM_FLOW_CTL_MIN       0
+#define PARM_FLOW_CTL_MAX       3
+
+#define PARM_WOL_LINK_DEF       3
+#define PARM_WOL_LINK_MIN       0
+#define PARM_WOL_LINK_MAX       3
+
+#define PARM_WOL_MATCH_DEF      7
+#define PARM_WOL_MATCH_MIN      0
+#define PARM_WOL_MATCH_MAX      7
+
+#define PARM_JUMBO_PKT_DEF      1514
+#define PARM_JUMBO_PKT_MIN      1514
+#define PARM_JUMBO_PKT_MAX      9216
+
+#define PARM_PHY_COMA_DEF       0
+#define PARM_PHY_COMA_MIN       0
+#define PARM_PHY_COMA_MAX       1
+
+#define PARM_RX_NUM_BUFS_DEF    4
+#define PARM_RX_NUM_BUFS_MIN    1
+#define PARM_RX_NUM_BUFS_MAX    64
+
+#define PARM_RX_TIME_INT_DEF    10
+#define PARM_RX_TIME_INT_MIN    2
+#define PARM_RX_TIME_INT_MAX    320
+
+#define PARM_TX_NUM_BUFS_DEF    4
+#define PARM_TX_NUM_BUFS_MIN    1
+#define PARM_TX_NUM_BUFS_MAX    40
+
+#define PARM_TX_TIME_INT_DEF    40
+#define PARM_TX_TIME_INT_MIN    1
+#define PARM_TX_TIME_INT_MAX    140
+
+#define PARM_RX_MEM_END_DEF     0x2bc
+#define PARM_RX_MEM_END_MIN     0
+#define PARM_RX_MEM_END_MAX     0x3ff
+
+#define PARM_MAC_STAT_DEF       1
+#define PARM_MAC_STAT_MIN       0
+#define PARM_MAC_STAT_MAX       1
+
+#define PARM_SC_GAIN_DEF        7
+#define PARM_SC_GAIN_MIN        0
+#define PARM_SC_GAIN_MAX        7
+
+#define PARM_PM_WOL_DEF         0
+#define PARM_PM_WOL_MIN         0
+#define PARM_PM_WOL_MAX         1
+
+#define PARM_NMI_DISABLE_DEF    0
+#define PARM_NMI_DISABLE_MIN    0
+#define PARM_NMI_DISABLE_MAX    2
+
+#define PARM_DMA_CACHE_DEF      0
+#define PARM_DMA_CACHE_MIN      0
+#define PARM_DMA_CACHE_MAX      15
+
+#define PARM_PHY_LOOPBK_DEF     0
+#define PARM_PHY_LOOPBK_MIN     0
+#define PARM_PHY_LOOPBK_MAX     1
+
+#define PARM_MAC_ADDRESS_DEF    { 0x00, 0x05, 0x3d, 0x00, 0x02, 0x00 }
+
+/* Module parameter for disabling NMI
+ * et131x_speed_set :
+ * Set Link speed and dublex manually (0-5)  [0]
+ *  1 : 10Mb   Half-Duplex
+ *  2 : 10Mb   Full-Duplex
+ *  3 : 100Mb  Half-Duplex
+ *  4 : 100Mb  Full-Duplex
+ *  5 : 1000Mb Full-Duplex
+ *  0 : Auto Speed Auto Dublex // default
+ */
+static u32 et131x_nmi_disable = PARM_NMI_DISABLE_DEF;
+module_param(et131x_nmi_disable, uint, 0);
+MODULE_PARM_DESC(et131x_nmi_disable, "Disable NMI (0-2) [0]");
+
+/* Module parameter for manual speed setting
+ * et131x_nmi_disable :
+ * Disable NMI (0-2) [0]
+ *  0 :
+ *  1 :
+ *  2 :
+ */
+static u32 et131x_speed_set = PARM_SPEED_DUPLEX_DEF;
+module_param(et131x_speed_set, uint, 0);
+MODULE_PARM_DESC(et131x_speed_set,
+		 "Set Link speed and dublex manually (0-5)  [0] \n  1 : 10Mb   Half-Duplex \n  2 : 10Mb   Full-Duplex \n  3 : 100Mb  Half-Duplex \n  4 : 100Mb  Full-Duplex \n  5 : 1000Mb Full-Duplex \n 0 : Auto Speed Auto Dublex");
+
+/**
+ * et131x_config_parse
+ * @pAdapter: pointer to the private adapter struct
+ *
+ * Parses a configuration from some location (module parameters, for example)
+ * into the private adapter struct
+ */
+void et131x_config_parse(struct et131x_adapter *pAdapter)
+{
+	uint8_t macAddrDef[] = PARM_MAC_ADDRESS_DEF;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/*
+	 * The NDIS driver uses the registry to store persistent per-device
+	 * configuration, and reads this configuration into the appropriate
+	 * elements of the private adapter structure on initialization.
+	 * Because Linux has no analog to the registry, use this function to
+	 * initialize the private adapter structure with a default
+	 * configuration.
+	 *
+	 * One other possibility is to use a series of module parameters which
+	 * can be passed in by the caller when the module is initialized.
+	 * However, this implementation does not allow for seperate
+	 * configurations in the event multiple devices are present, and hence
+	 * will not suffice.
+	 *
+	 * If another method is derived which addresses this problem, this is
+	 * where it should be implemented.
+	 */
+
+	 /* Set the private adapter struct with default values for the
+	  * corresponding parameters
+	  */
+	if (et131x_speed_set != PARM_SPEED_DUPLEX_DEF) {
+		DBG_VERBOSE(et131x_dbginfo, "Speed set manually to : %d \n",
+			    et131x_speed_set);
+		pAdapter->SpeedDuplex = et131x_speed_set;
+	} else {
+		pAdapter->SpeedDuplex = PARM_SPEED_DUPLEX_DEF;
+	}
+
+	//  pAdapter->SpeedDuplex            = PARM_SPEED_DUPLEX_DEF;
+
+	pAdapter->RegistryVlanTag = PARM_VLAN_TAG_DEF;
+	pAdapter->RegistryFlowControl = PARM_FLOW_CTL_DEF;
+	pAdapter->RegistryWOLLink = PARM_WOL_LINK_DEF;
+	pAdapter->RegistryWOLMatch = PARM_WOL_MATCH_DEF;
+	pAdapter->RegistryJumboPacket = PARM_JUMBO_PKT_DEF;
+	pAdapter->RegistryPhyComa = PARM_PHY_COMA_DEF;
+	pAdapter->RegistryRxNumBuffers = PARM_RX_NUM_BUFS_DEF;
+	pAdapter->RegistryRxTimeInterval = PARM_RX_TIME_INT_DEF;
+	pAdapter->RegistryTxNumBuffers = PARM_TX_NUM_BUFS_DEF;
+	pAdapter->RegistryTxTimeInterval = PARM_TX_TIME_INT_DEF;
+	pAdapter->RegistryRxMemEnd = PARM_RX_MEM_END_DEF;
+	pAdapter->RegistryMACStat = PARM_MAC_STAT_DEF;
+	pAdapter->RegistrySCGain = PARM_SC_GAIN_DEF;
+	pAdapter->RegistryPMWOL = PARM_PM_WOL_DEF;
+
+	if (et131x_nmi_disable != PARM_NMI_DISABLE_DEF) {
+		pAdapter->RegistryNMIDisable = et131x_nmi_disable;
+	} else {
+		pAdapter->RegistryNMIDisable = PARM_NMI_DISABLE_DEF;
+	}
+
+	pAdapter->RegistryDMACache = PARM_DMA_CACHE_DEF;
+	pAdapter->RegistryPhyLoopbk = PARM_PHY_LOOPBK_DEF;
+
+	/* Set the MAC address to a default */
+	memcpy(pAdapter->CurrentAddress, macAddrDef, ETH_ALEN);
+	pAdapter->bOverrideAddress = false;
+
+	DBG_TRACE(et131x_dbginfo,
+		  "Default MAC Address  : %02x:%02x:%02x:%02x:%02x:%02x\n",
+		  pAdapter->CurrentAddress[0], pAdapter->CurrentAddress[1],
+		  pAdapter->CurrentAddress[2], pAdapter->CurrentAddress[3],
+		  pAdapter->CurrentAddress[4], pAdapter->CurrentAddress[5]);
+
+	/* Decode SpeedDuplex
+	 *
+	 * Set up as if we are auto negotiating always and then change if we
+	 * go into force mode
+	 */
+	pAdapter->AiForceSpeed = 0;	// Auto speed
+	pAdapter->AiForceDpx = 0;	// Auto FDX
+
+	/* If we are the 10/100 device, and gigabit is somehow requested then
+	 * knock it down to 100 full.
+	 */
+	if ((pAdapter->DeviceID == ET131X_PCI_DEVICE_ID_FAST) &&
+	    (pAdapter->SpeedDuplex == 5)) {
+		pAdapter->SpeedDuplex = 4;
+	}
+
+	switch (pAdapter->SpeedDuplex) {
+	case 1:		// 10Mb   Half-Duplex
+		pAdapter->AiForceSpeed = 10;
+		pAdapter->AiForceDpx = 1;
+		break;
+
+	case 2:		// 10Mb   Full-Duplex
+		pAdapter->AiForceSpeed = 10;
+		pAdapter->AiForceDpx = 2;
+		break;
+
+	case 3:		// 100Mb  Half-Duplex
+		pAdapter->AiForceSpeed = 100;
+		pAdapter->AiForceDpx = 1;
+		break;
+
+	case 4:		// 100Mb  Full-Duplex
+		pAdapter->AiForceSpeed = 100;
+		pAdapter->AiForceDpx = 2;
+		break;
+
+	case 5:		// 1000Mb Full-Duplex
+		pAdapter->AiForceSpeed = 1000;
+		pAdapter->AiForceDpx = 2;
+		break;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
diff --git a/drivers/staging/et131x/et131x_config.h b/drivers/staging/et131x/et131x_config.h
new file mode 100644
index 0000000..642c0f6
--- /dev/null
+++ b/drivers/staging/et131x/et131x_config.h
@@ -0,0 +1,67 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_config.h - Defines, structs, enums, prototypes, etc. to support
+ *                   et131x_config.c
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_CONFIG_H__
+#define __ET131X_CONFIG_H__
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+void et131x_config_parse(struct et131x_adapter *adapter);
+
+#endif /* __ET131X_CONFIG_H__ */
diff --git a/drivers/staging/et131x/et131x_debug.c b/drivers/staging/et131x/et131x_debug.c
new file mode 100644
index 0000000..9ee5bce
--- /dev/null
+++ b/drivers/staging/et131x/et131x_debug.c
@@ -0,0 +1,218 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_debug.c - Routines used for debugging.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifdef CONFIG_ET131X_DEBUG
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/random.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_netdev.h"
+#include "et131x_config.h"
+#include "et131x_isr.h"
+
+#include "et1310_address_map.h"
+#include "et1310_jagcore.h"
+#include "et1310_tx.h"
+#include "et1310_rx.h"
+#include "et1310_mac.h"
+
+/* Data for debugging facilities */
+extern dbg_info_t *et131x_dbginfo;
+
+/**
+ * DumpTxQueueContents - Dump out the tx queue and the shadow pointers
+ * @pAdapter: pointer to our adapter structure
+ */
+void DumpTxQueueContents(int dbgLvl, struct et131x_adapter *pAdapter)
+{
+	MMC_t __iomem *mmc = &pAdapter->CSRAddress->mmc;
+	uint32_t TxQueueAddr;
+
+	if (DBG_FLAGS(et131x_dbginfo) & dbgLvl) {
+		for (TxQueueAddr = 0x200; TxQueueAddr < 0x3ff; TxQueueAddr++) {
+			MMC_SRAM_ACCESS_t sram_access;
+
+			sram_access.value = readl(&mmc->sram_access.value);
+			sram_access.bits.req_addr = TxQueueAddr;
+			sram_access.bits.req_access = 1;
+			writel(sram_access.value, &mmc->sram_access.value);
+
+			DBG_PRINT("Addr 0x%x, Access 0x%08x\t"
+				  "Value 1 0x%08x, Value 2 0x%08x, "
+				  "Value 3 0x%08x, Value 4 0x%08x, \n",
+				  TxQueueAddr,
+				  readl(&mmc->sram_access.value),
+				  readl(&mmc->sram_word1),
+				  readl(&mmc->sram_word2),
+				  readl(&mmc->sram_word3),
+				  readl(&mmc->sram_word4));
+		}
+
+		DBG_PRINT("Shadow Pointers 0x%08x\n",
+			  readl(&pAdapter->CSRAddress->txmac.shadow_ptr.value));
+	}
+}
+
+/**
+ * DumpDeviceBlock
+ * @pAdapter: pointer to our adapter
+ *
+ * Dumps the first 64 regs of each block of the et-1310 (each block is
+ * mapped to a new page, each page is 4096 bytes).
+ */
+#define NUM_BLOCKS 8
+void DumpDeviceBlock(int dbgLvl, struct et131x_adapter *pAdapter,
+		     uint32_t Block)
+{
+	uint32_t Address1, Address2;
+	uint32_t __iomem *BigDevicePointer =
+		(uint32_t __iomem *) pAdapter->CSRAddress;
+	const char *BlockNames[NUM_BLOCKS] = {
+		"Global", "Tx DMA", "Rx DMA", "Tx MAC",
+		"Rx MAC", "MAC", "MAC Stat", "MMC"
+	};
+
+	/* Output the debug counters to the debug terminal */
+	if (DBG_FLAGS(et131x_dbginfo) & dbgLvl) {
+		DBG_PRINT("%s block\n", BlockNames[Block]);
+		BigDevicePointer += Block * 1024;
+		for (Address1 = 0; Address1 < 8; Address1++) {
+			for (Address2 = 0; Address2 < 8; Address2++) {
+				if (Block == 0 &&
+				    (Address1 * 8 + Address2) == 6) {
+					DBG_PRINT("  ISR    , ");
+				} else {
+					DBG_PRINT("0x%08x, ",
+						  readl(BigDevicePointer++));
+				}
+			}
+			DBG_PRINT("\n");
+		}
+		DBG_PRINT("\n");
+	}
+}
+
+/**
+ * DumpDeviceReg
+ * @pAdapter: pointer to our adapter
+ *
+ * Dumps the first 64 regs of each block of the et-1310 (each block is
+ * mapped to a new page, each page is 4096 bytes).
+ */
+void DumpDeviceReg(int dbgLvl, struct et131x_adapter *pAdapter)
+{
+	uint32_t Address1, Address2;
+	uint32_t Block;
+	uint32_t __iomem *BigDevicePointer =
+		(uint32_t __iomem *) pAdapter->CSRAddress;
+	uint32_t __iomem *Pointer;
+	const char *BlockNames[NUM_BLOCKS] = {
+		"Global", "Tx DMA", "Rx DMA", "Tx MAC",
+		"Rx MAC", "MAC", "MAC Stat", "MMC"
+	};
+
+	/* Output the debug counters to the debug terminal */
+	if (DBG_FLAGS(et131x_dbginfo) & dbgLvl) {
+		for (Block = 0; Block < NUM_BLOCKS; Block++) {
+			DBG_PRINT("%s block\n", BlockNames[Block]);
+			Pointer = BigDevicePointer + (Block * 1024);
+
+			for (Address1 = 0; Address1 < 8; Address1++) {
+				for (Address2 = 0; Address2 < 8; Address2++) {
+					DBG_PRINT("0x%08x, ",
+						  readl(Pointer++));
+				}
+				DBG_PRINT("\n");
+			}
+			DBG_PRINT("\n");
+		}
+	}
+}
+
+#endif // CONFIG_ET131X_DEBUG
diff --git a/drivers/staging/et131x/et131x_debug.h b/drivers/staging/et131x/et131x_debug.h
new file mode 100644
index 0000000..dab6080
--- /dev/null
+++ b/drivers/staging/et131x/et131x_debug.h
@@ -0,0 +1,201 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_debug.h - Defines, structs, enums, prototypes, etc. used for
+ *                  outputting debug messages to the system logging facility
+ *                  (ksyslogd)
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_DBG_H__
+#define __ET131X_DBG_H__
+
+/* Define Masks for debugging types/levels */
+#define DBG_ERROR_ON        0x00000001L
+#define DBG_WARNING_ON      0x00000002L
+#define DBG_NOTICE_ON       0x00000004L
+#define DBG_TRACE_ON        0x00000008L
+#define DBG_VERBOSE_ON      0x00000010L
+#define DBG_PARAM_ON        0x00000020L
+#define DBG_BREAK_ON        0x00000040L
+#define DBG_RX_ON           0x00000100L
+#define DBG_TX_ON           0x00000200L
+
+#ifdef CONFIG_ET131X_DEBUG
+
+/*
+ * Set the level of debugging if not done with a preprocessor define. See
+ * et131x_main.c, function et131x_init_module() for how the debug level
+ * translates into the types of messages displayed.
+ */
+#ifndef DBG_LVL
+#define DBG_LVL	3
+#endif /* DBG_LVL */
+
+#define DBG_DEFAULTS		(DBG_ERROR_ON | DBG_WARNING_ON | DBG_BREAK_ON )
+
+#define DBG_FLAGS(A)		(A)->dbgFlags
+#define DBG_NAME(A)		(A)->dbgName
+#define DBG_LEVEL(A)		(A)->dbgLevel
+
+#ifndef DBG_PRINT
+#define DBG_PRINT(S...)		printk(KERN_DEBUG S)
+#endif /* DBG_PRINT */
+
+#ifndef DBG_PRINTC
+#define DBG_PRINTC(S...)	printk(S)
+#endif /* DBG_PRINTC */
+
+#ifndef DBG_TRAP
+#define DBG_TRAP		{}	/* BUG() */
+#endif /* DBG_TRAP */
+
+#define _ENTER_STR	">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
+#define _LEAVE_STR	"<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
+
+#define _DBG_ENTER(A)	printk(KERN_DEBUG "%s:%.*s:%s\n", DBG_NAME(A),	\
+				++DBG_LEVEL(A), _ENTER_STR, __func__)
+#define _DBG_LEAVE(A)	printk(KERN_DEBUG "%s:%.*s:%s\n", DBG_NAME(A),	\
+				DBG_LEVEL(A)--, _LEAVE_STR, __func__)
+
+#define DBG_ENTER(A)        {if (DBG_FLAGS(A) & DBG_TRACE_ON) \
+                                _DBG_ENTER(A);}
+
+#define DBG_LEAVE(A)        {if (DBG_FLAGS(A) & DBG_TRACE_ON) \
+                                _DBG_LEAVE(A);}
+
+#define DBG_PARAM(A,N,F,S...)   {if (DBG_FLAGS(A) & DBG_PARAM_ON) \
+                                    DBG_PRINT("  %s -- "F"\n",N,S);}
+
+#define DBG_ERROR(A,S...)	\
+	if (DBG_FLAGS(A) & DBG_ERROR_ON) {				\
+		DBG_PRINT("%s:ERROR:%s ",DBG_NAME(A), __func__);	\
+		DBG_PRINTC(S);						\
+		DBG_TRAP;						\
+	}
+
+#define DBG_WARNING(A,S...) {if (DBG_FLAGS(A) & DBG_WARNING_ON) \
+                                {DBG_PRINT("%s:WARNING:%s ",DBG_NAME(A),__func__);DBG_PRINTC(S);}}
+
+#define DBG_NOTICE(A,S...)  {if (DBG_FLAGS(A) & DBG_NOTICE_ON) \
+                                {DBG_PRINT("%s:NOTICE:%s ",DBG_NAME(A),__func__);DBG_PRINTC(S);}}
+
+#define DBG_TRACE(A,S...)   {if (DBG_FLAGS(A) & DBG_TRACE_ON) \
+                                {DBG_PRINT("%s:TRACE:%s ",DBG_NAME(A), __func__);DBG_PRINTC(S);}}
+
+#define DBG_VERBOSE(A,S...) {if (DBG_FLAGS(A) & DBG_VERBOSE_ON) \
+                                {DBG_PRINT("%s:VERBOSE:%s ",DBG_NAME(A), __func__);DBG_PRINTC(S);}}
+
+#define DBG_RX(A,S...)      {if (DBG_FLAGS(A) & DBG_RX_ON) \
+                                {DBG_PRINT(S);}}
+
+#define DBG_RX_ENTER(A)     {if (DBG_FLAGS(A) & DBG_RX_ON) \
+                                _DBG_ENTER(A);}
+
+#define DBG_RX_LEAVE(A)     {if (DBG_FLAGS(A) & DBG_RX_ON) \
+                                _DBG_LEAVE(A);}
+
+#define DBG_TX(A,S...)      {if (DBG_FLAGS(A) & DBG_TX_ON) \
+                                {DBG_PRINT(S);}}
+
+#define DBG_TX_ENTER(A)     {if (DBG_FLAGS(A) & DBG_TX_ON) \
+                                _DBG_ENTER(A);}
+
+#define DBG_TX_LEAVE(A)     {if (DBG_FLAGS(A) & DBG_TX_ON) \
+                                _DBG_LEAVE(A);}
+
+#define DBG_ASSERT(C)       {if (!(C)) \
+                                {DBG_PRINT("ASSERT(%s) -- %s#%d (%s)\n", \
+                                    #C,__FILE__,__LINE__,__func__); \
+                                DBG_TRAP;}}
+#define STATIC
+
+typedef struct {
+	char *dbgName;
+	int dbgLevel;
+	unsigned long dbgFlags;
+} dbg_info_t;
+
+#else /* CONFIG_ET131X_DEBUG */
+
+#define DBG_DEFN
+#define DBG_TRAP
+#define DBG_PRINT(S...)
+#define DBG_ENTER(A)
+#define DBG_LEAVE(A)
+#define DBG_PARAM(A,N,F,S...)
+#define DBG_ERROR(A,S...)
+#define DBG_WARNING(A,S...)
+#define DBG_NOTICE(A,S...)
+#define DBG_TRACE(A,S...)
+#define DBG_VERBOSE(A,S...)
+#define DBG_RX(A,S...)
+#define DBG_RX_ENTER(A)
+#define DBG_RX_LEAVE(A)
+#define DBG_TX(A,S...)
+#define DBG_TX_ENTER(A)
+#define DBG_TX_LEAVE(A)
+#define DBG_ASSERT(C)
+#define STATIC static
+
+#endif /* CONFIG_ET131X_DEBUG */
+
+/* Forward declaration of the private adapter structure */
+struct et131x_adapter;
+
+void DumpTxQueueContents(int dbgLvl, struct et131x_adapter *adapter);
+void DumpDeviceBlock(int dbgLvl, struct et131x_adapter *adapter,
+		     unsigned int Block);
+void DumpDeviceReg(int dbgLvl, struct et131x_adapter *adapter);
+
+#endif /* __ET131X_DBG_H__ */
diff --git a/drivers/staging/et131x/et131x_defs.h b/drivers/staging/et131x/et131x_defs.h
new file mode 100644
index 0000000..886cb78
--- /dev/null
+++ b/drivers/staging/et131x/et131x_defs.h
@@ -0,0 +1,128 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_defs.h - Defines, structs, enums, prototypes, etc. to assist with OS
+ *                 compatibility
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_DEFS_H__
+#define __ET131X_DEFS_H__
+
+/* Packet and header sizes */
+#define NIC_MIN_PACKET_SIZE	60
+#define NIC_HEADER_SIZE		ETH_HLEN	/* 14 */
+
+/* Multicast list size */
+#define NIC_MAX_MCAST_LIST	128
+
+/* Supported Filters */
+#define ET131X_PACKET_TYPE_DIRECTED		0x0001
+#define ET131X_PACKET_TYPE_MULTICAST		0x0002
+#define ET131X_PACKET_TYPE_BROADCAST		0x0004
+#define ET131X_PACKET_TYPE_PROMISCUOUS		0x0008
+#define ET131X_PACKET_TYPE_ALL_MULTICAST	0x0010
+
+/* Tx Timeout */
+#define ET131X_TX_TIMEOUT	(1 * HZ)
+#define NIC_SEND_HANG_THRESHOLD	0
+
+/* MP_TCB flags */
+#define fMP_DEST_MULTI			0x00000001
+#define fMP_DEST_BROAD			0x00000002
+
+/* MP_ADAPTER flags */
+#define fMP_ADAPTER_RECV_LOOKASIDE	0x00000004
+#define fMP_ADAPTER_INTERRUPT_IN_USE	0x00000008
+#define fMP_ADAPTER_SECONDARY		0x00000010
+
+/* MP_SHARED flags */
+#define fMP_ADAPTER_SHUTDOWN		0x00100000
+#define fMP_ADAPTER_LOWER_POWER		0x00200000
+
+#define fMP_ADAPTER_NON_RECOVER_ERROR	0x00800000
+#define fMP_ADAPTER_RESET_IN_PROGRESS	0x01000000
+#define fMP_ADAPTER_NO_CABLE		0x02000000
+#define fMP_ADAPTER_HARDWARE_ERROR	0x04000000
+#define fMP_ADAPTER_REMOVE_IN_PROGRESS	0x08000000
+#define fMP_ADAPTER_HALT_IN_PROGRESS	0x10000000
+#define fMP_ADAPTER_LINK_DETECTION	0x20000000
+
+#define fMP_ADAPTER_FAIL_SEND_MASK	0x3ff00000
+#define fMP_ADAPTER_NOT_READY_MASK	0x3ff00000
+
+/* Some offsets in PCI config space that are actually used. */
+#define ET1310_PCI_PM_CAPABILITY	0x40
+#define ET1310_PCI_PM_CSR		0x44
+#define ET1310_PCI_MAX_PYLD		0x4C
+#define ET1310_PCI_DEV_CTRL		0x50
+#define ET1310_PCI_DEV_STAT		0x52
+#define ET1310_NMI_DISABLE		0x61
+#define ET1310_PCI_MAC_ADDRESS		0xA4
+#define ET1310_PCI_EEPROM_STATUS	0xB2
+#define ET1310_PCI_PHY_INDEX_REG	0xB4
+#define ET1310_PCI_ACK_NACK		0xC0
+#define ET1310_PCI_REPLAY		0xC2
+#define ET1310_PCI_L0L1LATENCY		0xCF
+#define ET1310_PCI_SEL_PHY_CTRL		0xE4
+#define ET1310_PCI_ADVANCED_ERR		0x100
+
+/* PCI Vendor/Product IDs */
+#define ET131X_PCI_VENDOR_ID		0x11C1	// Agere Systems
+#define ET131X_PCI_DEVICE_ID_GIG	0xED00	// ET1310 1000 Base-T
+#define ET131X_PCI_DEVICE_ID_FAST	0xED01	// ET1310 100  Base-T
+
+/* Define order of magnitude converter */
+#define NANO_IN_A_MICRO	1000
+
+#endif /* __ET131X_DEFS_H__ */
diff --git a/drivers/staging/et131x/et131x_initpci.c b/drivers/staging/et131x/et131x_initpci.c
new file mode 100644
index 0000000..4c6f171
--- /dev/null
+++ b/drivers/staging/et131x/et131x_initpci.c
@@ -0,0 +1,1046 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_initpci.c - Routines and data used to register the driver with the
+ *                    PCI (and PCI Express) subsystem, as well as basic driver
+ *                    init and startup.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+#include <linux/random.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+
+#include "et131x_adapter.h"
+#include "et131x_netdev.h"
+#include "et131x_config.h"
+#include "et131x_isr.h"
+
+#include "et1310_address_map.h"
+#include "et1310_jagcore.h"
+#include "et1310_tx.h"
+#include "et1310_rx.h"
+#include "et1310_mac.h"
+#include "et1310_eeprom.h"
+
+
+int __devinit et131x_pci_setup(struct pci_dev *pdev,
+			       const struct pci_device_id *ent);
+void __devexit et131x_pci_remove(struct pci_dev *pdev);
+
+
+/* Modinfo parameters (filled out using defines from et131x_version.h) */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_INFO);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+/* Module Parameters and related data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+static u32 et131x_debug_level = DBG_LVL;
+static u32 et131x_debug_flags = DBG_DEFAULTS;
+
+/*
+et131x_debug_level :
+ Level of debugging desired (0-7)
+   7 : DBG_RX_ON | DBG_TX_ON
+   6 : DBG_PARAM_ON
+   5 : DBG_VERBOSE_ON
+   4 : DBG_TRACE_ON
+   3 : DBG_NOTICE_ON
+   2 : no debug info
+   1 : no debug info
+   0 : no debug info
+*/
+
+module_param(et131x_debug_level, uint, 0);
+module_param(et131x_debug_flags, uint, 0);
+
+MODULE_PARM_DESC(et131x_debug_level, "Level of debugging desired (0-7)");
+
+static dbg_info_t et131x_info = { DRIVER_NAME_EXT, 0, 0 };
+dbg_info_t *et131x_dbginfo = &et131x_info;
+#endif /* CONFIG_ET131X_DEBUG */
+
+static struct pci_device_id et131x_pci_table[] __devinitdata = {
+	{ET131X_PCI_VENDOR_ID, ET131X_PCI_DEVICE_ID_GIG, PCI_ANY_ID,
+	 PCI_ANY_ID, 0, 0, 0UL},
+	{ET131X_PCI_VENDOR_ID, ET131X_PCI_DEVICE_ID_FAST, PCI_ANY_ID,
+	 PCI_ANY_ID, 0, 0, 0UL},
+	{0,}
+};
+
+MODULE_DEVICE_TABLE(pci, et131x_pci_table);
+
+static struct pci_driver et131x_driver = {
+      .name	= DRIVER_NAME,
+      .id_table	= et131x_pci_table,
+      .probe	= et131x_pci_setup,
+      .remove	= __devexit_p(et131x_pci_remove),
+      .suspend	= NULL,		//et131x_pci_suspend,
+      .resume	= NULL,		//et131x_pci_resume,
+};
+
+
+/**
+ * et131x_init_module - The "main" entry point called on driver initialization
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_init_module(void)
+{
+	int result;
+
+#ifdef CONFIG_ET131X_DEBUG
+	/* Set the level of debug messages displayed using the module
+	 * parameter
+	 */
+	et131x_dbginfo->dbgFlags = et131x_debug_flags;
+
+	switch (et131x_debug_level) {
+	case 7:
+		et131x_dbginfo->dbgFlags |= (DBG_RX_ON | DBG_TX_ON);
+
+	case 6:
+		et131x_dbginfo->dbgFlags |= DBG_PARAM_ON;
+
+	case 5:
+		et131x_dbginfo->dbgFlags |= DBG_VERBOSE_ON;
+
+	case 4:
+		et131x_dbginfo->dbgFlags |= DBG_TRACE_ON;
+
+	case 3:
+		et131x_dbginfo->dbgFlags |= DBG_NOTICE_ON;
+
+	case 2:
+	case 1:
+	case 0:
+	default:
+		break;
+	}
+#endif /* CONFIG_ET131X_DEBUG */
+
+	DBG_ENTER(et131x_dbginfo);
+	DBG_PRINT("%s\n", DRIVER_INFO);
+
+	result = pci_register_driver(&et131x_driver);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return result;
+}
+
+/**
+ * et131x_cleanup_module - The entry point called on driver cleanup
+ */
+void et131x_cleanup_module(void)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	pci_unregister_driver(&et131x_driver);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/*
+ * These macros map the driver-specific init_module() and cleanup_module()
+ * routines so they can be called by the kernel.
+ */
+module_init(et131x_init_module);
+module_exit(et131x_cleanup_module);
+
+
+/**
+ * et131x_find_adapter - Find the adapter and get all the assigned resources
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_find_adapter(struct et131x_adapter *adapter, struct pci_dev *pdev)
+{
+	int result;
+	uint8_t eepromStat;
+	uint8_t maxPayload = 0;
+	uint8_t read_size_reg;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Allow disabling of Non-Maskable Interrupts in I/O space, to
+	 * support validation.
+	 */
+	if (adapter->RegistryNMIDisable) {
+		uint8_t RegisterVal;
+
+		RegisterVal = inb(ET1310_NMI_DISABLE);
+		RegisterVal &= 0xf3;
+
+		if (adapter->RegistryNMIDisable == 2) {
+			RegisterVal |= 0xc;
+		}
+
+		outb(ET1310_NMI_DISABLE, RegisterVal);
+	}
+
+	/* We first need to check the EEPROM Status code located at offset
+	 * 0xB2 of config space
+	 */
+	result = pci_read_config_byte(pdev, ET1310_PCI_EEPROM_STATUS,
+				      &eepromStat);
+
+	/* THIS IS A WORKAROUND:
+ 	 * I need to call this function twice to get my card in a
+	 * LG M1 Express Dual running. I tried also a msleep before this
+	 * function, because I thougth there could be some time condidions
+	 * but it didn't work. Call the whole function twice also work.
+	 */
+	result = pci_read_config_byte(pdev, ET1310_PCI_EEPROM_STATUS,
+				      &eepromStat);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		DBG_ERROR(et131x_dbginfo, "Could not read PCI config space for "
+			  "EEPROM Status\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	/* Determine if the error(s) we care about are present.  If they are
+	 * present, we need to fail.
+	 */
+	if (eepromStat & 0x4C) {
+		result = pci_read_config_byte(pdev, PCI_REVISION_ID,
+					      &adapter->RevisionID);
+		if (result != PCIBIOS_SUCCESSFUL) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Could not read PCI config space for "
+				  "Revision ID\n");
+			DBG_LEAVE(et131x_dbginfo);
+			return -EIO;
+		} else if (adapter->RevisionID == 0x01) {
+			int32_t nLoop;
+			uint8_t ucTemp[4] = { 0xFE, 0x13, 0x10, 0xFF };
+
+			/* Re-write the first 4 bytes if we have an eeprom
+			 * present and the revision id is 1, this fixes the
+			 * corruption seen with 1310 B Silicon
+			 */
+			for (nLoop = 0; nLoop < 3; nLoop++) {
+				EepromWriteByte(adapter, nLoop, ucTemp[nLoop],
+						0, SINGLE_BYTE);
+			}
+		}
+
+		DBG_ERROR(et131x_dbginfo,
+			  "Fatal EEPROM Status Error - 0x%04x\n", eepromStat);
+
+		/* This error could mean that there was an error reading the
+		 * eeprom or that the eeprom doesn't exist.  We will treat
+		 * each case the same and not try to gather additional
+		 * information that normally would come from the eeprom, like
+		 * MAC Address
+		 */
+		adapter->bEepromPresent = false;
+
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	} else {
+		DBG_TRACE(et131x_dbginfo, "EEPROM Status Code - 0x%04x\n",
+			  eepromStat);
+		adapter->bEepromPresent = true;
+	}
+
+	/* Read the EEPROM for information regarding LED behavior. Refer to
+	 * ET1310_phy.c, et131x_xcvr_init(), for its use.
+	 */
+	EepromReadByte(adapter, 0x70, &adapter->eepromData[0], 0, SINGLE_BYTE);
+	EepromReadByte(adapter, 0x71, &adapter->eepromData[1], 0, SINGLE_BYTE);
+
+	if (adapter->eepromData[0] != 0xcd) {
+		adapter->eepromData[1] = 0x00;	// Disable all optional features
+	}
+
+	/* Let's set up the PORT LOGIC Register.  First we need to know what
+	 * the max_payload_size is
+	 */
+	result = pci_read_config_byte(pdev, ET1310_PCI_MAX_PYLD, &maxPayload);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		DBG_ERROR(et131x_dbginfo, "Could not read PCI config space for "
+			  "Max Payload Size\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	/* Program the Ack/Nak latency and replay timers */
+	maxPayload &= 0x07;	// Only the lower 3 bits are valid
+
+	if (maxPayload < 2) {
+		const uint16_t AckNak[2] = { 0x76, 0xD0 };
+		const uint16_t Replay[2] = { 0x1E0, 0x2ED };
+
+		result = pci_write_config_word(pdev, ET1310_PCI_ACK_NACK,
+					       AckNak[maxPayload]);
+		if (result != PCIBIOS_SUCCESSFUL) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Could not write PCI config space "
+				  "for ACK/NAK\n");
+			DBG_LEAVE(et131x_dbginfo);
+			return -EIO;
+		}
+
+		result = pci_write_config_word(pdev, ET1310_PCI_REPLAY,
+					       Replay[maxPayload]);
+		if (result != PCIBIOS_SUCCESSFUL) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Could not write PCI config space "
+				  "for Replay Timer\n");
+			DBG_LEAVE(et131x_dbginfo);
+			return -EIO;
+		}
+	}
+
+	/* l0s and l1 latency timers.  We are using default values.
+	 * Representing 001 for L0s and 010 for L1
+	 */
+	result = pci_write_config_byte(pdev, ET1310_PCI_L0L1LATENCY, 0x11);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Could not write PCI config space for "
+			  "Latency Timers\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	/* Change the max read size to 2k */
+	result = pci_read_config_byte(pdev, 0x51, &read_size_reg);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Could not read PCI config space for Max read size\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	read_size_reg &= 0x8f;
+	read_size_reg |= 0x40;
+
+	result = pci_write_config_byte(pdev, 0x51, read_size_reg);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Could not write PCI config space for Max read size\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	/* PCI Express Configuration registers 0x48-0x5B (Device Control) */
+	result = pci_read_config_word(pdev, ET1310_PCI_DEV_CTRL,
+				      &adapter->PciXDevCtl);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Could not read PCI config space for PCI Express Dev Ctl\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return -EIO;
+	}
+
+	/* Get MAC address from config space if an eeprom exists, otherwise
+	 * the MAC address there will not be valid
+	 */
+	if (adapter->bEepromPresent) {
+		int i;
+
+		for (i = 0; i < ETH_ALEN; i++) {
+			result = pci_read_config_byte(
+					pdev, ET1310_PCI_MAC_ADDRESS + i,
+					adapter->PermanentAddress + i);
+			if (result != PCIBIOS_SUCCESSFUL) {
+				DBG_ERROR(et131x_dbginfo,
+					  "Could not read PCI config space for MAC address\n");
+				DBG_LEAVE(et131x_dbginfo);
+				return -EIO;
+			}
+		}
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+/**
+ * et131x_error_timer_handler
+ * @data: timer-specific variable; here a pointer to our adapter structure
+ *
+ * The routine called when the error timer expires, to track the number of
+ * recurring errors.
+ */
+void et131x_error_timer_handler(unsigned long data)
+{
+	struct et131x_adapter *pAdapter = (struct et131x_adapter *) data;
+	PM_CSR_t pm_csr;
+
+	pm_csr.value = readl(&pAdapter->CSRAddress->global.pm_csr.value);
+
+	if (pm_csr.bits.pm_phy_sw_coma == 0) {
+		if (pAdapter->RegistryMACStat) {
+			UpdateMacStatHostCounters(pAdapter);
+		}
+	} else {
+		DBG_VERBOSE(et131x_dbginfo,
+			    "No interrupts, in PHY coma, pm_csr = 0x%x\n",
+			    pm_csr.value);
+	}
+
+	if (!pAdapter->Bmsr.bits.link_status &&
+	    pAdapter->RegistryPhyComa &&
+	    pAdapter->PoMgmt.TransPhyComaModeOnBoot < 11) {
+		pAdapter->PoMgmt.TransPhyComaModeOnBoot++;
+	}
+
+	if (pAdapter->PoMgmt.TransPhyComaModeOnBoot == 10) {
+		if (!pAdapter->Bmsr.bits.link_status
+		    && pAdapter->RegistryPhyComa) {
+			if (pm_csr.bits.pm_phy_sw_coma == 0) {
+				// NOTE - This was originally a 'sync with interrupt'. How
+				//        to do that under Linux?
+				et131x_enable_interrupts(pAdapter);
+				EnablePhyComa(pAdapter);
+			}
+		}
+	}
+
+	/* This is a periodic timer, so reschedule */
+	mod_timer(&pAdapter->ErrorTimer, jiffies +
+		  TX_ERROR_PERIOD * HZ / 1000);
+}
+
+/**
+ * et131x_link_detection_handler
+ *
+ * Timer function for link up at driver load time
+ */
+void et131x_link_detection_handler(unsigned long data)
+{
+	struct et131x_adapter *pAdapter = (struct et131x_adapter *) data;
+	unsigned long lockflags;
+
+	/* Let everyone know that we have run */
+	pAdapter->bLinkTimerActive = false;
+
+	if (pAdapter->MediaState == 0) {
+		spin_lock_irqsave(&pAdapter->Lock, lockflags);
+
+		pAdapter->MediaState = NETIF_STATUS_MEDIA_DISCONNECT;
+		MP_CLEAR_FLAG(pAdapter, fMP_ADAPTER_LINK_DETECTION);
+
+		spin_unlock_irqrestore(&pAdapter->Lock, lockflags);
+
+		netif_carrier_off(pAdapter->netdev);
+
+		pAdapter->bSetPending = false;
+	}
+}
+
+/**
+ * et131x_adapter_setup - Set the adapter up as per cassini+ documentation
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_adapter_setup(struct et131x_adapter *pAdapter)
+{
+	int status = 0;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Configure the JAGCore */
+	ConfigGlobalRegs(pAdapter);
+
+	ConfigMACRegs1(pAdapter);
+	ConfigMMCRegs(pAdapter);
+
+	ConfigRxMacRegs(pAdapter);
+	ConfigTxMacRegs(pAdapter);
+
+	ConfigRxDmaRegs(pAdapter);
+	ConfigTxDmaRegs(pAdapter);
+
+	ConfigMacStatRegs(pAdapter);
+
+	/* Move the following code to Timer function?? */
+	status = et131x_xcvr_find(pAdapter);
+
+	if (status != 0) {
+		DBG_WARNING(et131x_dbginfo, "Could not find the xcvr\n");
+	}
+
+	/* Prepare the TRUEPHY library. */
+	ET1310_PhyInit(pAdapter);
+
+	/* Reset the phy now so changes take place */
+	ET1310_PhyReset(pAdapter);
+
+	/* Power down PHY */
+	ET1310_PhyPowerDown(pAdapter, 1);
+
+	/*
+	 * We need to turn off 1000 base half dulplex, the mac does not
+	 * support it. For the 10/100 part, turn off all gig advertisement
+	 */
+	if (pAdapter->DeviceID != ET131X_PCI_DEVICE_ID_FAST) {
+		ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_FULL);
+	} else {
+		ET1310_PhyAdvertise1000BaseT(pAdapter, TRUEPHY_ADV_DUPLEX_NONE);
+	}
+
+	/* Power up PHY */
+	ET1310_PhyPowerDown(pAdapter, 0);
+
+	et131x_setphy_normal(pAdapter);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_setup_hardware_properties - set up the MAC Address on the ET1310
+ * @adapter: pointer to our private adapter structure
+ */
+void et131x_setup_hardware_properties(struct et131x_adapter *adapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* If have our default mac from registry and no mac address from
+	 * EEPROM then we need to generate the last octet and set it on the
+	 * device
+	 */
+	if (!adapter->bOverrideAddress) {
+		if (adapter->PermanentAddress[0] == 0x00 &&
+		    adapter->PermanentAddress[1] == 0x00 &&
+		    adapter->PermanentAddress[2] == 0x00 &&
+		    adapter->PermanentAddress[3] == 0x00 &&
+		    adapter->PermanentAddress[4] == 0x00 &&
+		    adapter->PermanentAddress[5] == 0x00) {
+			/*
+			 * We need to randomly generate the last octet so we
+			 * decrease our chances of setting the mac address to
+			 * same as another one of our cards in the system
+			 */
+			get_random_bytes(&adapter->CurrentAddress[5], 1);
+
+			/*
+			 * We have the default value in the register we are
+			 * working with so we need to copy the current
+			 * address into the permanent address
+			 */
+			memcpy(adapter->PermanentAddress,
+			       adapter->CurrentAddress, ETH_ALEN);
+		} else {
+			/* We do not have an override address, so set the
+			 * current address to the permanent address and add
+			 * it to the device
+			 */
+			memcpy(adapter->CurrentAddress,
+			       adapter->PermanentAddress, ETH_ALEN);
+		}
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_soft_reset - Issue a soft reset to the hardware, complete for ET1310
+ * @adapter: pointer to our private adapter structure
+ */
+void et131x_soft_reset(struct et131x_adapter *adapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Disable MAC Core */
+	writel(0xc00f0000, &adapter->CSRAddress->mac.cfg1.value);
+
+	/* Set everything to a reset value */
+	writel(0x7F, &adapter->CSRAddress->global.sw_reset.value);
+	writel(0x000f0000, &adapter->CSRAddress->mac.cfg1.value);
+	writel(0x00000000, &adapter->CSRAddress->mac.cfg1.value);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_align_allocated_memory - Align allocated memory on a given boundary
+ * @adapter: pointer to our adapter structure
+ * @phys_addr: pointer to Physical address
+ * @offset: pointer to the offset variable
+ * @mask: correct mask
+ */
+void et131x_align_allocated_memory(struct et131x_adapter *adapter,
+				   uint64_t *phys_addr,
+				   uint64_t *offset, uint64_t mask)
+{
+	uint64_t new_addr;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	*offset = 0;
+
+	new_addr = *phys_addr & ~mask;
+
+	if (new_addr != *phys_addr) {
+		/* Move to next aligned block */
+		new_addr += mask + 1;
+		/* Return offset for adjusting virt addr */
+		*offset = new_addr - *phys_addr;
+		/* Return new physical address */
+		*phys_addr = new_addr;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_adapter_memory_alloc
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h).
+ *
+ * Allocate all the memory blocks for send, receive and others.
+ */
+int et131x_adapter_memory_alloc(struct et131x_adapter *adapter)
+{
+	int status = 0;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	do {
+		/* Allocate memory for the Tx Ring */
+		status = et131x_tx_dma_memory_alloc(adapter);
+		if (status != 0) {
+			DBG_ERROR(et131x_dbginfo,
+				  "et131x_tx_dma_memory_alloc FAILED\n");
+			break;
+		}
+
+		/* Receive buffer memory allocation */
+		status = et131x_rx_dma_memory_alloc(adapter);
+		if (status != 0) {
+			DBG_ERROR(et131x_dbginfo,
+				  "et131x_rx_dma_memory_alloc FAILED\n");
+			et131x_tx_dma_memory_free(adapter);
+			break;
+		}
+
+		/* Init receive data structures */
+		status = et131x_init_recv(adapter);
+		if (status != 0) {
+			DBG_ERROR(et131x_dbginfo, "et131x_init_recv FAILED\n");
+			et131x_tx_dma_memory_free(adapter);
+			et131x_rx_dma_memory_free(adapter);
+			break;
+		}
+	} while (0);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_adapter_memory_free - Free all memory allocated for use by Tx & Rx
+ * @adapter: pointer to our private adapter structure
+ */
+void et131x_adapter_memory_free(struct et131x_adapter *adapter)
+{
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Free DMA memory */
+	et131x_tx_dma_memory_free(adapter);
+	et131x_rx_dma_memory_free(adapter);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_pci_remove
+ * @pdev: a pointer to the device's pci_dev structure
+ *
+ * Registered in the pci_driver structure, this function is called when the
+ * PCI subsystem detects that a PCI device which matches the information
+ * contained in the pci_device_id table has been removed.
+ */
+void __devexit et131x_pci_remove(struct pci_dev *pdev)
+{
+	struct net_device *netdev;
+	struct et131x_adapter *adapter;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Retrieve the net_device pointer from the pci_dev struct, as well
+	 * as the private adapter struct
+	 */
+	netdev = (struct net_device *) pci_get_drvdata(pdev);
+	adapter = netdev_priv(netdev);
+
+	/* Perform device cleanup */
+	unregister_netdev(netdev);
+	et131x_adapter_memory_free(adapter);
+	iounmap(adapter->CSRAddress);
+	free_netdev(netdev);
+	pci_release_regions(pdev);
+	pci_disable_device(pdev);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_pci_setup - Perform device initialization
+ * @pdev: a pointer to the device's pci_dev structure
+ * @ent: this device's entry in the pci_device_id table
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ *
+ * Registered in the pci_driver structure, this function is called when the
+ * PCI subsystem finds a new PCI device which matches the information
+ * contained in the pci_device_id table. This routine is the equivalent to
+ * a device insertion routine.
+ */
+int __devinit et131x_pci_setup(struct pci_dev *pdev,
+			       const struct pci_device_id *ent)
+{
+	int result = 0;
+	int pm_cap;
+	bool pci_using_dac;
+	struct net_device *netdev = NULL;
+	struct et131x_adapter *adapter = NULL;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Enable the device via the PCI subsystem */
+	result = pci_enable_device(pdev);
+	if (result != 0) {
+		DBG_ERROR(et131x_dbginfo, "pci_enable_device() failed\n");
+		goto out;
+	}
+
+	/* Perform some basic PCI checks */
+	if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Can't find PCI device's base address\n");
+		result = -ENODEV;
+		goto out;
+	}
+
+	result = pci_request_regions(pdev, DRIVER_NAME);
+	if (result != 0) {
+		DBG_ERROR(et131x_dbginfo, "Can't get PCI resources\n");
+		goto err_disable;
+	}
+
+	/* Enable PCI bus mastering */
+	DBG_TRACE(et131x_dbginfo, "Setting PCI Bus Mastering...\n");
+	pci_set_master(pdev);
+
+	/* Query PCI for Power Mgmt Capabilities
+	 *
+	 * NOTE: Now reading PowerMgmt in another location; is this still
+	 * needed?
+	 */
+	pm_cap = pci_find_capability(pdev, PCI_CAP_ID_PM);
+	if (pm_cap == 0) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Cannot find Power Management capabilities\n");
+		result = -EIO;
+		goto err_release_res;
+	}
+
+	/* Check the DMA addressing support of this device */
+	if (!pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
+		DBG_TRACE(et131x_dbginfo, "64-bit DMA addressing supported\n");
+		pci_using_dac = true;
+
+		result =
+		    pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+		if (result != 0) {
+			DBG_ERROR(et131x_dbginfo,
+				  "Unable to obtain 64 bit DMA for consistent allocations\n");
+			goto err_release_res;
+		}
+	} else if (!pci_set_dma_mask(pdev, 0xffffffffULL)) {
+		DBG_TRACE(et131x_dbginfo,
+			  "64-bit DMA addressing NOT supported\n");
+		DBG_TRACE(et131x_dbginfo,
+			  "32-bit DMA addressing will be used\n");
+		pci_using_dac = false;
+	} else {
+		DBG_ERROR(et131x_dbginfo, "No usable DMA addressing method\n");
+		result = -EIO;
+		goto err_release_res;
+	}
+
+	/* Allocate netdev and private adapter structs */
+	DBG_TRACE(et131x_dbginfo,
+		  "Allocate netdev and private adapter structs...\n");
+	netdev = et131x_device_alloc();
+	if (netdev == NULL) {
+		DBG_ERROR(et131x_dbginfo, "Couldn't alloc netdev struct\n");
+		result = -ENOMEM;
+		goto err_release_res;
+	}
+
+	/* Setup the fundamental net_device and private adapter structure elements  */
+	DBG_TRACE(et131x_dbginfo, "Setting fundamental net_device info...\n");
+	SET_NETDEV_DEV(netdev, &pdev->dev);
+	if (pci_using_dac) {
+		//netdev->features |= NETIF_F_HIGHDMA;
+	}
+
+	/*
+	 * NOTE - Turn this on when we're ready to deal with SG-DMA
+	 *
+	 * NOTE: According to "Linux Device Drivers", 3rd ed, Rubini et al,
+	 * if checksumming is not performed in HW, then the kernel will not
+	 * use SG.
+	 * From pp 510-511:
+	 *
+	 * "Note that the kernel does not perform scatter/gather I/O to your
+	 * device if it does not also provide some form of checksumming as
+	 * well. The reason is that, if the kernel has to make a pass over a
+	 * fragmented ("nonlinear") packet to calculate the checksum, it
+	 * might as well copy the data and coalesce the packet at the same
+	 * time."
+	 *
+	 * This has been verified by setting the flags below and still not
+	 * receiving a scattered buffer from the network stack, so leave it
+	 * off until checksums are calculated in HW.
+	 */
+	//netdev->features |= NETIF_F_SG;
+	//netdev->features |= NETIF_F_NO_CSUM;
+	//netdev->features |= NETIF_F_LLTX;
+
+	/* Allocate private adapter struct and copy in relevant information */
+	adapter = netdev_priv(netdev);
+	adapter->pdev = pdev;
+	adapter->netdev = netdev;
+	adapter->VendorID = pdev->vendor;
+	adapter->DeviceID = pdev->device;
+
+	/* Do the same for the netdev struct */
+	netdev->irq = pdev->irq;
+	netdev->base_addr = pdev->resource[0].start;
+
+	/* Initialize spinlocks here */
+	DBG_TRACE(et131x_dbginfo, "Initialize spinlocks...\n");
+
+	spin_lock_init(&adapter->Lock);
+	spin_lock_init(&adapter->TCBSendQLock);
+	spin_lock_init(&adapter->TCBReadyQLock);
+	spin_lock_init(&adapter->SendHWLock);
+	spin_lock_init(&adapter->SendWaitLock);
+	spin_lock_init(&adapter->RcvLock);
+	spin_lock_init(&adapter->RcvPendLock);
+	spin_lock_init(&adapter->FbrLock);
+	spin_lock_init(&adapter->PHYLock);
+
+	/* Parse configuration parameters into the private adapter struct */
+	et131x_config_parse(adapter);
+
+	/* Find the physical adapter
+	 *
+	 * NOTE: This is the equivalent of the MpFindAdapter() routine; can we
+	 *       lump it's init with the device specific init below into a
+	 *       single init function?
+	 */
+	//while (et131x_find_adapter(adapter, pdev) != 0);
+	et131x_find_adapter(adapter, pdev);
+
+	/* Map the bus-relative registers to system virtual memory */
+	DBG_TRACE(et131x_dbginfo,
+		  "Mapping bus-relative registers to virtual memory...\n");
+
+	adapter->CSRAddress = ioremap_nocache(pci_resource_start(pdev, 0),
+					      pci_resource_len(pdev, 0));
+	if (adapter->CSRAddress == NULL) {
+		DBG_ERROR(et131x_dbginfo, "Cannot map device registers\n");
+		result = -ENOMEM;
+		goto err_free_dev;
+	}
+
+	/* Perform device-specific initialization here (See code below) */
+
+	/* If Phy COMA mode was enabled when we went down, disable it here. */
+	{
+		PM_CSR_t GlobalPmCSR = { 0 };
+
+		GlobalPmCSR.bits.pm_sysclk_gate = 1;
+		GlobalPmCSR.bits.pm_txclk_gate = 1;
+		GlobalPmCSR.bits.pm_rxclk_gate = 1;
+		writel(GlobalPmCSR.value,
+		       &adapter->CSRAddress->global.pm_csr.value);
+	}
+
+	/* Issue a global reset to the et1310 */
+	DBG_TRACE(et131x_dbginfo, "Issuing soft reset...\n");
+	et131x_soft_reset(adapter);
+
+	/* Disable all interrupts (paranoid) */
+	DBG_TRACE(et131x_dbginfo, "Disable device interrupts...\n");
+	et131x_disable_interrupts(adapter);
+
+	/* Allocate DMA memory */
+	result = et131x_adapter_memory_alloc(adapter);
+	if (result != 0) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Could not alloc adapater memory (DMA)\n");
+		goto err_iounmap;
+	}
+
+	/* Init send data structures */
+	DBG_TRACE(et131x_dbginfo, "Init send data structures...\n");
+	et131x_init_send(adapter);
+
+	adapter->PoMgmt.PowerState = NdisDeviceStateD0;
+
+	/* Register the interrupt
+	 *
+	 * NOTE - This is being done in the open routine, where most other
+	 *         Linux drivers setup IRQ handlers. Make sure device
+	 *         interrupts are not turned on before the IRQ is registered!!
+	 *
+	 *         What we will do here is setup the task structure for the
+	 *         ISR's deferred handler
+	 */
+	INIT_WORK(&adapter->task, et131x_isr_handler);
+
+	/* Determine MAC Address, and copy into the net_device struct */
+	DBG_TRACE(et131x_dbginfo, "Retrieve MAC address...\n");
+	et131x_setup_hardware_properties(adapter);
+
+	memcpy(netdev->dev_addr, adapter->CurrentAddress, ETH_ALEN);
+
+	/* Setup et1310 as per the documentation */
+	DBG_TRACE(et131x_dbginfo, "Setup the adapter...\n");
+	et131x_adapter_setup(adapter);
+
+	/* Create a timer to count errors received by the NIC */
+	init_timer(&adapter->ErrorTimer);
+
+	adapter->ErrorTimer.expires = jiffies + TX_ERROR_PERIOD * HZ / 1000;
+	adapter->ErrorTimer.function = et131x_error_timer_handler;
+	adapter->ErrorTimer.data = (unsigned long)adapter;
+
+	/* Initialize link state */
+	et131x_link_detection_handler((unsigned long)adapter);
+
+	/* Intialize variable for counting how long we do not have link status */
+	adapter->PoMgmt.TransPhyComaModeOnBoot = 0;
+
+	/* We can enable interrupts now
+	 *
+	 *  NOTE - Because registration of interrupt handler is done in the
+	 *         device's open(), defer enabling device interrupts to that
+	 *         point
+	 */
+
+	/* Register the net_device struct with the Linux network layer */
+	DBG_TRACE(et131x_dbginfo, "Registering net_device...\n");
+	if ((result = register_netdev(netdev)) != 0) {
+		DBG_ERROR(et131x_dbginfo, "register_netdev() failed\n");
+		goto err_mem_free;
+	}
+
+	/* Register the net_device struct with the PCI subsystem. Save a copy
+	 * of the PCI config space for this device now that the device has
+	 * been initialized, just in case it needs to be quickly restored.
+	 */
+	pci_set_drvdata(pdev, netdev);
+
+	pci_save_state(adapter->pdev);
+
+out:
+	DBG_LEAVE(et131x_dbginfo);
+	return result;
+
+err_mem_free:
+	et131x_adapter_memory_free(adapter);
+err_iounmap:
+	iounmap(adapter->CSRAddress);
+err_free_dev:
+	free_netdev(netdev);
+err_release_res:
+	pci_release_regions(pdev);
+err_disable:
+	pci_disable_device(pdev);
+	goto out;
+}
diff --git a/drivers/staging/et131x/et131x_initpci.h b/drivers/staging/et131x/et131x_initpci.h
new file mode 100644
index 0000000..bbacb62
--- /dev/null
+++ b/drivers/staging/et131x/et131x_initpci.h
@@ -0,0 +1,73 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_initpci.h - Header which includes common data and function prototypes
+ *                    related to the driver's PCI (and PCI Express) information.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_INITPCI_H__
+#define __ET131X_INITPCI_H__
+
+/* Function Prototypes */
+void et131x_align_allocated_memory(struct et131x_adapter *adapter,
+				   u64 *phys_addr,
+				   u64 *offset, u64 mask);
+
+int et131x_adapter_setup(struct et131x_adapter *adapter);
+int et131x_adapter_memory_alloc(struct et131x_adapter *adapter);
+void et131x_adapter_memory_free(struct et131x_adapter *adapter);
+void et131x_setup_hardware_properties(struct et131x_adapter *adapter);
+void et131x_soft_reset(struct et131x_adapter *adapter);
+
+#endif /* __ET131X_INITPCI_H__ */
diff --git a/drivers/staging/et131x/et131x_isr.c b/drivers/staging/et131x/et131x_isr.c
new file mode 100644
index 0000000..00afad1
--- /dev/null
+++ b/drivers/staging/et131x/et131x_isr.c
@@ -0,0 +1,488 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_isr.c - File which contains the ISR, ISR handler, and related routines
+ *                for processing interrupts from the device.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+#include "et1310_mac.h"
+
+#include "et131x_adapter.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+/**
+ * et131x_isr - The Interrupt Service Routine for the driver.
+ * @irq: the IRQ on which the interrupt was received.
+ * @dev_id: device-specific info (here a pointer to a net_device struct)
+ *
+ * Returns a value indicating if the interrupt was handled.
+ */
+irqreturn_t et131x_isr(int irq, void *dev_id)
+{
+	bool handled = true;
+	struct net_device *netdev = (struct net_device *)dev_id;
+	struct et131x_adapter *adapter = NULL;
+	INTERRUPT_t status;
+
+	if (netdev == NULL || !netif_device_present(netdev)) {
+		DBG_WARNING(et131x_dbginfo,
+			    "No net_device struct or device not present\n");
+		handled = false;
+		goto out;
+	}
+
+	adapter = netdev_priv(netdev);
+
+	/* If the adapter is in low power state, then it should not
+	 * recognize any interrupt
+	 */
+
+	/* Disable Device Interrupts */
+	et131x_disable_interrupts(adapter);
+
+	/* Get a copy of the value in the interrupt status register
+	 * so we can process the interrupting section
+	 */
+	status.value = readl(&adapter->CSRAddress->global.int_status.value);
+
+	if (adapter->FlowControl == TxOnly ||
+	    adapter->FlowControl == Both) {
+		status.value &= ~INT_MASK_ENABLE;
+	} else {
+		status.value &= ~INT_MASK_ENABLE_NO_FLOW;
+	}
+
+	/* Make sure this is our interrupt */
+	if (!status.value) {
+#ifdef CONFIG_ET131X_DEBUG
+		adapter->Stats.UnhandledInterruptsPerSec++;
+#endif
+		handled = false;
+		DBG_VERBOSE(et131x_dbginfo, "NOT OUR INTERRUPT\n");
+		et131x_enable_interrupts(adapter);
+		goto out;
+	}
+
+	/* This is our interrupt, so process accordingly */
+#ifdef CONFIG_ET131X_DEBUG
+	if (status.bits.rxdma_xfr_done) {
+		adapter->Stats.RxDmaInterruptsPerSec++;
+	}
+
+	if (status.bits.txdma_isr) {
+		adapter->Stats.TxDmaInterruptsPerSec++;
+	}
+#endif
+
+	if (status.bits.watchdog_interrupt) {
+		PMP_TCB pMpTcb = adapter->TxRing.CurrSendHead;
+
+		if (pMpTcb) {
+			if (++pMpTcb->PacketStaleCount > 1) {
+				status.bits.txdma_isr = 1;
+			}
+		}
+
+		if (adapter->RxRing.UnfinishedReceives) {
+			status.bits.rxdma_xfr_done = 1;
+		} else if (pMpTcb == NULL) {
+			writel(0, &adapter->CSRAddress->global.watchdog_timer);
+		}
+
+		status.bits.watchdog_interrupt = 0;
+#ifdef CONFIG_ET131X_DEBUG
+		adapter->Stats.WatchDogInterruptsPerSec++;
+#endif
+	}
+
+	if (status.value == 0) {
+		/* This interrupt has in some way been "handled" by
+		 * the ISR. Either it was a spurious Rx interrupt, or
+		 * it was a Tx interrupt that has been filtered by
+		 * the ISR.
+		 */
+		et131x_enable_interrupts(adapter);
+		goto out;
+	}
+
+	/* We need to save the interrupt status value for use in our
+	 * DPC. We will clear the software copy of that in that
+	 * routine.
+	 */
+	adapter->Stats.InterruptStatus = status;
+
+	/* Schedule the ISR handler as a bottom-half task in the
+	 * kernel's tq_immediate queue, and mark the queue for
+	 * execution
+	 */
+	schedule_work(&adapter->task);
+
+out:
+	return IRQ_RETVAL(handled);
+}
+
+/**
+ * et131x_isr_handler - The ISR handler
+ * @p_adapter, a pointer to the device's private adapter structure
+ *
+ * scheduled to run in a deferred context by the ISR. This is where the ISR's
+ * work actually gets done.
+ */
+void et131x_isr_handler(struct work_struct *work)
+{
+	struct et131x_adapter *pAdapter =
+		container_of(work, struct et131x_adapter, task);
+	INTERRUPT_t GlobStatus = pAdapter->Stats.InterruptStatus;
+	ADDRESS_MAP_t __iomem *iomem = pAdapter->CSRAddress;
+
+	/*
+	 * These first two are by far the most common.  Once handled, we clear
+	 * their two bits in the status word.  If the word is now zero, we
+	 * exit.
+	 */
+	/* Handle all the completed Transmit interrupts */
+	if (GlobStatus.bits.txdma_isr) {
+		DBG_TX(et131x_dbginfo, "TXDMA_ISR interrupt\n");
+		et131x_handle_send_interrupt(pAdapter);
+	}
+
+	/* Handle all the completed Receives interrupts */
+	if (GlobStatus.bits.rxdma_xfr_done) {
+		DBG_RX(et131x_dbginfo, "RXDMA_XFR_DONE interrupt\n");
+		et131x_handle_recv_interrupt(pAdapter);
+	}
+
+	GlobStatus.value &= 0xffffffd7;
+
+	if (GlobStatus.value) {
+		/* Handle the TXDMA Error interrupt */
+		if (GlobStatus.bits.txdma_err) {
+			TXDMA_ERROR_t TxDmaErr;
+
+			/* Following read also clears the register (COR) */
+			TxDmaErr.value = readl(&iomem->txdma.TxDmaError.value);
+
+			DBG_WARNING(et131x_dbginfo,
+				    "TXDMA_ERR interrupt, error = %d\n",
+				    TxDmaErr.value);
+		}
+
+		/* Handle Free Buffer Ring 0 and 1 Low interrupt */
+		if (GlobStatus.bits.rxdma_fb_ring0_low ||
+		    GlobStatus.bits.rxdma_fb_ring1_low) {
+			/*
+			 * This indicates the number of unused buffers in
+			 * RXDMA free buffer ring 0 is <= the limit you
+			 * programmed. Free buffer resources need to be
+			 * returned.  Free buffers are consumed as packets
+			 * are passed from the network to the host. The host
+			 * becomes aware of the packets from the contents of
+			 * the packet status ring. This ring is queried when
+			 * the packet done interrupt occurs. Packets are then
+			 * passed to the OS. When the OS is done with the
+			 * packets the resources can be returned to the
+			 * ET1310 for re-use. This interrupt is one method of
+			 * returning resources.
+			 */
+			DBG_WARNING(et131x_dbginfo,
+				    "RXDMA_FB_RING0_LOW or "
+				    "RXDMA_FB_RING1_LOW interrupt\n");
+
+			/* If the user has flow control on, then we will
+			 * send a pause packet, otherwise just exit
+			 */
+			if (pAdapter->FlowControl == TxOnly ||
+			    pAdapter->FlowControl == Both) {
+				PM_CSR_t pm_csr;
+
+				/* Tell the device to send a pause packet via
+				 * the back pressure register
+				 */
+				pm_csr.value = readl(&iomem->global.pm_csr.value);
+				if (pm_csr.bits.pm_phy_sw_coma == 0) {
+					TXMAC_BP_CTRL_t bp_ctrl = { 0 };
+
+					bp_ctrl.bits.bp_req = 1;
+					bp_ctrl.bits.bp_xonxoff = 1;
+					writel(bp_ctrl.value,
+					       &iomem->txmac.bp_ctrl.value);
+				}
+			}
+		}
+
+		/* Handle Packet Status Ring Low Interrupt */
+		if (GlobStatus.bits.rxdma_pkt_stat_ring_low) {
+			DBG_WARNING(et131x_dbginfo,
+				    "RXDMA_PKT_STAT_RING_LOW interrupt\n");
+
+			/*
+			 * Same idea as with the two Free Buffer Rings.
+			 * Packets going from the network to the host each
+			 * consume a free buffer resource and a packet status
+			 * resource.  These resoures are passed to the OS.
+			 * When the OS is done with the resources, they need
+			 * to be returned to the ET1310. This is one method
+			 * of returning the resources.
+			 */
+		}
+
+		/* Handle RXDMA Error Interrupt */
+		if (GlobStatus.bits.rxdma_err) {
+			/*
+			 * The rxdma_error interrupt is sent when a time-out
+			 * on a request issued by the JAGCore has occurred or
+			 * a completion is returned with an un-successful
+			 * status.  In both cases the request is considered
+			 * complete. The JAGCore will automatically re-try the
+			 * request in question. Normally information on events
+			 * like these are sent to the host using the "Advanced
+			 * Error Reporting" capability. This interrupt is
+			 * another way of getting similar information. The
+			 * only thing required is to clear the interrupt by
+			 * reading the ISR in the global resources. The
+			 * JAGCore will do a re-try on the request.  Normally
+			 * you should never see this interrupt. If you start
+			 * to see this interrupt occurring frequently then
+			 * something bad has occurred. A reset might be the
+			 * thing to do.
+			 */
+			// TRAP();
+
+			pAdapter->TxMacTest.value =
+				readl(&iomem->txmac.tx_test.value);
+			DBG_WARNING(et131x_dbginfo,
+				    "RxDMA_ERR interrupt, error %x\n",
+				    pAdapter->TxMacTest.value);
+		}
+
+		/* Handle the Wake on LAN Event */
+		if (GlobStatus.bits.wake_on_lan) {
+			/*
+			 * This is a secondary interrupt for wake on LAN.
+			 * The driver should never see this, if it does,
+			 * something serious is wrong. We will TRAP the
+			 * message when we are in DBG mode, otherwise we
+			 * will ignore it.
+			 */
+			DBG_ERROR(et131x_dbginfo, "WAKE_ON_LAN interrupt\n");
+		}
+
+		/* Handle the PHY interrupt */
+		if (GlobStatus.bits.phy_interrupt) {
+			PM_CSR_t pm_csr;
+			MI_BMSR_t BmsrInts, BmsrData;
+			MI_ISR_t myIsr;
+
+			DBG_VERBOSE(et131x_dbginfo, "PHY interrupt\n");
+
+			/* If we are in coma mode when we get this interrupt,
+			 * we need to disable it.
+			 */
+			pm_csr.value = readl(&iomem->global.pm_csr.value);
+			if (pm_csr.bits.pm_phy_sw_coma == 1) {
+				/*
+				 * Check to see if we are in coma mode and if
+				 * so, disable it because we will not be able
+				 * to read PHY values until we are out.
+				 */
+				DBG_VERBOSE(et131x_dbginfo,
+					    "Device is in COMA mode, "
+					    "need to wake up\n");
+				DisablePhyComa(pAdapter);
+			}
+
+			/* Read the PHY ISR to clear the reason for the
+			 * interrupt.
+			 */
+			MiRead(pAdapter, (uint8_t) offsetof(MI_REGS_t, isr),
+			       &myIsr.value);
+
+			if (!pAdapter->ReplicaPhyLoopbk) {
+				MiRead(pAdapter,
+				       (uint8_t) offsetof(MI_REGS_t, bmsr),
+				       &BmsrData.value);
+
+				BmsrInts.value =
+				    pAdapter->Bmsr.value ^ BmsrData.value;
+				pAdapter->Bmsr.value = BmsrData.value;
+
+				DBG_VERBOSE(et131x_dbginfo,
+					    "Bmsr.value = 0x%04x,"
+					    "Bmsr_ints.value = 0x%04x\n",
+					    BmsrData.value, BmsrInts.value);
+
+				/* Do all the cable in / cable out stuff */
+				et131x_Mii_check(pAdapter, BmsrData, BmsrInts);
+			}
+		}
+
+		/* Let's move on to the TxMac */
+		if (GlobStatus.bits.txmac_interrupt) {
+			pAdapter->TxRing.TxMacErr.value =
+				readl(&iomem->txmac.err.value);
+
+			/*
+			 * When any of the errors occur and TXMAC generates
+			 * an interrupt to report these errors, it usually
+			 * means that TXMAC has detected an error in the data
+			 * stream retrieved from the on-chip Tx Q. All of
+			 * these errors are catastrophic and TXMAC won't be
+			 * able to recover data when these errors occur.  In
+			 * a nutshell, the whole Tx path will have to be reset
+			 * and re-configured afterwards.
+			 */
+			DBG_WARNING(et131x_dbginfo,
+				    "TXMAC interrupt, error 0x%08x\n",
+				    pAdapter->TxRing.TxMacErr.value);
+
+			/* If we are debugging, we want to see this error,
+			 * otherwise we just want the device to be reset and
+			 * continue
+			 */
+			//DBG_TRAP();
+		}
+
+		/* Handle RXMAC Interrupt */
+		if (GlobStatus.bits.rxmac_interrupt) {
+			/*
+			 * These interrupts are catastrophic to the device,
+			 * what we need to do is disable the interrupts and
+			 * set the flag to cause us to reset so we can solve
+			 * this issue.
+			 */
+			// MP_SET_FLAG( pAdapter, fMP_ADAPTER_HARDWARE_ERROR );
+
+			DBG_WARNING(et131x_dbginfo,
+				    "RXMAC interrupt, error 0x%08x.  Requesting reset\n",
+				    readl(&iomem->rxmac.err_reg.value));
+
+			DBG_WARNING(et131x_dbginfo,
+				    "Enable 0x%08x, Diag 0x%08x\n",
+				    readl(&iomem->rxmac.ctrl.value),
+				    readl(&iomem->rxmac.rxq_diag.value));
+
+			/*
+			 * If we are debugging, we want to see this error,
+			 * otherwise we just want the device to be reset and
+			 * continue
+			 */
+			// TRAP();
+		}
+
+		/* Handle MAC_STAT Interrupt */
+		if (GlobStatus.bits.mac_stat_interrupt) {
+			/*
+			 * This means at least one of the un-masked counters
+			 * in the MAC_STAT block has rolled over.  Use this
+			 * to maintain the top, software managed bits of the
+			 * counter(s).
+			 */
+			DBG_VERBOSE(et131x_dbginfo, "MAC_STAT interrupt\n");
+			HandleMacStatInterrupt(pAdapter);
+		}
+
+		/* Handle SLV Timeout Interrupt */
+		if (GlobStatus.bits.slv_timeout) {
+			/*
+			 * This means a timeout has occured on a read or
+			 * write request to one of the JAGCore registers. The
+			 * Global Resources block has terminated the request
+			 * and on a read request, returned a "fake" value.
+			 * The most likely reasons are: Bad Address or the
+			 * addressed module is in a power-down state and
+			 * can't respond.
+			 */
+			DBG_VERBOSE(et131x_dbginfo, "SLV_TIMEOUT interrupt\n");
+		}
+	}
+
+	if (pAdapter->PoMgmt.PowerState == NdisDeviceStateD0) {
+		et131x_enable_interrupts(pAdapter);
+	}
+}
diff --git a/drivers/staging/et131x/et131x_isr.h b/drivers/staging/et131x/et131x_isr.h
new file mode 100644
index 0000000..76a51d5
--- /dev/null
+++ b/drivers/staging/et131x/et131x_isr.h
@@ -0,0 +1,65 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_isr.h - Defines, structs, enums, prototypes, etc. pertaining to the
+ *                ISR processing code.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_ISR_H__
+#define __ET131X_ISR_H__
+
+irqreturn_t et131x_isr(int irq, void *dev_id);
+void et131x_isr_handler(struct work_struct *work);
+
+#endif /* __ET131X_ISR_H__ */
diff --git a/drivers/staging/et131x/et131x_netdev.c b/drivers/staging/et131x/et131x_netdev.c
new file mode 100644
index 0000000..de65972
--- /dev/null
+++ b/drivers/staging/et131x/et131x_netdev.c
@@ -0,0 +1,856 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_netdev.c - Routines and data required by all Linux network devices.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#include "et131x_version.h"
+#include "et131x_debug.h"
+#include "et131x_defs.h"
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/in.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+#include <linux/mii.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/ioport.h>
+
+#include "et1310_phy.h"
+#include "et1310_pm.h"
+#include "et1310_jagcore.h"
+#include "et1310_mac.h"
+#include "et1310_tx.h"
+
+#include "et131x_adapter.h"
+#include "et131x_isr.h"
+#include "et131x_initpci.h"
+
+/* Data for debugging facilities */
+#ifdef CONFIG_ET131X_DEBUG
+extern dbg_info_t *et131x_dbginfo;
+#endif /* CONFIG_ET131X_DEBUG */
+
+struct net_device_stats *et131x_stats(struct net_device *netdev);
+int et131x_open(struct net_device *netdev);
+int et131x_close(struct net_device *netdev);
+int et131x_ioctl(struct net_device *netdev, struct ifreq *reqbuf, int cmd);
+void et131x_multicast(struct net_device *netdev);
+int et131x_tx(struct sk_buff *skb, struct net_device *netdev);
+void et131x_tx_timeout(struct net_device *netdev);
+int et131x_change_mtu(struct net_device *netdev, int new_mtu);
+int et131x_set_mac_addr(struct net_device *netdev, void *new_mac);
+void et131x_vlan_rx_register(struct net_device *netdev, struct vlan_group *grp);
+void et131x_vlan_rx_add_vid(struct net_device *netdev, uint16_t vid);
+void et131x_vlan_rx_kill_vid(struct net_device *netdev, uint16_t vid);
+
+/**
+ * et131x_device_alloc
+ *
+ * Returns pointer to the allocated and initialized net_device struct for
+ * this device.
+ *
+ * Create instances of net_device and wl_private for the new adapter and
+ * register the device's entry points in the net_device structure.
+ */
+struct net_device *et131x_device_alloc(void)
+{
+	struct net_device *netdev;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Alloc net_device and adapter structs */
+	netdev = alloc_etherdev(sizeof(struct et131x_adapter));
+
+	if (netdev == NULL) {
+		DBG_ERROR(et131x_dbginfo,
+			  "Alloc of net_device struct failed\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return NULL;
+	}
+
+	/* Setup the function registration table (and other data) for a
+	 * net_device
+	 */
+	//netdev->init               = &et131x_init;
+	//netdev->set_config = &et131x_config;
+	netdev->get_stats = &et131x_stats;
+	netdev->open = &et131x_open;
+	netdev->stop = &et131x_close;
+	netdev->do_ioctl = &et131x_ioctl;
+	netdev->set_multicast_list = &et131x_multicast;
+	netdev->hard_start_xmit = &et131x_tx;
+	netdev->tx_timeout = &et131x_tx_timeout;
+	netdev->watchdog_timeo = ET131X_TX_TIMEOUT;
+	netdev->change_mtu = &et131x_change_mtu;
+	netdev->set_mac_address = &et131x_set_mac_addr;
+
+	//netdev->ethtool_ops        = &et131x_ethtool_ops;
+
+	// Poll?
+	//netdev->poll               = &et131x_poll;
+	//netdev->poll_controller    = &et131x_poll_controller;
+
+	DBG_LEAVE(et131x_dbginfo);
+	return netdev;
+}
+
+/**
+ * et131x_stats - Return the current device statistics.
+ * @netdev: device whose stats are being queried
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+struct net_device_stats *et131x_stats(struct net_device *netdev)
+{
+	struct et131x_adapter *adapter = netdev_priv(netdev);
+	struct net_device_stats *stats = &adapter->net_stats;
+	CE_STATS_t *devstat = &adapter->Stats;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	stats->rx_packets = devstat->ipackets;
+	stats->tx_packets = devstat->opackets;
+	stats->rx_errors = devstat->length_err + devstat->alignment_err +
+	    devstat->crc_err + devstat->code_violations + devstat->other_errors;
+	stats->tx_errors = devstat->max_pkt_error;
+	stats->multicast = devstat->multircv;
+	stats->collisions = devstat->collisions;
+
+	stats->rx_length_errors = devstat->length_err;
+	stats->rx_over_errors = devstat->rx_ov_flow;
+	stats->rx_crc_errors = devstat->crc_err;
+
+	// NOTE: These stats don't have corresponding values in CE_STATS, so we're
+	//       going to have to update these directly from within the TX/RX code
+	//stats->rx_bytes            = 20; //devstat->;
+	//stats->tx_bytes            = 20; //devstat->;
+	//stats->rx_dropped          = devstat->;
+	//stats->tx_dropped          = devstat->;
+
+	// NOTE: Not used, can't find analogous statistics
+	//stats->rx_frame_errors     = devstat->;
+	//stats->rx_fifo_errors      = devstat->;
+	//stats->rx_missed_errors    = devstat->;
+
+	//stats->tx_aborted_errors   = devstat->;
+	//stats->tx_carrier_errors   = devstat->;
+	//stats->tx_fifo_errors      = devstat->;
+	//stats->tx_heartbeat_errors = devstat->;
+	//stats->tx_window_errors    = devstat->;
+
+	DBG_LEAVE(et131x_dbginfo);
+	return stats;
+}
+
+/**
+ * et131x_open - Open the device for use.
+ * @netdev: device to be opened
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_open(struct net_device *netdev)
+{
+	int result = 0;
+	struct et131x_adapter *adapter = netdev_priv(netdev);
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Start the timer to track NIC errors */
+	add_timer(&adapter->ErrorTimer);
+
+	/* Register our ISR */
+	DBG_TRACE(et131x_dbginfo, "Registering ISR...\n");
+
+	result =
+	    request_irq(netdev->irq, et131x_isr, IRQF_SHARED, netdev->name,
+			netdev);
+	if (result) {
+		DBG_ERROR(et131x_dbginfo, "Could not register ISR\n");
+		DBG_LEAVE(et131x_dbginfo);
+		return result;
+	}
+
+	/* Enable the Tx and Rx DMA engines (if not already enabled) */
+	et131x_rx_dma_enable(adapter);
+	et131x_tx_dma_enable(adapter);
+
+	/* Enable device interrupts */
+	et131x_enable_interrupts(adapter);
+
+	MP_SET_FLAG(adapter, fMP_ADAPTER_INTERRUPT_IN_USE);
+
+	/* We're ready to move some data, so start the queue */
+	netif_start_queue(netdev);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return result;
+}
+
+/**
+ * et131x_close - Close the device
+ * @netdev: device to be closed
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_close(struct net_device *netdev)
+{
+	struct et131x_adapter *adapter = netdev_priv(netdev);
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* First thing is to stop the queue */
+	netif_stop_queue(netdev);
+
+	/* Stop the Tx and Rx DMA engines */
+	et131x_rx_dma_disable(adapter);
+	et131x_tx_dma_disable(adapter);
+
+	/* Disable device interrupts */
+	et131x_disable_interrupts(adapter);
+
+	/* Deregistering ISR */
+	MP_CLEAR_FLAG(adapter, fMP_ADAPTER_INTERRUPT_IN_USE);
+
+	DBG_TRACE(et131x_dbginfo, "Deregistering ISR...\n");
+	free_irq(netdev->irq, netdev);
+
+	/* Stop the error timer */
+	del_timer_sync(&adapter->ErrorTimer);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return 0;
+}
+
+/**
+ * et131x_ioctl_mii - The function which handles MII IOCTLs
+ * @netdev: device on which the query is being made
+ * @reqbuf: the request-specific data buffer
+ * @cmd: the command request code
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_ioctl_mii(struct net_device *netdev, struct ifreq *reqbuf, int cmd)
+{
+	int status = 0;
+	struct et131x_adapter *pAdapter = netdev_priv(netdev);
+	struct mii_ioctl_data *data = if_mii(reqbuf);
+
+	DBG_ENTER(et131x_dbginfo);
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+		DBG_VERBOSE(et131x_dbginfo, "SIOCGMIIPHY\n");
+		data->phy_id = pAdapter->Stats.xcvr_addr;
+		break;
+
+	case SIOCGMIIREG:
+		DBG_VERBOSE(et131x_dbginfo, "SIOCGMIIREG\n");
+		if (!capable(CAP_NET_ADMIN)) {
+			status = -EPERM;
+		} else {
+			status = MiRead(pAdapter,
+					data->reg_num, &data->val_out);
+		}
+		break;
+
+	case SIOCSMIIREG:
+		DBG_VERBOSE(et131x_dbginfo, "SIOCSMIIREG\n");
+		if (!capable(CAP_NET_ADMIN)) {
+			status = -EPERM;
+		} else {
+			status = MiWrite(pAdapter, data->reg_num,
+					 data->val_in);
+		}
+		break;
+
+	default:
+		status = -EOPNOTSUPP;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_ioctl - The I/O Control handler for the driver
+ * @netdev: device on which the control request is being made
+ * @reqbuf: a pointer to the IOCTL request buffer
+ * @cmd: the IOCTL command code
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_ioctl(struct net_device *netdev, struct ifreq *reqbuf, int cmd)
+{
+	int status = 0;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	switch (cmd) {
+	case SIOCGMIIPHY:
+	case SIOCGMIIREG:
+	case SIOCSMIIREG:
+		status = et131x_ioctl_mii(netdev, reqbuf, cmd);
+		break;
+
+	default:
+		DBG_WARNING(et131x_dbginfo, "Unhandled IOCTL Code: 0x%04x\n",
+			    cmd);
+		status = -EOPNOTSUPP;
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_set_packet_filter - Configures the Rx Packet filtering on the device
+ * @adapter: pointer to our private adapter structure
+ *
+ * Returns 0 on success, errno on failure
+ */
+int et131x_set_packet_filter(struct et131x_adapter *adapter)
+{
+	int status = 0;
+	uint32_t filter = adapter->PacketFilter;
+	RXMAC_CTRL_t ctrl;
+	RXMAC_PF_CTRL_t pf_ctrl;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	ctrl.value = readl(&adapter->CSRAddress->rxmac.ctrl.value);
+	pf_ctrl.value = readl(&adapter->CSRAddress->rxmac.pf_ctrl.value);
+
+	/* Default to disabled packet filtering.  Enable it in the individual
+	 * case statements that require the device to filter something
+	 */
+	ctrl.bits.pkt_filter_disable = 1;
+
+	/* Set us to be in promiscuous mode so we receive everything, this
+	 * is also true when we get a packet filter of 0
+	 */
+	if ((filter & ET131X_PACKET_TYPE_PROMISCUOUS) || filter == 0) {
+		pf_ctrl.bits.filter_broad_en = 0;
+		pf_ctrl.bits.filter_multi_en = 0;
+		pf_ctrl.bits.filter_uni_en = 0;
+	} else {
+		/*
+		 * Set us up with Multicast packet filtering.  Three cases are
+		 * possible - (1) we have a multi-cast list, (2) we receive ALL
+		 * multicast entries or (3) we receive none.
+		 */
+		if (filter & ET131X_PACKET_TYPE_ALL_MULTICAST) {
+			DBG_VERBOSE(et131x_dbginfo,
+				    "Multicast filtering OFF (Rx ALL MULTICAST)\n");
+			pf_ctrl.bits.filter_multi_en = 0;
+		} else {
+			DBG_VERBOSE(et131x_dbginfo, "Multicast filtering ON\n");
+			SetupDeviceForMulticast(adapter);
+			pf_ctrl.bits.filter_multi_en = 1;
+			ctrl.bits.pkt_filter_disable = 0;
+		}
+
+		/* Set us up with Unicast packet filtering */
+		if (filter & ET131X_PACKET_TYPE_DIRECTED) {
+			DBG_VERBOSE(et131x_dbginfo, "Unicast Filtering ON\n");
+			SetupDeviceForUnicast(adapter);
+			pf_ctrl.bits.filter_uni_en = 1;
+			ctrl.bits.pkt_filter_disable = 0;
+		}
+
+		/* Set us up with Broadcast packet filtering */
+		if (filter & ET131X_PACKET_TYPE_BROADCAST) {
+			DBG_VERBOSE(et131x_dbginfo, "Broadcast Filtering ON\n");
+			pf_ctrl.bits.filter_broad_en = 1;
+			ctrl.bits.pkt_filter_disable = 0;
+		} else {
+			DBG_VERBOSE(et131x_dbginfo,
+				    "Broadcast Filtering OFF\n");
+			pf_ctrl.bits.filter_broad_en = 0;
+		}
+
+		/* Setup the receive mac configuration registers - Packet
+		 * Filter control + the enable / disable for packet filter
+		 * in the control reg.
+		 */
+		writel(pf_ctrl.value,
+		       &adapter->CSRAddress->rxmac.pf_ctrl.value);
+		writel(ctrl.value, &adapter->CSRAddress->rxmac.ctrl.value);
+	}
+
+	DBG_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_multicast - The handler to configure multicasting on the interface
+ * @netdev: a pointer to a net_device struct representing the device
+ */
+void et131x_multicast(struct net_device *netdev)
+{
+	struct et131x_adapter *adapter = netdev_priv(netdev);
+	uint32_t PacketFilter = 0;
+	uint32_t count;
+	unsigned long lockflags;
+	struct dev_mc_list *mclist = netdev->mc_list;
+
+	DBG_ENTER(et131x_dbginfo);
+
+	spin_lock_irqsave(&adapter->Lock, lockflags);
+
+	/* Before we modify the platform-independent filter flags, store them
+	 * locally. This allows us to determine if anything's changed and if
+	 * we even need to bother the hardware
+	 */
+	PacketFilter = adapter->PacketFilter;
+
+	/* Clear the 'multicast' flag locally; becuase we only have a single
+	 * flag to check multicast, and multiple multicast addresses can be
+	 * set, this is the easiest way to determine if more than one
+	 * multicast address is being set.
+	 */
+	PacketFilter &= ~ET131X_PACKET_TYPE_MULTICAST;
+
+	/* Check the net_device flags and set the device independent flags
+	 * accordingly
+	 */
+	DBG_VERBOSE(et131x_dbginfo,
+		    "MULTICAST ADDR COUNT: %d\n", netdev->mc_count);
+
+	if (netdev->flags & IFF_PROMISC) {
+		DBG_VERBOSE(et131x_dbginfo, "Request: PROMISCUOUS MODE ON\n");
+		adapter->PacketFilter |= ET131X_PACKET_TYPE_PROMISCUOUS;
+	} else {
+		DBG_VERBOSE(et131x_dbginfo, "Request: PROMISCUOUS MODE OFF\n");
+		adapter->PacketFilter &= ~ET131X_PACKET_TYPE_PROMISCUOUS;
+	}
+
+	if (netdev->flags & IFF_ALLMULTI) {
+		DBG_VERBOSE(et131x_dbginfo, "Request: ACCEPT ALL MULTICAST\n");
+		adapter->PacketFilter |= ET131X_PACKET_TYPE_ALL_MULTICAST;
+	}
+
+	if (netdev->mc_count > NIC_MAX_MCAST_LIST) {
+		DBG_WARNING(et131x_dbginfo,
+			    "ACCEPT ALL MULTICAST for now, as there's more Multicast "
+			    "addresses than the HW supports\n");
+
+		adapter->PacketFilter |= ET131X_PACKET_TYPE_ALL_MULTICAST;
+	}
+
+	if (netdev->mc_count < 1) {
+		DBG_VERBOSE(et131x_dbginfo, "Request: REJECT ALL MULTICAST\n");
+		adapter->PacketFilter &= ~ET131X_PACKET_TYPE_ALL_MULTICAST;
+		adapter->PacketFilter &= ~ET131X_PACKET_TYPE_MULTICAST;
+	} else {
+		DBG_VERBOSE(et131x_dbginfo,
+			    "Request: SET MULTICAST FILTER(S)\n");
+		adapter->PacketFilter |= ET131X_PACKET_TYPE_MULTICAST;
+	}
+
+	/* Set values in the private adapter struct */
+	adapter->MCAddressCount = netdev->mc_count;
+
+	if (netdev->mc_count) {
+		if (mclist->dmi_addrlen != ETH_ALEN) {
+			DBG_WARNING(et131x_dbginfo,
+				    "Multicast addrs are not ETH_ALEN in size\n");
+		} else {
+			count = netdev->mc_count - 1;
+			memcpy(adapter->MCList[count], mclist->dmi_addr,
+			       ETH_ALEN);
+		}
+	}
+
+	/* Are the new flags different from the previous ones? If not, then no
+	 * action is required
+	 *
+	 * NOTE - This block will always update the MCList with the hardware,
+	 *        even if the addresses aren't the same.
+	 */
+	if (PacketFilter != adapter->PacketFilter) {
+		/* Call the device's filter function */
+		DBG_VERBOSE(et131x_dbginfo, "UPDATE REQUIRED, FLAGS changed\n");
+
+		et131x_set_packet_filter(adapter);
+	} else {
+		DBG_VERBOSE(et131x_dbginfo,
+			    "NO UPDATE REQUIRED, FLAGS didn't change\n");
+	}
+
+	spin_unlock_irqrestore(&adapter->Lock, lockflags);
+
+	DBG_LEAVE(et131x_dbginfo);
+}
+
+/**
+ * et131x_tx - The handler to tx a packet on the device
+ * @skb: data to be Tx'd
+ * @netdev: device on which data is to be Tx'd
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_tx(struct sk_buff *skb, struct net_device *netdev)
+{
+	int status = 0;
+
+	DBG_TX_ENTER(et131x_dbginfo);
+
+	/* Save the timestamp for the TX timeout watchdog */
+	netdev->trans_start = jiffies;
+
+	/* Call the device-specific data Tx routine */
+	status = et131x_send_packets(skb, netdev);
+
+	/* Check status and manage the netif queue if necessary */
+	if (status != 0) {
+		if (status == -ENOMEM) {
+			DBG_VERBOSE(et131x_dbginfo,
+				    "OUT OF TCBs; STOP NETIF QUEUE\n");
+
+			/* Put the queue to sleep until resources are
+			 * available
+			 */
+			netif_stop_queue(netdev);
+			status = 1;
+		} else {
+			DBG_WARNING(et131x_dbginfo,
+				    "Misc error; drop packet\n");
+			status = 0;
+		}
+	}
+
+	DBG_TX_LEAVE(et131x_dbginfo);
+	return status;
+}
+
+/**
+ * et131x_tx_timeout - Timeout handler
+ * @netdev: a pointer to a net_device struct representing the device
+ *
+ * The handler called when a Tx request times out. The timeout period is
+ * specified by the 'tx_timeo" element in the net_device structure (see
+ * et131x_alloc_device() to see how this value is set).
+ */
+void et131x_tx_timeout(struct net_device *netdev)
+{
+	struct et131x_adapter *pAdapter = netdev_priv(netdev);
+	PMP_TCB pMpTcb;
+	unsigned long lockflags;
+
+	DBG_WARNING(et131x_dbginfo, "TX TIMEOUT\n");
+
+	/* Just skip this part if the adapter is doing link detection */
+	if (MP_TEST_FLAG(pAdapter, fMP_ADAPTER_LINK_DETECTION)) {
+		DBG_ERROR(et131x_dbginfo, "Still doing link detection\n");
+		return;
+	}
+
+	/* Any nonrecoverable hardware error?
+	 * Checks adapter->flags for any failure in phy reading
+	 */
+	if (MP_TEST_FLAG(pAdapter, fMP_ADAPTER_NON_RECOVER_ERROR)) {
+		DBG_WARNING(et131x_dbginfo, "Non recoverable error - remove\n");
+		return;
+	}
+
+	/* Hardware failure? */
+	if (MP_TEST_FLAG(pAdapter, fMP_ADAPTER_HARDWARE_ERROR)) {
+		DBG_WARNING(et131x_dbginfo, "hardware error - reset\n");
+		return;
+	}
+
+	/* Is send stuck? */
+	spin_lock_irqsave(&pAdapter->TCBSendQLock, lockflags);
+
+	pMpTcb = pAdapter->TxRing.CurrSendHead;
+
+	if (pMpTcb != NULL) {
+		pMpTcb->Count++;
+
+		if (pMpTcb->Count > NIC_SEND_HANG_THRESHOLD) {
+#ifdef CONFIG_ET131X_DEBUG
+			TX_STATUS_BLOCK_t txDmaComplete =
+			    *(pAdapter->TxRing.pTxStatusVa);
+			PTX_DESC_ENTRY_t pDesc =
+			    pAdapter->TxRing.pTxDescRingVa +
+			    pMpTcb->WrIndex.bits.val;
+#endif
+			TX_DESC_ENTRY_t StuckDescriptors[10];
+
+			if (pMpTcb->WrIndex.bits.val > 7) {
+				memcpy(StuckDescriptors,
+				       pAdapter->TxRing.pTxDescRingVa +
+				       pMpTcb->WrIndex.bits.val - 6,
+				       sizeof(TX_DESC_ENTRY_t) * 10);
+			}
+
+			spin_unlock_irqrestore(&pAdapter->TCBSendQLock,
+					       lockflags);
+
+			DBG_WARNING(et131x_dbginfo,
+				    "Send stuck - reset.  pMpTcb->WrIndex %x, Flags 0x%08x\n",
+				    pMpTcb->WrIndex.bits.val,
+				    pMpTcb->Flags);
+
+			DBG_WARNING(et131x_dbginfo,
+				    "pDesc 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+				    pDesc->DataBufferPtrHigh,
+				    pDesc->DataBufferPtrLow, pDesc->word2.value,
+				    pDesc->word3.value);
+
+			DBG_WARNING(et131x_dbginfo,
+				    "WbStatus 0x%08x\n", txDmaComplete.value);
+
+#ifdef CONFIG_ET131X_DEBUG
+			DumpDeviceBlock(DBG_WARNING_ON, pAdapter, 0);
+			DumpDeviceBlock(DBG_WARNING_ON, pAdapter, 1);
+			DumpDeviceBlock(DBG_WARNING_ON, pAdapter, 3);
+			DumpDeviceBlock(DBG_WARNING_ON, pAdapter, 5);
+#endif
+			et131x_close(netdev);
+			et131x_open(netdev);
+
+			return;
+		}
+	}
+
+	spin_unlock_irqrestore(&pAdapter->TCBSendQLock, lockflags);
+}
+
+/**
+ * et131x_change_mtu - The handler called to change the MTU for the device
+ * @netdev: device whose MTU is to be changed
+ * @new_mtu: the desired MTU
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ */
+int et131x_change_mtu(struct net_device *netdev, int new_mtu)
+{
+	int result = 0;
+	struct et131x_adapter *adapter = netdev_priv(netdev);
+
+	DBG_ENTER(et131x_dbginfo);
+
+	/* Make sure the requested MTU is valid */
+	if (new_mtu == 0 || new_mtu > 9216) {
+		DBG_LEAVE(et131x_dbginfo);
+		return -EINVAL;
+	}
+
+	/* Stop the netif queue */
+	netif_stop_queue(netdev);
+
+	/* Stop the Tx and Rx DMA engines */
+	et131x_rx_dma_disable(adapter);
+	et131x_tx_dma_disable(adapter);
+
+	/* Disable device interrupts */
+	et131x_disable_interrupts(adapter);
+	et131x_handle_send_interrupt(adapter);
+	et131x_handle_recv_interrupt(adapter);
+
+	/* Set the new MTU */
+	netdev->mtu = new_mtu;
+
+	/* Free Rx DMA memory */
+	et131x_adapter_memory_free(adapter);
+
+	/* Set the config parameter for Jumbo Packet support */
+	adapter->RegistryJumboPacket = new_mtu + 14;
+	et131x_soft_reset(adapter);
+
+	/* Alloc and init Rx DMA memory */
+	result = et131x_adapter_memory_alloc(adapter);
+	if (result != 0) {
+		DBG_WARNING(et131x_dbginfo,
+			    "Change MTU failed; couldn't re-alloc DMA memory\n");
+		return result;
+	}
+
+	et131x_init_send(adapter);
+
+	et131x_setup_hardware_properties(adapter);
+	memcpy(netdev->dev_addr, adapter->CurrentAddress, ETH_ALEN);
+
+	/* Init the device with the new settings */
+	et131x_adapter_setup(adapter);
+
+	/* Enable interrupts */
+	if (MP_TEST_FLAG(adapter, fMP_ADAPTER_INTERRUPT_IN_USE)) {
+		et131x_enable_interrupts(adapter);
+	}
+
+	/* Restart the Tx and Rx DMA engines */
+	et131x_rx_dma_enable(adapter);
+	et131x_tx_dma_enable(adapter);
+
+	/* Restart the netif queue */
+	netif_wake_queue(netdev);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return result;
+}
+
+/**
+ * et131x_set_mac_addr - handler to change the MAC address for the device
+ * @netdev: device whose MAC is to be changed
+ * @new_mac: the desired MAC address
+ *
+ * Returns 0 on success, errno on failure (as defined in errno.h)
+ *
+ * IMPLEMENTED BY : blux http://berndlux.de 22.01.2007 21:14
+ */
+int et131x_set_mac_addr(struct net_device *netdev, void *new_mac)
+{
+	int result = 0;
+	struct et131x_adapter *adapter = netdev_priv(netdev);
+	struct sockaddr *address = new_mac;
+
+	DBG_ENTER(et131x_dbginfo);
+	// begin blux
+	// DBG_VERBOSE( et131x_dbginfo, "Function not implemented!!\n" );
+
+	if (adapter == NULL) {
+		DBG_LEAVE(et131x_dbginfo);
+		return -ENODEV;
+	}
+
+	/* Make sure the requested MAC is valid */
+	if (!is_valid_ether_addr(address->sa_data)) {
+		DBG_LEAVE(et131x_dbginfo);
+		return -EINVAL;
+	}
+
+	/* Stop the netif queue */
+	netif_stop_queue(netdev);
+
+	/* Stop the Tx and Rx DMA engines */
+	et131x_rx_dma_disable(adapter);
+	et131x_tx_dma_disable(adapter);
+
+	/* Disable device interrupts */
+	et131x_disable_interrupts(adapter);
+	et131x_handle_send_interrupt(adapter);
+	et131x_handle_recv_interrupt(adapter);
+
+	/* Set the new MAC */
+	// netdev->set_mac_address  = &new_mac;
+	// netdev->mtu = new_mtu;
+
+	memcpy(netdev->dev_addr, address->sa_data, netdev->addr_len);
+
+	printk("%s: Setting MAC address to %02x:%02x:%02x:%02x:%02x:%02x\n",
+	       netdev->name, netdev->dev_addr[0], netdev->dev_addr[1],
+	       netdev->dev_addr[2], netdev->dev_addr[3], netdev->dev_addr[4],
+	       netdev->dev_addr[5]);
+
+	/* Free Rx DMA memory */
+	et131x_adapter_memory_free(adapter);
+
+	/* Set the config parameter for Jumbo Packet support */
+	// adapter->RegistryJumboPacket = new_mtu + 14;
+	// blux: not needet here, w'll change the MAC
+
+	et131x_soft_reset(adapter);
+
+	/* Alloc and init Rx DMA memory */
+	result = et131x_adapter_memory_alloc(adapter);
+	if (result != 0) {
+		DBG_WARNING(et131x_dbginfo,
+			    "Change MAC failed; couldn't re-alloc DMA memory\n");
+		return result;
+	}
+
+	et131x_init_send(adapter);
+
+	et131x_setup_hardware_properties(adapter);
+	// memcpy( netdev->dev_addr, adapter->CurrentAddress, ETH_ALEN );
+	// blux: no, do not override our nice address
+
+	/* Init the device with the new settings */
+	et131x_adapter_setup(adapter);
+
+	/* Enable interrupts */
+	if (MP_TEST_FLAG(adapter, fMP_ADAPTER_INTERRUPT_IN_USE)) {
+		et131x_enable_interrupts(adapter);
+	}
+
+	/* Restart the Tx and Rx DMA engines */
+	et131x_rx_dma_enable(adapter);
+	et131x_tx_dma_enable(adapter);
+
+	/* Restart the netif queue */
+	netif_wake_queue(netdev);
+
+	DBG_LEAVE(et131x_dbginfo);
+	return result;
+}
diff --git a/drivers/staging/et131x/et131x_netdev.h b/drivers/staging/et131x/et131x_netdev.h
new file mode 100644
index 0000000..b8acd14
--- /dev/null
+++ b/drivers/staging/et131x/et131x_netdev.h
@@ -0,0 +1,64 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_netdev.h - Defines, structs, enums, prototypes, etc. related to the
+ *                   driver's net_device support.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_NETDEV_H__
+#define __ET131X_NETDEV_H__
+
+struct net_device *et131x_device_alloc(void);
+
+#endif /* __ET131X_NETDEV_H__ */
diff --git a/drivers/staging/et131x/et131x_version.h b/drivers/staging/et131x/et131x_version.h
new file mode 100644
index 0000000..2ea645e
--- /dev/null
+++ b/drivers/staging/et131x/et131x_version.h
@@ -0,0 +1,81 @@
+/*
+ * Agere Systems Inc.
+ * 10/100/1000 Base-T Ethernet Driver for the ET1301 and ET131x series MACs
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *   http://www.agere.com
+ *
+ *------------------------------------------------------------------------------
+ *
+ * et131x_version.h - This file provides system and device version information.
+ *
+ *------------------------------------------------------------------------------
+ *
+ * SOFTWARE LICENSE
+ *
+ * This software is provided subject to the following terms and conditions,
+ * which you should read carefully before using the software.  Using this
+ * software indicates your acceptance of these terms and conditions.  If you do
+ * not agree with these terms and conditions, do not use the software.
+ *
+ * Copyright © 2005 Agere Systems Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source or binary forms, with or without
+ * modifications, are permitted provided that the following conditions are met:
+ *
+ * . Redistributions of source code must retain the above copyright notice, this
+ *    list of conditions and the following Disclaimer as comments in the code as
+ *    well as in the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * . Redistributions in binary form must reproduce the above copyright notice,
+ *    this list of conditions and the following Disclaimer in the documentation
+ *    and/or other materials provided with the distribution.
+ *
+ * . Neither the name of Agere Systems Inc. nor the names of the contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * Disclaimer
+ *
+ * THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, INFRINGEMENT AND THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  ANY
+ * USE, MODIFICATION OR DISTRIBUTION OF THIS SOFTWARE IS SOLELY AT THE USERS OWN
+ * RISK. IN NO EVENT SHALL AGERE SYSTEMS INC. OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, INCLUDING, BUT NOT LIMITED TO, CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ */
+
+#ifndef __ET131X_VERSION_H__
+#define __ET131X_VERSION_H__
+
+#define DRIVER_AUTHOR		"Victor Soriano (vjsoriano@agere.com)"
+#define DRIVER_LICENSE		"Dual BSD/GPL"
+#define DRIVER_DEVICE_STRING	"ET1310"
+#define DRIVER_NAME		"et131x"
+#define DRIVER_MAJOR_VERSION	1
+#define DRIVER_MINOR_VERSION	2
+#define DRIVER_PATCH_VERSION	3
+#define DRIVER_VERSION_STRING	"1.2.3"
+#define DRIVER_VENDOR		"Agere Systems, http://www.agere.com"
+#define DRIVER_DESC		"10/100/1000 Base-T Ethernet Driver"
+
+#define STRUCT_MODULE		"net"	/* blux: missed by the kernel */
+
+#define DRIVER_INFO		DRIVER_DESC " for the "\
+				DRIVER_DEVICE_STRING ", v" \
+				DRIVER_VERSION_STRING " by " \
+				DRIVER_VENDOR
+
+#define DRIVER_NAME_EXT		"et131x.ko"
+
+#endif /* __ET131X_VERSION_H__ */
-- 
1.6.0.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 09/23] Staging: add me4000 pci data collection driver
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
                   ` (4 preceding siblings ...)
  2008-10-10 22:42 ` [PATCH 05/23] Staging: add et131x network driver Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-15  8:41   ` Andrew Morton
  2008-10-10 22:42 ` [PATCH 10/23] Staging: add the go7007 video driver Greg KH
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: Greg Kroah-Hartman, Wolfgang Beiter, Guenter Gebhardt

From: Greg Kroah-Hartman <gregkh@suse.de>

Originally written by Guenter Gebhardt <g.gebhardt@meilhaus.de>

TODO:
	- checkpatch.pl cleanups
	- sparse cleanups
	- possible /proc interaction cleanups
	- more info needed for Kconfig entry
	- real device id?
	- module parameter cleanup

Cc: Wolfgang Beiter <w.beiter@aon.at>
Cc: Guenter Gebhardt <g.gebhardt@meilhaus.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/staging/Kconfig         |    2 +
 drivers/staging/Makefile        |    1 +
 drivers/staging/me4000/Kconfig  |   10 +
 drivers/staging/me4000/Makefile |    1 +
 drivers/staging/me4000/README   |   13 +
 drivers/staging/me4000/me4000.c | 6133 +++++++++++++++++++++++++++++++++++++++
 drivers/staging/me4000/me4000.h |  954 ++++++
 7 files changed, 7114 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/me4000/Kconfig
 create mode 100644 drivers/staging/me4000/Makefile
 create mode 100644 drivers/staging/me4000/README
 create mode 100644 drivers/staging/me4000/me4000.c
 create mode 100644 drivers/staging/me4000/me4000.h

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 6da7662..56c73bc 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -29,4 +29,6 @@ source "drivers/staging/slicoss/Kconfig"
 
 source "drivers/staging/sxg/Kconfig"
 
+source "drivers/staging/me4000/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index cd6d6a5..97df19b 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -3,3 +3,4 @@
 obj-$(CONFIG_ET131X)		+= et131x/
 obj-$(CONFIG_SLICOSS)		+= slicoss/
 obj-$(CONFIG_SXG)		+= sxg/
+obj-$(CONFIG_ME4000)		+= me4000/
diff --git a/drivers/staging/me4000/Kconfig b/drivers/staging/me4000/Kconfig
new file mode 100644
index 0000000..5e6c9de
--- /dev/null
+++ b/drivers/staging/me4000/Kconfig
@@ -0,0 +1,10 @@
+config ME4000
+	tristate "Meilhaus ME-4000 support"
+	default n
+	depends on PCI
+	help
+	  This driver supports the Meilhaus ME-4000 family of boards
+	  that do data collection and multipurpose I/O.
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called me4000.
diff --git a/drivers/staging/me4000/Makefile b/drivers/staging/me4000/Makefile
new file mode 100644
index 0000000..74487cd
--- /dev/null
+++ b/drivers/staging/me4000/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ME4000)		+= me4000.o
diff --git a/drivers/staging/me4000/README b/drivers/staging/me4000/README
new file mode 100644
index 0000000..bbb8386
--- /dev/null
+++ b/drivers/staging/me4000/README
@@ -0,0 +1,13 @@
+
+TODO:
+	- checkpatch.pl cleanups
+	- sparse cleanups
+	- possible /proc interaction cleanups
+	- more info needed for Kconfig entry
+	- real device id?
+	- module parameter cleanup
+
+Please send patches to Greg Kroah-Hartman <gregkh@suse.de>
+and Cc: Wolfgang Beiter <w.beiter@aon.at> and
+Guenter Gebhardt <g.gebhardt@meilhaus.de>
+
diff --git a/drivers/staging/me4000/me4000.c b/drivers/staging/me4000/me4000.c
new file mode 100644
index 0000000..862dd7f
--- /dev/null
+++ b/drivers/staging/me4000/me4000.c
@@ -0,0 +1,6133 @@
+/* Device driver for Meilhaus ME-4000 board family.
+ * ================================================
+ *
+ *  Copyright (C) 2003 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ *  This file is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  Author:	Guenter Gebhardt	<g.gebhardt@meilhaus.de>
+ */
+
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/unistd.h>
+#include <linux/list.h>
+#include <linux/proc_fs.h>
+
+#include <linux/poll.h>
+#include <linux/vmalloc.h>
+#include <asm/pgtable.h>
+#include <asm/uaccess.h>
+#include <linux/types.h>
+
+#include <linux/slab.h>
+
+/* Include-File for the Meilhaus ME-4000 I/O board */
+#include "me4000.h"
+#include "me4000_firmware.h"
+#include "me4610_firmware.h"
+
+/* Administrative stuff for modinfo */
+MODULE_AUTHOR("Guenter Gebhardt <g.gebhardt@meilhaus.de>");
+MODULE_DESCRIPTION
+    ("Device Driver Module for Meilhaus ME-4000 boards version 1.0.5");
+MODULE_SUPPORTED_DEVICE("Meilhaus ME-4000 Multi I/O boards");
+MODULE_LICENSE("GPL");
+
+/* Board specific data are kept in a global list */
+LIST_HEAD(me4000_board_info_list);
+
+/* Major Device Numbers. 0 means to get it automatically from the System */
+static int me4000_ao_major_driver_no = 0;
+static int me4000_ai_major_driver_no = 0;
+static int me4000_dio_major_driver_no = 0;
+static int me4000_cnt_major_driver_no = 0;
+static int me4000_ext_int_major_driver_no = 0;
+
+/* Let the user specify a custom major driver number */
+module_param(me4000_ao_major_driver_no, int, 0);
+MODULE_PARM_DESC(me4000_ao_major_driver_no,
+		 "Major driver number for analog output (default 0)");
+
+module_param(me4000_ai_major_driver_no, int, 0);
+MODULE_PARM_DESC(me4000_ai_major_driver_no,
+		 "Major driver number for analog input (default 0)");
+
+module_param(me4000_dio_major_driver_no, int, 0);
+MODULE_PARM_DESC(me4000_dio_major_driver_no,
+		 "Major driver number digital I/O (default 0)");
+
+module_param(me4000_cnt_major_driver_no, int, 0);
+MODULE_PARM_DESC(me4000_cnt_major_driver_no,
+		 "Major driver number for counter (default 0)");
+
+module_param(me4000_ext_int_major_driver_no, int, 0);
+MODULE_PARM_DESC(me4000_ext_int_major_driver_no,
+		 "Major driver number for external interrupt (default 0)");
+
+/*-----------------------------------------------------------------------------
+  Module stuff
+  ---------------------------------------------------------------------------*/
+int init_module(void);
+void cleanup_module(void);
+
+/*-----------------------------------------------------------------------------
+  Board detection and initialization
+  ---------------------------------------------------------------------------*/
+static int me4000_probe(struct pci_dev *dev, const struct pci_device_id *id);
+static int me4000_xilinx_download(me4000_info_t *);
+static int me4000_reset_board(me4000_info_t *);
+
+static void clear_board_info_list(void);
+static int get_registers(struct pci_dev *dev, me4000_info_t * info);
+static int init_board_info(struct pci_dev *dev, me4000_info_t * board_info);
+static int alloc_ao_contexts(me4000_info_t * info);
+static void release_ao_contexts(me4000_info_t * board_info);
+static int alloc_ai_context(me4000_info_t * info);
+static int alloc_dio_context(me4000_info_t * info);
+static int alloc_cnt_context(me4000_info_t * info);
+static int alloc_ext_int_context(me4000_info_t * info);
+
+/*-----------------------------------------------------------------------------
+  Stuff used by all device parts
+  ---------------------------------------------------------------------------*/
+static int me4000_open(struct inode *, struct file *);
+static int me4000_release(struct inode *, struct file *);
+
+static int me4000_get_user_info(me4000_user_info_t *,
+				me4000_info_t * board_info);
+static int me4000_read_procmem(char *, char **, off_t, int, int *, void *);
+
+/*-----------------------------------------------------------------------------
+  Analog output stuff
+  ---------------------------------------------------------------------------*/
+static ssize_t me4000_ao_write_sing(struct file *, const char *, size_t,
+				    loff_t *);
+static ssize_t me4000_ao_write_wrap(struct file *, const char *, size_t,
+				    loff_t *);
+static ssize_t me4000_ao_write_cont(struct file *, const char *, size_t,
+				    loff_t *);
+
+static int me4000_ao_ioctl_sing(struct inode *, struct file *, unsigned int,
+				unsigned long);
+static int me4000_ao_ioctl_wrap(struct inode *, struct file *, unsigned int,
+				unsigned long);
+static int me4000_ao_ioctl_cont(struct inode *, struct file *, unsigned int,
+				unsigned long);
+
+static unsigned int me4000_ao_poll_cont(struct file *, poll_table *);
+static int me4000_ao_fsync_cont(struct file *, struct dentry *, int);
+
+static int me4000_ao_start(unsigned long *, me4000_ao_context_t *);
+static int me4000_ao_stop(me4000_ao_context_t *);
+static int me4000_ao_immediate_stop(me4000_ao_context_t *);
+static int me4000_ao_timer_set_divisor(u32 *, me4000_ao_context_t *);
+static int me4000_ao_preload(me4000_ao_context_t *);
+static int me4000_ao_preload_update(me4000_ao_context_t *);
+static int me4000_ao_ex_trig_set_edge(int *, me4000_ao_context_t *);
+static int me4000_ao_ex_trig_enable(me4000_ao_context_t *);
+static int me4000_ao_ex_trig_disable(me4000_ao_context_t *);
+static int me4000_ao_prepare(me4000_ao_context_t * ao_info);
+static int me4000_ao_reset(me4000_ao_context_t * ao_info);
+static int me4000_ao_enable_do(me4000_ao_context_t *);
+static int me4000_ao_disable_do(me4000_ao_context_t *);
+static int me4000_ao_fsm_state(int *, me4000_ao_context_t *);
+
+static int me4000_ao_simultaneous_ex_trig(me4000_ao_context_t * ao_context);
+static int me4000_ao_simultaneous_sw(me4000_ao_context_t * ao_context);
+static int me4000_ao_simultaneous_disable(me4000_ao_context_t * ao_context);
+static int me4000_ao_simultaneous_update(me4000_ao_channel_list_t * channels,
+					 me4000_ao_context_t * ao_context);
+
+static int me4000_ao_synchronous_ex_trig(me4000_ao_context_t * ao_context);
+static int me4000_ao_synchronous_sw(me4000_ao_context_t * ao_context);
+static int me4000_ao_synchronous_disable(me4000_ao_context_t * ao_context);
+
+static int me4000_ao_ex_trig_timeout(unsigned long *arg,
+				     me4000_ao_context_t * ao_context);
+static int me4000_ao_get_free_buffer(unsigned long *arg,
+				     me4000_ao_context_t * ao_context);
+
+/*-----------------------------------------------------------------------------
+  Analog input stuff
+  ---------------------------------------------------------------------------*/
+static int me4000_ai_single(me4000_ai_single_t *, me4000_ai_context_t *);
+static int me4000_ai_ioctl_sing(struct inode *, struct file *, unsigned int,
+				unsigned long);
+
+static ssize_t me4000_ai_read(struct file *, char *, size_t, loff_t *);
+static int me4000_ai_ioctl_sw(struct inode *, struct file *, unsigned int,
+			      unsigned long);
+static unsigned int me4000_ai_poll(struct file *, poll_table *);
+static int me4000_ai_fasync(int fd, struct file *file_p, int mode);
+
+static int me4000_ai_ioctl_ext(struct inode *, struct file *, unsigned int,
+			       unsigned long);
+
+static int me4000_ai_prepare(me4000_ai_context_t * ai_context);
+static int me4000_ai_reset(me4000_ai_context_t * ai_context);
+static int me4000_ai_config(me4000_ai_config_t *, me4000_ai_context_t *);
+static int me4000_ai_start(me4000_ai_context_t *);
+static int me4000_ai_start_ex(unsigned long *, me4000_ai_context_t *);
+static int me4000_ai_stop(me4000_ai_context_t *);
+static int me4000_ai_immediate_stop(me4000_ai_context_t *);
+static int me4000_ai_ex_trig_enable(me4000_ai_context_t *);
+static int me4000_ai_ex_trig_disable(me4000_ai_context_t *);
+static int me4000_ai_ex_trig_setup(me4000_ai_trigger_t *,
+				   me4000_ai_context_t *);
+static int me4000_ai_sc_setup(me4000_ai_sc_t * arg,
+			      me4000_ai_context_t * ai_context);
+static int me4000_ai_offset_enable(me4000_ai_context_t * ai_context);
+static int me4000_ai_offset_disable(me4000_ai_context_t * ai_context);
+static int me4000_ai_fullscale_enable(me4000_ai_context_t * ai_context);
+static int me4000_ai_fullscale_disable(me4000_ai_context_t * ai_context);
+static int me4000_ai_fsm_state(int *arg, me4000_ai_context_t * ai_context);
+static int me4000_ai_get_count_buffer(unsigned long *arg,
+				      me4000_ai_context_t * ai_context);
+
+/*-----------------------------------------------------------------------------
+  EEPROM stuff
+  ---------------------------------------------------------------------------*/
+static int me4000_eeprom_read(me4000_eeprom_t * arg,
+			      me4000_ai_context_t * ai_context);
+static int me4000_eeprom_write(me4000_eeprom_t * arg,
+			       me4000_ai_context_t * ai_context);
+static unsigned short eeprom_read_cmd(me4000_ai_context_t * ai_context,
+				      unsigned long cmd, int length);
+static int eeprom_write_cmd(me4000_ai_context_t * ai_context, unsigned long cmd,
+			    int length);
+
+/*-----------------------------------------------------------------------------
+  Digital I/O stuff
+  ---------------------------------------------------------------------------*/
+static int me4000_dio_ioctl(struct inode *, struct file *, unsigned int,
+			    unsigned long);
+static int me4000_dio_config(me4000_dio_config_t *, me4000_dio_context_t *);
+static int me4000_dio_get_byte(me4000_dio_byte_t *, me4000_dio_context_t *);
+static int me4000_dio_set_byte(me4000_dio_byte_t *, me4000_dio_context_t *);
+static int me4000_dio_reset(me4000_dio_context_t *);
+
+/*-----------------------------------------------------------------------------
+  Counter stuff
+  ---------------------------------------------------------------------------*/
+static int me4000_cnt_ioctl(struct inode *, struct file *, unsigned int,
+			    unsigned long);
+static int me4000_cnt_config(me4000_cnt_config_t *, me4000_cnt_context_t *);
+static int me4000_cnt_read(me4000_cnt_t *, me4000_cnt_context_t *);
+static int me4000_cnt_write(me4000_cnt_t *, me4000_cnt_context_t *);
+static int me4000_cnt_reset(me4000_cnt_context_t *);
+
+/*-----------------------------------------------------------------------------
+  External interrupt routines
+  ---------------------------------------------------------------------------*/
+static int me4000_ext_int_ioctl(struct inode *, struct file *, unsigned int,
+				unsigned long);
+static int me4000_ext_int_enable(me4000_ext_int_context_t *);
+static int me4000_ext_int_disable(me4000_ext_int_context_t *);
+static int me4000_ext_int_count(unsigned long *arg,
+				me4000_ext_int_context_t * ext_int_context);
+static int me4000_ext_int_fasync(int fd, struct file *file_ptr, int mode);
+
+/*-----------------------------------------------------------------------------
+  The interrupt service routines
+  ---------------------------------------------------------------------------*/
+static irqreturn_t me4000_ao_isr(int, void *);
+static irqreturn_t me4000_ai_isr(int, void *);
+static irqreturn_t me4000_ext_int_isr(int, void *);
+
+/*-----------------------------------------------------------------------------
+  Inline functions
+  ---------------------------------------------------------------------------*/
+static int inline me4000_buf_count(me4000_circ_buf_t, int);
+static int inline me4000_buf_space(me4000_circ_buf_t, int);
+static int inline me4000_space_to_end(me4000_circ_buf_t, int);
+static int inline me4000_values_to_end(me4000_circ_buf_t, int);
+
+static void inline me4000_outb(unsigned char value, unsigned long port);
+static void inline me4000_outl(unsigned long value, unsigned long port);
+static unsigned long inline me4000_inl(unsigned long port);
+static unsigned char inline me4000_inb(unsigned long port);
+
+static int me4000_buf_count(me4000_circ_buf_t buf, int size)
+{
+	return ((buf.head - buf.tail) & (size - 1));
+}
+
+static int me4000_buf_space(me4000_circ_buf_t buf, int size)
+{
+	return ((buf.tail - (buf.head + 1)) & (size - 1));
+}
+
+static int me4000_values_to_end(me4000_circ_buf_t buf, int size)
+{
+	int end;
+	int n;
+	end = size - buf.tail;
+	n = (buf.head + end) & (size - 1);
+	return (n < end) ? n : end;
+}
+
+static int me4000_space_to_end(me4000_circ_buf_t buf, int size)
+{
+	int end;
+	int n;
+
+	end = size - 1 - buf.head;
+	n = (end + buf.tail) & (size - 1);
+	return (n <= end) ? n : (end + 1);
+}
+
+static void me4000_outb(unsigned char value, unsigned long port)
+{
+	PORT_PDEBUG("--> 0x%02X port 0x%04lX\n", value, port);
+	outb(value, port);
+}
+
+static void me4000_outl(unsigned long value, unsigned long port)
+{
+	PORT_PDEBUG("--> 0x%08lX port 0x%04lX\n", value, port);
+	outl(value, port);
+}
+
+static unsigned long me4000_inl(unsigned long port)
+{
+	unsigned long value;
+	value = inl(port);
+	PORT_PDEBUG("<-- 0x%08lX port 0x%04lX\n", value, port);
+	return value;
+}
+
+static unsigned char me4000_inb(unsigned long port)
+{
+	unsigned char value;
+	value = inb(port);
+	PORT_PDEBUG("<-- 0x%08X port 0x%04lX\n", value, port);
+	return value;
+}
+
+struct pci_driver me4000_driver = {
+	.name = ME4000_NAME,
+	.id_table = me4000_pci_table,
+	.probe = me4000_probe
+};
+
+static struct file_operations me4000_ao_fops_sing = {
+      owner:THIS_MODULE,
+      write:me4000_ao_write_sing,
+      ioctl:me4000_ao_ioctl_sing,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_ao_fops_wrap = {
+      owner:THIS_MODULE,
+      write:me4000_ao_write_wrap,
+      ioctl:me4000_ao_ioctl_wrap,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_ao_fops_cont = {
+      owner:THIS_MODULE,
+      write:me4000_ao_write_cont,
+      poll:me4000_ao_poll_cont,
+      ioctl:me4000_ao_ioctl_cont,
+      open:me4000_open,
+      release:me4000_release,
+      fsync:me4000_ao_fsync_cont,
+};
+
+static struct file_operations me4000_ai_fops_sing = {
+      owner:THIS_MODULE,
+      ioctl:me4000_ai_ioctl_sing,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_ai_fops_cont_sw = {
+      owner:THIS_MODULE,
+      read:me4000_ai_read,
+      poll:me4000_ai_poll,
+      ioctl:me4000_ai_ioctl_sw,
+      open:me4000_open,
+      release:me4000_release,
+      fasync:me4000_ai_fasync,
+};
+
+static struct file_operations me4000_ai_fops_cont_et = {
+      owner:THIS_MODULE,
+      read:me4000_ai_read,
+      poll:me4000_ai_poll,
+      ioctl:me4000_ai_ioctl_ext,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_ai_fops_cont_et_value = {
+      owner:THIS_MODULE,
+      read:me4000_ai_read,
+      poll:me4000_ai_poll,
+      ioctl:me4000_ai_ioctl_ext,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_ai_fops_cont_et_chanlist = {
+      owner:THIS_MODULE,
+      read:me4000_ai_read,
+      poll:me4000_ai_poll,
+      ioctl:me4000_ai_ioctl_ext,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_dio_fops = {
+      owner:THIS_MODULE,
+      ioctl:me4000_dio_ioctl,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_cnt_fops = {
+      owner:THIS_MODULE,
+      ioctl:me4000_cnt_ioctl,
+      open:me4000_open,
+      release:me4000_release,
+};
+
+static struct file_operations me4000_ext_int_fops = {
+      owner:THIS_MODULE,
+      ioctl:me4000_ext_int_ioctl,
+      open:me4000_open,
+      release:me4000_release,
+      fasync:me4000_ext_int_fasync,
+};
+
+static struct file_operations *me4000_ao_fops_array[] = {
+	&me4000_ao_fops_sing,	// single operations
+	&me4000_ao_fops_wrap,	// wraparound operations
+	&me4000_ao_fops_cont,	// continous operations
+};
+
+static struct file_operations *me4000_ai_fops_array[] = {
+	&me4000_ai_fops_sing,	// single operations
+	&me4000_ai_fops_cont_sw,	// continuous operations with software start
+	&me4000_ai_fops_cont_et,	// continous operations with external trigger
+	&me4000_ai_fops_cont_et_value,	// sample values by external trigger
+	&me4000_ai_fops_cont_et_chanlist,	// work through one channel list by external trigger
+};
+
+int __init me4000_init_module(void)
+{
+	int result = 0;
+
+	CALL_PDEBUG("init_module() is executed\n");
+
+	/* Register driver capabilities */
+	result = pci_register_driver(&me4000_driver);
+	PDEBUG("init_module():%d devices detected\n", result);
+	if (result < 0) {
+		printk(KERN_ERR "ME4000:init_module():Can't register driver\n");
+		goto INIT_ERROR_1;
+	}
+
+	/* Allocate major number for analog output */
+	result =
+	    register_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME,
+			    &me4000_ao_fops_sing);
+	if (result < 0) {
+		printk(KERN_ERR "ME4000:init_module():Can't get AO major no\n");
+		goto INIT_ERROR_2;
+	} else {
+		me4000_ao_major_driver_no = result;
+	}
+	PDEBUG("init_module():Major driver number for AO = %ld\n",
+	       me4000_ao_major_driver_no);
+
+	/* Allocate major number for analog input  */
+	result =
+	    register_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME,
+			    &me4000_ai_fops_sing);
+	if (result < 0) {
+		printk(KERN_ERR "ME4000:init_module():Can't get AI major no\n");
+		goto INIT_ERROR_3;
+	} else {
+		me4000_ai_major_driver_no = result;
+	}
+	PDEBUG("init_module():Major driver number for AI = %ld\n",
+	       me4000_ai_major_driver_no);
+
+	/* Allocate major number for digital I/O */
+	result =
+	    register_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME,
+			    &me4000_dio_fops);
+	if (result < 0) {
+		printk(KERN_ERR
+		       "ME4000:init_module():Can't get DIO major no\n");
+		goto INIT_ERROR_4;
+	} else {
+		me4000_dio_major_driver_no = result;
+	}
+	PDEBUG("init_module():Major driver number for DIO = %ld\n",
+	       me4000_dio_major_driver_no);
+
+	/* Allocate major number for counter */
+	result =
+	    register_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME,
+			    &me4000_cnt_fops);
+	if (result < 0) {
+		printk(KERN_ERR
+		       "ME4000:init_module():Can't get CNT major no\n");
+		goto INIT_ERROR_5;
+	} else {
+		me4000_cnt_major_driver_no = result;
+	}
+	PDEBUG("init_module():Major driver number for CNT = %ld\n",
+	       me4000_cnt_major_driver_no);
+
+	/* Allocate major number for external interrupt */
+	result =
+	    register_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME,
+			    &me4000_ext_int_fops);
+	if (result < 0) {
+		printk(KERN_ERR
+		       "ME4000:init_module():Can't get major no for external interrupt\n");
+		goto INIT_ERROR_6;
+	} else {
+		me4000_ext_int_major_driver_no = result;
+	}
+	PDEBUG
+	    ("init_module():Major driver number for external interrupt = %ld\n",
+	     me4000_ext_int_major_driver_no);
+
+	/* Create the /proc/me4000 entry */
+	if (!create_proc_read_entry
+	    ("me4000", 0, NULL, me4000_read_procmem, NULL)) {
+		result = -ENODEV;
+		printk(KERN_ERR
+		       "ME4000:init_module():Can't create proc entry\n");
+		goto INIT_ERROR_7;
+	}
+
+	return 0;
+
+      INIT_ERROR_7:
+	unregister_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME);
+
+      INIT_ERROR_6:
+	unregister_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME);
+
+      INIT_ERROR_5:
+	unregister_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME);
+
+      INIT_ERROR_4:
+	unregister_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME);
+
+      INIT_ERROR_3:
+	unregister_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME);
+
+      INIT_ERROR_2:
+	pci_unregister_driver(&me4000_driver);
+	clear_board_info_list();
+
+      INIT_ERROR_1:
+	return result;
+}
+
+module_init(me4000_init_module);
+
+static void clear_board_info_list(void)
+{
+	struct list_head *board_p;
+	struct list_head *dac_p;
+	me4000_info_t *board_info;
+	me4000_ao_context_t *ao_context;
+
+	/* Clear context lists */
+	for (board_p = me4000_board_info_list.next;
+	     board_p != &me4000_board_info_list; board_p = board_p->next) {
+		board_info = list_entry(board_p, me4000_info_t, list);
+		/* Clear analog output context list */
+		while (!list_empty(&board_info->ao_context_list)) {
+			dac_p = board_info->ao_context_list.next;
+			ao_context =
+			    list_entry(dac_p, me4000_ao_context_t, list);
+			me4000_ao_reset(ao_context);
+			free_irq(ao_context->irq, ao_context);
+			if (ao_context->circ_buf.buf)
+				kfree(ao_context->circ_buf.buf);
+			list_del(dac_p);
+			kfree(ao_context);
+		}
+
+		/* Clear analog input context */
+		if (board_info->ai_context->circ_buf.buf)
+			kfree(board_info->ai_context->circ_buf.buf);
+		kfree(board_info->ai_context);
+
+		/* Clear digital I/O context */
+		kfree(board_info->dio_context);
+
+		/* Clear counter context */
+		kfree(board_info->cnt_context);
+
+		/* Clear external interrupt context */
+		kfree(board_info->ext_int_context);
+	}
+
+	/* Clear the board info list */
+	while (!list_empty(&me4000_board_info_list)) {
+		board_p = me4000_board_info_list.next;
+		board_info = list_entry(board_p, me4000_info_t, list);
+		pci_release_regions(board_info->pci_dev_p);
+		list_del(board_p);
+		kfree(board_info);
+	}
+}
+
+static int get_registers(struct pci_dev *dev, me4000_info_t * board_info)
+{
+
+	/*--------------------------- plx regbase ---------------------------------*/
+
+	board_info->plx_regbase = pci_resource_start(dev, 1);
+	if (board_info->plx_regbase == 0) {
+		printk(KERN_ERR
+		       "ME4000:get_registers():PCI base address 1 is not available\n");
+		return -ENODEV;
+	}
+	board_info->plx_regbase_size = pci_resource_len(dev, 1);
+
+	PDEBUG
+	    ("get_registers():PLX configuration registers at address 0x%4lX [0x%4lX]\n",
+	     board_info->plx_regbase, board_info->plx_regbase_size);
+
+	/*--------------------------- me4000 regbase ------------------------------*/
+
+	board_info->me4000_regbase = pci_resource_start(dev, 2);
+	if (board_info->me4000_regbase == 0) {
+		printk(KERN_ERR
+		       "ME4000:get_registers():PCI base address 2 is not available\n");
+		return -ENODEV;
+	}
+	board_info->me4000_regbase_size = pci_resource_len(dev, 2);
+
+	PDEBUG("get_registers():ME4000 registers at address 0x%4lX [0x%4lX]\n",
+	       board_info->me4000_regbase, board_info->me4000_regbase_size);
+
+	/*--------------------------- timer regbase ------------------------------*/
+
+	board_info->timer_regbase = pci_resource_start(dev, 3);
+	if (board_info->timer_regbase == 0) {
+		printk(KERN_ERR
+		       "ME4000:get_registers():PCI base address 3 is not available\n");
+		return -ENODEV;
+	}
+	board_info->timer_regbase_size = pci_resource_len(dev, 3);
+
+	PDEBUG("get_registers():Timer registers at address 0x%4lX [0x%4lX]\n",
+	       board_info->timer_regbase, board_info->timer_regbase_size);
+
+	/*--------------------------- program regbase ------------------------------*/
+
+	board_info->program_regbase = pci_resource_start(dev, 5);
+	if (board_info->program_regbase == 0) {
+		printk(KERN_ERR
+		       "get_registers():ME4000:PCI base address 5 is not available\n");
+		return -ENODEV;
+	}
+	board_info->program_regbase_size = pci_resource_len(dev, 5);
+
+	PDEBUG("get_registers():Program registers at address 0x%4lX [0x%4lX]\n",
+	       board_info->program_regbase, board_info->program_regbase_size);
+
+	return 0;
+}
+
+static int init_board_info(struct pci_dev *pci_dev_p,
+			   me4000_info_t * board_info)
+{
+	int i;
+	int result;
+	struct list_head *board_p;
+	board_info->pci_dev_p = pci_dev_p;
+
+	for (i = 0; i < ME4000_BOARD_VERSIONS; i++) {
+		if (me4000_boards[i].device_id == pci_dev_p->device) {
+			board_info->board_p = &me4000_boards[i];
+			break;
+		}
+	}
+	if (i == ME4000_BOARD_VERSIONS) {
+		printk(KERN_ERR
+		       "ME4000:init_board_info():Device ID not valid\n");
+		return -ENODEV;
+	}
+
+	/* Get the index of the board in the global list */
+	for (board_p = me4000_board_info_list.next, i = 0;
+	     board_p != &me4000_board_info_list; board_p = board_p->next, i++) {
+		if (board_p == &board_info->list) {
+			board_info->board_count = i;
+			break;
+		}
+	}
+	if (board_p == &me4000_board_info_list) {
+		printk(KERN_ERR
+		       "ME4000:init_board_info():Cannot get index of baord\n");
+		return -ENODEV;
+	}
+
+	/* Init list head for analog output contexts */
+	INIT_LIST_HEAD(&board_info->ao_context_list);
+
+	/* Init spin locks */
+	spin_lock_init(&board_info->preload_lock);
+	spin_lock_init(&board_info->ai_ctrl_lock);
+
+	/* Get the serial number */
+	result = pci_read_config_dword(pci_dev_p, 0x2C, &board_info->serial_no);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		printk(KERN_WARNING
+		       "ME4000:init_board_info: Can't get serial_no\n");
+		return result;
+	}
+	PDEBUG("init_board_info():serial_no = 0x%x\n", board_info->serial_no);
+
+	/* Get the hardware revision */
+	result =
+	    pci_read_config_byte(pci_dev_p, 0x08, &board_info->hw_revision);
+	if (result != PCIBIOS_SUCCESSFUL) {
+		printk(KERN_WARNING
+		       "ME4000:init_board_info():Can't get hw_revision\n");
+		return result;
+	}
+	PDEBUG("init_board_info():hw_revision = 0x%x\n",
+	       board_info->hw_revision);
+
+	/* Get the vendor id */
+	board_info->vendor_id = pci_dev_p->vendor;
+	PDEBUG("init_board_info():vendor_id = 0x%x\n", board_info->vendor_id);
+
+	/* Get the device id */
+	board_info->device_id = pci_dev_p->device;
+	PDEBUG("init_board_info():device_id = 0x%x\n", board_info->device_id);
+
+	/* Get the pci device number */
+	board_info->pci_dev_no = PCI_FUNC(pci_dev_p->devfn);
+	PDEBUG("init_board_info():pci_func_no = 0x%x\n",
+	       board_info->pci_func_no);
+
+	/* Get the pci slot number */
+	board_info->pci_dev_no = PCI_SLOT(pci_dev_p->devfn);
+	PDEBUG("init_board_info():pci_dev_no = 0x%x\n", board_info->pci_dev_no);
+
+	/* Get the pci bus number */
+	board_info->pci_bus_no = pci_dev_p->bus->number;
+	PDEBUG("init_board_info():pci_bus_no = 0x%x\n", board_info->pci_bus_no);
+
+	/* Get the irq assigned to the board */
+	board_info->irq = pci_dev_p->irq;
+	PDEBUG("init_board_info():irq = %d\n", board_info->irq);
+
+	return 0;
+}
+
+static int alloc_ao_contexts(me4000_info_t * info)
+{
+	int i;
+	int err;
+	me4000_ao_context_t *ao_context;
+
+	for (i = 0; i < info->board_p->ao.count; i++) {
+		ao_context = kmalloc(sizeof(me4000_ao_context_t), GFP_KERNEL);
+		if (!ao_context) {
+			printk(KERN_ERR
+			       "alloc_ao_contexts():Can't get memory for ao context\n");
+			release_ao_contexts(info);
+			return -ENOMEM;
+		}
+		memset(ao_context, 0, sizeof(me4000_ao_context_t));
+
+		spin_lock_init(&ao_context->use_lock);
+		spin_lock_init(&ao_context->int_lock);
+		ao_context->irq = info->irq;
+		init_waitqueue_head(&ao_context->wait_queue);
+		ao_context->board_info = info;
+
+		if (info->board_p->ao.fifo_count) {
+			/* Allocate circular buffer */
+			ao_context->circ_buf.buf =
+			    kmalloc(ME4000_AO_BUFFER_SIZE, GFP_KERNEL);
+			if (!ao_context->circ_buf.buf) {
+				printk(KERN_ERR
+				       "alloc_ao_contexts():Can't get circular buffer\n");
+				release_ao_contexts(info);
+				return -ENOMEM;
+			}
+			memset(ao_context->circ_buf.buf, 0,
+			       ME4000_AO_BUFFER_SIZE);
+
+			/* Clear the circular buffer */
+			ao_context->circ_buf.head = 0;
+			ao_context->circ_buf.tail = 0;
+		}
+
+		switch (i) {
+		case 0:
+			ao_context->ctrl_reg =
+			    info->me4000_regbase + ME4000_AO_00_CTRL_REG;
+			ao_context->status_reg =
+			    info->me4000_regbase + ME4000_AO_00_STATUS_REG;
+			ao_context->fifo_reg =
+			    info->me4000_regbase + ME4000_AO_00_FIFO_REG;
+			ao_context->single_reg =
+			    info->me4000_regbase + ME4000_AO_00_SINGLE_REG;
+			ao_context->timer_reg =
+			    info->me4000_regbase + ME4000_AO_00_TIMER_REG;
+			ao_context->irq_status_reg =
+			    info->me4000_regbase + ME4000_IRQ_STATUS_REG;
+			ao_context->preload_reg =
+			    info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
+			break;
+		case 1:
+			ao_context->ctrl_reg =
+			    info->me4000_regbase + ME4000_AO_01_CTRL_REG;
+			ao_context->status_reg =
+			    info->me4000_regbase + ME4000_AO_01_STATUS_REG;
+			ao_context->fifo_reg =
+			    info->me4000_regbase + ME4000_AO_01_FIFO_REG;
+			ao_context->single_reg =
+			    info->me4000_regbase + ME4000_AO_01_SINGLE_REG;
+			ao_context->timer_reg =
+			    info->me4000_regbase + ME4000_AO_01_TIMER_REG;
+			ao_context->irq_status_reg =
+			    info->me4000_regbase + ME4000_IRQ_STATUS_REG;
+			ao_context->preload_reg =
+			    info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
+			break;
+		case 2:
+			ao_context->ctrl_reg =
+			    info->me4000_regbase + ME4000_AO_02_CTRL_REG;
+			ao_context->status_reg =
+			    info->me4000_regbase + ME4000_AO_02_STATUS_REG;
+			ao_context->fifo_reg =
+			    info->me4000_regbase + ME4000_AO_02_FIFO_REG;
+			ao_context->single_reg =
+			    info->me4000_regbase + ME4000_AO_02_SINGLE_REG;
+			ao_context->timer_reg =
+			    info->me4000_regbase + ME4000_AO_02_TIMER_REG;
+			ao_context->irq_status_reg =
+			    info->me4000_regbase + ME4000_IRQ_STATUS_REG;
+			ao_context->preload_reg =
+			    info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
+			break;
+		case 3:
+			ao_context->ctrl_reg =
+			    info->me4000_regbase + ME4000_AO_03_CTRL_REG;
+			ao_context->status_reg =
+			    info->me4000_regbase + ME4000_AO_03_STATUS_REG;
+			ao_context->fifo_reg =
+			    info->me4000_regbase + ME4000_AO_03_FIFO_REG;
+			ao_context->single_reg =
+			    info->me4000_regbase + ME4000_AO_03_SINGLE_REG;
+			ao_context->timer_reg =
+			    info->me4000_regbase + ME4000_AO_03_TIMER_REG;
+			ao_context->irq_status_reg =
+			    info->me4000_regbase + ME4000_IRQ_STATUS_REG;
+			ao_context->preload_reg =
+			    info->me4000_regbase + ME4000_AO_LOADSETREG_XX;
+			break;
+		default:
+			break;
+		}
+
+		if (info->board_p->ao.fifo_count) {
+			/* Request the interrupt line */
+			err =
+			    request_irq(ao_context->irq, me4000_ao_isr,
+					IRQF_DISABLED | IRQF_SHARED,
+					ME4000_NAME, ao_context);
+			if (err) {
+				printk(KERN_ERR
+				       "alloc_ao_contexts():Can't get interrupt line");
+				if (ao_context->circ_buf.buf)
+					kfree(ao_context->circ_buf.buf);
+				kfree(ao_context);
+				release_ao_contexts(info);
+				return -ENODEV;
+			}
+		}
+
+		list_add_tail(&ao_context->list, &info->ao_context_list);
+		ao_context->index = i;
+	}
+
+	return 0;
+}
+
+static void release_ao_contexts(me4000_info_t * board_info)
+{
+	struct list_head *dac_p;
+	me4000_ao_context_t *ao_context;
+
+	/* Clear analog output context list */
+	while (!list_empty(&board_info->ao_context_list)) {
+		dac_p = board_info->ao_context_list.next;
+		ao_context = list_entry(dac_p, me4000_ao_context_t, list);
+		free_irq(ao_context->irq, ao_context);
+		if (ao_context->circ_buf.buf)
+			kfree(ao_context->circ_buf.buf);
+		list_del(dac_p);
+		kfree(ao_context);
+	}
+}
+
+static int alloc_ai_context(me4000_info_t * info)
+{
+	me4000_ai_context_t *ai_context;
+
+	if (info->board_p->ai.count) {
+		ai_context = kmalloc(sizeof(me4000_ai_context_t), GFP_KERNEL);
+		if (!ai_context) {
+			printk(KERN_ERR
+			       "ME4000:alloc_ai_context():Can't get memory for ai context\n");
+			return -ENOMEM;
+		}
+		memset(ai_context, 0, sizeof(me4000_ai_context_t));
+
+		info->ai_context = ai_context;
+
+		spin_lock_init(&ai_context->use_lock);
+		spin_lock_init(&ai_context->int_lock);
+		ai_context->number = 0;
+		ai_context->irq = info->irq;
+		init_waitqueue_head(&ai_context->wait_queue);
+		ai_context->board_info = info;
+
+		ai_context->ctrl_reg =
+		    info->me4000_regbase + ME4000_AI_CTRL_REG;
+		ai_context->status_reg =
+		    info->me4000_regbase + ME4000_AI_STATUS_REG;
+		ai_context->channel_list_reg =
+		    info->me4000_regbase + ME4000_AI_CHANNEL_LIST_REG;
+		ai_context->data_reg =
+		    info->me4000_regbase + ME4000_AI_DATA_REG;
+		ai_context->chan_timer_reg =
+		    info->me4000_regbase + ME4000_AI_CHAN_TIMER_REG;
+		ai_context->chan_pre_timer_reg =
+		    info->me4000_regbase + ME4000_AI_CHAN_PRE_TIMER_REG;
+		ai_context->scan_timer_low_reg =
+		    info->me4000_regbase + ME4000_AI_SCAN_TIMER_LOW_REG;
+		ai_context->scan_timer_high_reg =
+		    info->me4000_regbase + ME4000_AI_SCAN_TIMER_HIGH_REG;
+		ai_context->scan_pre_timer_low_reg =
+		    info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_LOW_REG;
+		ai_context->scan_pre_timer_high_reg =
+		    info->me4000_regbase + ME4000_AI_SCAN_PRE_TIMER_HIGH_REG;
+		ai_context->start_reg =
+		    info->me4000_regbase + ME4000_AI_START_REG;
+		ai_context->irq_status_reg =
+		    info->me4000_regbase + ME4000_IRQ_STATUS_REG;
+		ai_context->sample_counter_reg =
+		    info->me4000_regbase + ME4000_AI_SAMPLE_COUNTER_REG;
+	}
+
+	return 0;
+}
+
+static int alloc_dio_context(me4000_info_t * info)
+{
+	me4000_dio_context_t *dio_context;
+
+	if (info->board_p->dio.count) {
+		dio_context = kmalloc(sizeof(me4000_dio_context_t), GFP_KERNEL);
+		if (!dio_context) {
+			printk(KERN_ERR
+			       "ME4000:alloc_dio_context():Can't get memory for dio context\n");
+			return -ENOMEM;
+		}
+		memset(dio_context, 0, sizeof(me4000_dio_context_t));
+
+		info->dio_context = dio_context;
+
+		spin_lock_init(&dio_context->use_lock);
+		dio_context->board_info = info;
+
+		dio_context->dio_count = info->board_p->dio.count;
+
+		dio_context->dir_reg =
+		    info->me4000_regbase + ME4000_DIO_DIR_REG;
+		dio_context->ctrl_reg =
+		    info->me4000_regbase + ME4000_DIO_CTRL_REG;
+		dio_context->port_0_reg =
+		    info->me4000_regbase + ME4000_DIO_PORT_0_REG;
+		dio_context->port_1_reg =
+		    info->me4000_regbase + ME4000_DIO_PORT_1_REG;
+		dio_context->port_2_reg =
+		    info->me4000_regbase + ME4000_DIO_PORT_2_REG;
+		dio_context->port_3_reg =
+		    info->me4000_regbase + ME4000_DIO_PORT_3_REG;
+	}
+
+	return 0;
+}
+
+static int alloc_cnt_context(me4000_info_t * info)
+{
+	me4000_cnt_context_t *cnt_context;
+
+	if (info->board_p->cnt.count) {
+		cnt_context = kmalloc(sizeof(me4000_cnt_context_t), GFP_KERNEL);
+		if (!cnt_context) {
+			printk(KERN_ERR
+			       "ME4000:alloc_cnt_context():Can't get memory for cnt context\n");
+			return -ENOMEM;
+		}
+		memset(cnt_context, 0, sizeof(me4000_cnt_context_t));
+
+		info->cnt_context = cnt_context;
+
+		spin_lock_init(&cnt_context->use_lock);
+		cnt_context->board_info = info;
+
+		cnt_context->ctrl_reg =
+		    info->timer_regbase + ME4000_CNT_CTRL_REG;
+		cnt_context->counter_0_reg =
+		    info->timer_regbase + ME4000_CNT_COUNTER_0_REG;
+		cnt_context->counter_1_reg =
+		    info->timer_regbase + ME4000_CNT_COUNTER_1_REG;
+		cnt_context->counter_2_reg =
+		    info->timer_regbase + ME4000_CNT_COUNTER_2_REG;
+	}
+
+	return 0;
+}
+
+static int alloc_ext_int_context(me4000_info_t * info)
+{
+	me4000_ext_int_context_t *ext_int_context;
+
+	if (info->board_p->cnt.count) {
+		ext_int_context =
+		    kmalloc(sizeof(me4000_ext_int_context_t), GFP_KERNEL);
+		if (!ext_int_context) {
+			printk(KERN_ERR
+			       "ME4000:alloc_ext_int_context():Can't get memory for cnt context\n");
+			return -ENOMEM;
+		}
+		memset(ext_int_context, 0, sizeof(me4000_ext_int_context_t));
+
+		info->ext_int_context = ext_int_context;
+
+		spin_lock_init(&ext_int_context->use_lock);
+		ext_int_context->board_info = info;
+
+		ext_int_context->fasync_ptr = NULL;
+		ext_int_context->irq = info->irq;
+
+		ext_int_context->ctrl_reg =
+		    info->me4000_regbase + ME4000_AI_CTRL_REG;
+		ext_int_context->irq_status_reg =
+		    info->me4000_regbase + ME4000_IRQ_STATUS_REG;
+	}
+
+	return 0;
+}
+
+static int me4000_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+	int result = 0;
+	me4000_info_t *board_info;
+
+	CALL_PDEBUG("me4000_probe() is executed\n");
+
+	/* Allocate structure for board context */
+	board_info = kmalloc(sizeof(me4000_info_t), GFP_KERNEL);
+	if (!board_info) {
+		printk(KERN_ERR
+		       "ME4000:Can't get memory for board info structure\n");
+		result = -ENOMEM;
+		goto PROBE_ERROR_1;
+	}
+	memset(board_info, 0, sizeof(me4000_info_t));
+
+	/* Add to global linked list */
+	list_add_tail(&board_info->list, &me4000_board_info_list);
+
+	/* Get the PCI base registers */
+	result = get_registers(dev, board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe():Cannot get registers\n");
+		goto PROBE_ERROR_2;
+	}
+
+	/* Enable the device */
+	result = pci_enable_device(dev);
+	if (result < 0) {
+		printk(KERN_ERR "me4000_probe():Cannot enable PCI device\n");
+		goto PROBE_ERROR_2;
+	}
+
+	/* Request the PCI register regions */
+	result = pci_request_regions(dev, ME4000_NAME);
+	if (result < 0) {
+		printk(KERN_ERR "me4000_probe():Cannot request I/O regions\n");
+		goto PROBE_ERROR_2;
+	}
+
+	/* Initialize board info */
+	result = init_board_info(dev, board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe():Cannot init baord info\n");
+		goto PROBE_ERROR_3;
+	}
+
+	/* Download the xilinx firmware */
+	result = me4000_xilinx_download(board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe:Can't download firmware\n");
+		goto PROBE_ERROR_3;
+	}
+
+	/* Make a hardware reset */
+	result = me4000_reset_board(board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe:Can't reset board\n");
+		goto PROBE_ERROR_3;
+	}
+
+	/* Allocate analog output context structures */
+	result = alloc_ao_contexts(board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe():Cannot allocate ao contexts\n");
+		goto PROBE_ERROR_3;
+	}
+
+	/* Allocate analog input context */
+	result = alloc_ai_context(board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe():Cannot allocate ai context\n");
+		goto PROBE_ERROR_4;
+	}
+
+	/* Allocate digital I/O context */
+	result = alloc_dio_context(board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe():Cannot allocate dio context\n");
+		goto PROBE_ERROR_5;
+	}
+
+	/* Allocate counter context */
+	result = alloc_cnt_context(board_info);
+	if (result) {
+		printk(KERN_ERR "me4000_probe():Cannot allocate cnt context\n");
+		goto PROBE_ERROR_6;
+	}
+
+	/* Allocate external interrupt context */
+	result = alloc_ext_int_context(board_info);
+	if (result) {
+		printk(KERN_ERR
+		       "me4000_probe():Cannot allocate ext_int context\n");
+		goto PROBE_ERROR_7;
+	}
+
+	return 0;
+
+      PROBE_ERROR_7:
+	kfree(board_info->cnt_context);
+
+      PROBE_ERROR_6:
+	kfree(board_info->dio_context);
+
+      PROBE_ERROR_5:
+	kfree(board_info->ai_context);
+
+      PROBE_ERROR_4:
+	release_ao_contexts(board_info);
+
+      PROBE_ERROR_3:
+	pci_release_regions(dev);
+
+      PROBE_ERROR_2:
+	list_del(&board_info->list);
+	kfree(board_info);
+
+      PROBE_ERROR_1:
+	return result;
+}
+
+static int me4000_xilinx_download(me4000_info_t * info)
+{
+	int size = 0;
+	u32 value = 0;
+	int idx = 0;
+	unsigned char *firm;
+	wait_queue_head_t queue;
+
+	CALL_PDEBUG("me4000_xilinx_download() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	firm = (info->device_id == 0x4610) ? xilinx_firm_4610 : xilinx_firm;
+
+	/*
+	 * Set PLX local interrupt 2 polarity to high.
+	 * Interrupt is thrown by init pin of xilinx.
+	 */
+	outl(0x10, info->plx_regbase + PLX_INTCSR);
+
+	/* Set /CS and /WRITE of the Xilinx */
+	value = inl(info->plx_regbase + PLX_ICR);
+	value |= 0x100;
+	outl(value, info->plx_regbase + PLX_ICR);
+
+	/* Init Xilinx with CS1 */
+	inb(info->program_regbase + 0xC8);
+
+	/* Wait until /INIT pin is set */
+	udelay(20);
+	if (!inl(info->plx_regbase + PLX_INTCSR) & 0x20) {
+		printk(KERN_ERR "me4000_xilinx_download():Can't init Xilinx\n");
+		return -EIO;
+	}
+
+	/* Reset /CS and /WRITE of the Xilinx */
+	value = inl(info->plx_regbase + PLX_ICR);
+	value &= ~0x100;
+	outl(value, info->plx_regbase + PLX_ICR);
+
+	/* Download Xilinx firmware */
+	size = (firm[0] << 24) + (firm[1] << 16) + (firm[2] << 8) + firm[3];
+	udelay(10);
+
+	for (idx = 0; idx < size; idx++) {
+		outb(firm[16 + idx], info->program_regbase);
+
+		udelay(10);
+
+		/* Check if BUSY flag is low */
+		if (inl(info->plx_regbase + PLX_ICR) & 0x20) {
+			printk(KERN_ERR
+			       "me4000_xilinx_download():Xilinx is still busy (idx = %d)\n",
+			       idx);
+			return -EIO;
+		}
+	}
+
+	PDEBUG("me4000_xilinx_download():%d bytes written\n", idx);
+
+	/* If done flag is high download was successful */
+	if (inl(info->plx_regbase + PLX_ICR) & 0x4) {
+		PDEBUG("me4000_xilinx_download():Done flag is set\n");
+		PDEBUG("me4000_xilinx_download():Download was successful\n");
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_xilinx_download():DONE flag is not set\n");
+		printk(KERN_ERR
+		       "ME4000:me4000_xilinx_download():Download not succesful\n");
+		return -EIO;
+	}
+
+	/* Set /CS and /WRITE */
+	value = inl(info->plx_regbase + PLX_ICR);
+	value |= 0x100;
+	outl(value, info->plx_regbase + PLX_ICR);
+
+	return 0;
+}
+
+static int me4000_reset_board(me4000_info_t * info)
+{
+	unsigned long icr;
+
+	CALL_PDEBUG("me4000_reset_board() is executed\n");
+
+	/* Make a hardware reset */
+	icr = me4000_inl(info->plx_regbase + PLX_ICR);
+	icr |= 0x40000000;
+	me4000_outl(icr, info->plx_regbase + PLX_ICR);
+	icr &= ~0x40000000;
+	me4000_outl(icr, info->plx_regbase + PLX_ICR);
+
+	/* Set both stop bits in the analog input control register */
+	me4000_outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP,
+		    info->me4000_regbase + ME4000_AI_CTRL_REG);
+
+	/* Set both stop bits in the analog output control register */
+	me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
+		    info->me4000_regbase + ME4000_AO_00_CTRL_REG);
+	me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
+		    info->me4000_regbase + ME4000_AO_01_CTRL_REG);
+	me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
+		    info->me4000_regbase + ME4000_AO_02_CTRL_REG);
+	me4000_outl(ME4000_AO_CTRL_BIT_IMMEDIATE_STOP | ME4000_AO_CTRL_BIT_STOP,
+		    info->me4000_regbase + ME4000_AO_03_CTRL_REG);
+
+	/* 0x8000 to the DACs means an output voltage of 0V */
+	me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_00_SINGLE_REG);
+	me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_01_SINGLE_REG);
+	me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_02_SINGLE_REG);
+	me4000_outl(0x8000, info->me4000_regbase + ME4000_AO_03_SINGLE_REG);
+
+	/* Enable interrupts on the PLX */
+	me4000_outl(0x43, info->plx_regbase + PLX_INTCSR);
+
+	/* Set the adustment register for AO demux */
+	me4000_outl(ME4000_AO_DEMUX_ADJUST_VALUE,
+		    info->me4000_regbase + ME4000_AO_DEMUX_ADJUST_REG);
+
+	/* Set digital I/O direction for port 0 to output on isolated versions */
+	if (!(me4000_inl(info->me4000_regbase + ME4000_DIO_DIR_REG) & 0x1)) {
+		me4000_outl(0x1, info->me4000_regbase + ME4000_DIO_CTRL_REG);
+	}
+
+	return 0;
+}
+
+static int me4000_open(struct inode *inode_p, struct file *file_p)
+{
+	int board, dev, mode;
+	int err = 0;
+	int i;
+	struct list_head *ptr;
+	me4000_info_t *board_info = NULL;
+	me4000_ao_context_t *ao_context = NULL;
+	me4000_ai_context_t *ai_context = NULL;
+	me4000_dio_context_t *dio_context = NULL;
+	me4000_cnt_context_t *cnt_context = NULL;
+	me4000_ext_int_context_t *ext_int_context = NULL;
+
+	CALL_PDEBUG("me4000_open() is executed\n");
+
+	/* Analog output */
+	if (MAJOR(inode_p->i_rdev) == me4000_ao_major_driver_no) {
+		board = AO_BOARD(inode_p->i_rdev);
+		dev = AO_PORT(inode_p->i_rdev);
+		mode = AO_MODE(inode_p->i_rdev);
+
+		PDEBUG("me4000_open():board = %d ao = %d mode = %d\n", board,
+		       dev, mode);
+
+		/* Search for the board context */
+		for (ptr = me4000_board_info_list.next, i = 0;
+		     ptr != &me4000_board_info_list; ptr = ptr->next, i++) {
+			board_info = list_entry(ptr, me4000_info_t, list);
+			if (i == board)
+				break;
+		}
+
+		if (ptr == &me4000_board_info_list) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Board %d not in device list\n",
+			       board);
+			return -ENODEV;
+		}
+
+		/* Search for the dac context */
+		for (ptr = board_info->ao_context_list.next, i = 0;
+		     ptr != &board_info->ao_context_list;
+		     ptr = ptr->next, i++) {
+			ao_context = list_entry(ptr, me4000_ao_context_t, list);
+			if (i == dev)
+				break;
+		}
+
+		if (ptr == &board_info->ao_context_list) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Device %d not in device list\n",
+			       dev);
+			return -ENODEV;
+		}
+
+		/* Check if mode is valid */
+		if (mode > 2) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Mode is not valid\n");
+			return -ENODEV;
+		}
+
+		/* Check if mode is valid for this AO */
+		if ((mode != ME4000_AO_CONV_MODE_SINGLE)
+		    && (dev >= board_info->board_p->ao.fifo_count)) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():AO %d only in single mode available\n",
+			       dev);
+			return -ENODEV;
+		}
+
+		/* Check if already opened */
+		spin_lock(&ao_context->use_lock);
+		if (ao_context->dac_in_use) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():AO %d already in use\n",
+			       dev);
+			spin_unlock(&ao_context->use_lock);
+			return -EBUSY;
+		}
+		ao_context->dac_in_use = 1;
+		spin_unlock(&ao_context->use_lock);
+
+		ao_context->mode = mode;
+
+		/* Hold the context in private data */
+		file_p->private_data = ao_context;
+
+		/* Set file operations pointer */
+		file_p->f_op = me4000_ao_fops_array[mode];
+
+		err = me4000_ao_prepare(ao_context);
+		if (err) {
+			ao_context->dac_in_use = 0;
+			return 1;
+		}
+	}
+	/* Analog input */
+	else if (MAJOR(inode_p->i_rdev) == me4000_ai_major_driver_no) {
+		board = AI_BOARD(inode_p->i_rdev);
+		mode = AI_MODE(inode_p->i_rdev);
+
+		PDEBUG("me4000_open():ai board = %d mode = %d\n", board, mode);
+
+		/* Search for the board context */
+		for (ptr = me4000_board_info_list.next, i = 0;
+		     ptr != &me4000_board_info_list; ptr = ptr->next, i++) {
+			board_info = list_entry(ptr, me4000_info_t, list);
+			if (i == board)
+				break;
+		}
+
+		if (ptr == &me4000_board_info_list) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Board %d not in device list\n",
+			       board);
+			return -ENODEV;
+		}
+
+		ai_context = board_info->ai_context;
+
+		/* Check if mode is valid */
+		if (mode > 5) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Mode is not valid\n");
+			return -EINVAL;
+		}
+
+		/* Check if already opened */
+		spin_lock(&ai_context->use_lock);
+		if (ai_context->in_use) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():AI already in use\n");
+			spin_unlock(&ai_context->use_lock);
+			return -EBUSY;
+		}
+		ai_context->in_use = 1;
+		spin_unlock(&ai_context->use_lock);
+
+		ai_context->mode = mode;
+
+		/* Hold the context in private data */
+		file_p->private_data = ai_context;
+
+		/* Set file operations pointer */
+		file_p->f_op = me4000_ai_fops_array[mode];
+
+		/* Prepare analog input */
+		me4000_ai_prepare(ai_context);
+	}
+	/* Digital I/O */
+	else if (MAJOR(inode_p->i_rdev) == me4000_dio_major_driver_no) {
+		board = DIO_BOARD(inode_p->i_rdev);
+		dev = 0;
+		mode = 0;
+
+		PDEBUG("me4000_open():board = %d\n", board);
+
+		/* Search for the board context */
+		for (ptr = me4000_board_info_list.next;
+		     ptr != &me4000_board_info_list; ptr = ptr->next) {
+			board_info = list_entry(ptr, me4000_info_t, list);
+			if (board_info->board_count == board)
+				break;
+		}
+
+		if (ptr == &me4000_board_info_list) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Board %d not in device list\n",
+			       board);
+			return -ENODEV;
+		}
+
+		/* Search for the dio context */
+		dio_context = board_info->dio_context;
+
+		/* Check if already opened */
+		spin_lock(&dio_context->use_lock);
+		if (dio_context->in_use) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():DIO already in use\n");
+			spin_unlock(&dio_context->use_lock);
+			return -EBUSY;
+		}
+		dio_context->in_use = 1;
+		spin_unlock(&dio_context->use_lock);
+
+		/* Hold the context in private data */
+		file_p->private_data = dio_context;
+
+		/* Set file operations pointer to single functions */
+		file_p->f_op = &me4000_dio_fops;
+
+		//me4000_dio_reset(dio_context);
+	}
+	/* Counters */
+	else if (MAJOR(inode_p->i_rdev) == me4000_cnt_major_driver_no) {
+		board = CNT_BOARD(inode_p->i_rdev);
+		dev = 0;
+		mode = 0;
+
+		PDEBUG("me4000_open():board = %d\n", board);
+
+		/* Search for the board context */
+		for (ptr = me4000_board_info_list.next;
+		     ptr != &me4000_board_info_list; ptr = ptr->next) {
+			board_info = list_entry(ptr, me4000_info_t, list);
+			if (board_info->board_count == board)
+				break;
+		}
+
+		if (ptr == &me4000_board_info_list) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Board %d not in device list\n",
+			       board);
+			return -ENODEV;
+		}
+
+		/* Get the cnt context */
+		cnt_context = board_info->cnt_context;
+
+		/* Check if already opened */
+		spin_lock(&cnt_context->use_lock);
+		if (cnt_context->in_use) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():CNT already in use\n");
+			spin_unlock(&cnt_context->use_lock);
+			return -EBUSY;
+		}
+		cnt_context->in_use = 1;
+		spin_unlock(&cnt_context->use_lock);
+
+		/* Hold the context in private data */
+		file_p->private_data = cnt_context;
+
+		/* Set file operations pointer to single functions */
+		file_p->f_op = &me4000_cnt_fops;
+	}
+	/* External Interrupt */
+	else if (MAJOR(inode_p->i_rdev) == me4000_ext_int_major_driver_no) {
+		board = EXT_INT_BOARD(inode_p->i_rdev);
+		dev = 0;
+		mode = 0;
+
+		PDEBUG("me4000_open():board = %d\n", board);
+
+		/* Search for the board context */
+		for (ptr = me4000_board_info_list.next;
+		     ptr != &me4000_board_info_list; ptr = ptr->next) {
+			board_info = list_entry(ptr, me4000_info_t, list);
+			if (board_info->board_count == board)
+				break;
+		}
+
+		if (ptr == &me4000_board_info_list) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Board %d not in device list\n",
+			       board);
+			return -ENODEV;
+		}
+
+		/* Get the external interrupt context */
+		ext_int_context = board_info->ext_int_context;
+
+		/* Check if already opened */
+		spin_lock(&cnt_context->use_lock);
+		if (ext_int_context->in_use) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():External interrupt already in use\n");
+			spin_unlock(&ext_int_context->use_lock);
+			return -EBUSY;
+		}
+		ext_int_context->in_use = 1;
+		spin_unlock(&ext_int_context->use_lock);
+
+		/* Hold the context in private data */
+		file_p->private_data = ext_int_context;
+
+		/* Set file operations pointer to single functions */
+		file_p->f_op = &me4000_ext_int_fops;
+
+		/* Request the interrupt line */
+		err =
+		    request_irq(ext_int_context->irq, me4000_ext_int_isr,
+				IRQF_DISABLED | IRQF_SHARED, ME4000_NAME,
+				ext_int_context);
+		if (err) {
+			printk(KERN_ERR
+			       "ME4000:me4000_open():Can't get interrupt line");
+			ext_int_context->in_use = 0;
+			return -ENODEV;
+		}
+
+		/* Reset the counter */
+		me4000_ext_int_disable(ext_int_context);
+	} else {
+		printk(KERN_ERR "ME4000:me4000_open():Major number unknown\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int me4000_release(struct inode *inode_p, struct file *file_p)
+{
+	me4000_ao_context_t *ao_context;
+	me4000_ai_context_t *ai_context;
+	me4000_dio_context_t *dio_context;
+	me4000_cnt_context_t *cnt_context;
+	me4000_ext_int_context_t *ext_int_context;
+
+	CALL_PDEBUG("me4000_release() is executed\n");
+
+	if (MAJOR(inode_p->i_rdev) == me4000_ao_major_driver_no) {
+		ao_context = file_p->private_data;
+
+		/* Mark DAC as unused */
+		ao_context->dac_in_use = 0;
+	} else if (MAJOR(inode_p->i_rdev) == me4000_ai_major_driver_no) {
+		ai_context = file_p->private_data;
+
+		/* Reset the analog input */
+		me4000_ai_reset(ai_context);
+
+		/* Free the interrupt and the circular buffer */
+		if (ai_context->mode) {
+			free_irq(ai_context->irq, ai_context);
+			kfree(ai_context->circ_buf.buf);
+			ai_context->circ_buf.buf = NULL;
+			ai_context->circ_buf.head = 0;
+			ai_context->circ_buf.tail = 0;
+		}
+
+		/* Mark AI as unused */
+		ai_context->in_use = 0;
+	} else if (MAJOR(inode_p->i_rdev) == me4000_dio_major_driver_no) {
+		dio_context = file_p->private_data;
+
+		/* Mark digital I/O as unused */
+		dio_context->in_use = 0;
+	} else if (MAJOR(inode_p->i_rdev) == me4000_cnt_major_driver_no) {
+		cnt_context = file_p->private_data;
+
+		/* Mark counters as unused */
+		cnt_context->in_use = 0;
+	} else if (MAJOR(inode_p->i_rdev) == me4000_ext_int_major_driver_no) {
+		ext_int_context = file_p->private_data;
+
+		/* Disable the externel interrupt */
+		me4000_ext_int_disable(ext_int_context);
+
+		free_irq(ext_int_context->irq, ext_int_context);
+
+		/* Delete the fasync structure and free memory */
+		me4000_ext_int_fasync(0, file_p, 0);
+
+		/* Mark as unused */
+		ext_int_context->in_use = 0;
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_release():Major number unknown\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*------------------------------- Analog output stuff --------------------------------------*/
+
+static int me4000_ao_prepare(me4000_ao_context_t * ao_context)
+{
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_prepare() is executed\n");
+
+	if (ao_context->mode == ME4000_AO_CONV_MODE_CONTINUOUS) {
+		/* Only do anything if not already in the correct mode */
+		unsigned long mode = me4000_inl(ao_context->ctrl_reg);
+		if ((mode & ME4000_AO_CONV_MODE_CONTINUOUS)
+		    && (mode & ME4000_AO_CTRL_BIT_ENABLE_FIFO)) {
+			return 0;
+		}
+
+		/* Stop any conversion */
+		me4000_ao_immediate_stop(ao_context);
+
+		/* Set the control register to default state  */
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		me4000_outl(ME4000_AO_CONV_MODE_CONTINUOUS |
+			    ME4000_AO_CTRL_BIT_ENABLE_FIFO |
+			    ME4000_AO_CTRL_BIT_STOP |
+			    ME4000_AO_CTRL_BIT_IMMEDIATE_STOP,
+			    ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+		/* Set to fastest sample rate */
+		me4000_outl(65, ao_context->timer_reg);
+	} else if (ao_context->mode == ME4000_AO_CONV_MODE_WRAPAROUND) {
+		/* Only do anything if not already in the correct mode */
+		unsigned long mode = me4000_inl(ao_context->ctrl_reg);
+		if ((mode & ME4000_AO_CONV_MODE_WRAPAROUND)
+		    && (mode & ME4000_AO_CTRL_BIT_ENABLE_FIFO)) {
+			return 0;
+		}
+
+		/* Stop any conversion */
+		me4000_ao_immediate_stop(ao_context);
+
+		/* Set the control register to default state  */
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		me4000_outl(ME4000_AO_CONV_MODE_WRAPAROUND |
+			    ME4000_AO_CTRL_BIT_ENABLE_FIFO |
+			    ME4000_AO_CTRL_BIT_STOP |
+			    ME4000_AO_CTRL_BIT_IMMEDIATE_STOP,
+			    ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+		/* Set to fastest sample rate */
+		me4000_outl(65, ao_context->timer_reg);
+	} else if (ao_context->mode == ME4000_AO_CONV_MODE_SINGLE) {
+		/* Only do anything if not already in the correct mode */
+		unsigned long mode = me4000_inl(ao_context->ctrl_reg);
+		if (!
+		    (mode &
+		     (ME4000_AO_CONV_MODE_WRAPAROUND |
+		      ME4000_AO_CONV_MODE_CONTINUOUS))) {
+			return 0;
+		}
+
+		/* Stop any conversion */
+		me4000_ao_immediate_stop(ao_context);
+
+		/* Clear the control register */
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		me4000_outl(0x0, ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+		/* Set voltage to 0V */
+		me4000_outl(0x8000, ao_context->single_reg);
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_prepare():Invalid mode specified\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int me4000_ao_reset(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	wait_queue_head_t queue;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_reset() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	if (ao_context->mode == ME4000_AO_CONV_MODE_WRAPAROUND) {
+		/*
+		 * First stop conversion of the DAC before reconfigure.
+		 * This is essantial, cause of the state machine.
+		 * If not stopped before configuring mode, it could
+		 * walk in a undefined state.
+		 */
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp |= ME4000_AO_CTRL_BIT_IMMEDIATE_STOP;
+		me4000_outl(tmp, ao_context->ctrl_reg);
+
+		while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) {
+			sleep_on_timeout(&queue, 1);
+		}
+
+		/* Set to transparent mode */
+		me4000_ao_simultaneous_disable(ao_context);
+
+		/* Set to single mode in order to set default voltage */
+		me4000_outl(0x0, ao_context->ctrl_reg);
+
+		/* Set voltage to 0V */
+		me4000_outl(0x8000, ao_context->single_reg);
+
+		/* Set to fastest sample rate */
+		me4000_outl(65, ao_context->timer_reg);
+
+		/* Set the original mode and enable FIFO */
+		me4000_outl(ME4000_AO_CONV_MODE_WRAPAROUND |
+			    ME4000_AO_CTRL_BIT_ENABLE_FIFO |
+			    ME4000_AO_CTRL_BIT_STOP |
+			    ME4000_AO_CTRL_BIT_IMMEDIATE_STOP,
+			    ao_context->ctrl_reg);
+	} else if (ao_context->mode == ME4000_AO_CONV_MODE_CONTINUOUS) {
+		/*
+		 * First stop conversion of the DAC before reconfigure.
+		 * This is essantial, cause of the state machine.
+		 * If not stopped before configuring mode, it could
+		 * walk in a undefined state.
+		 */
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp |= ME4000_AO_CTRL_BIT_STOP;
+		me4000_outl(tmp, ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+		while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) {
+			sleep_on_timeout(&queue, 1);
+		}
+
+		/* Clear the circular buffer */
+		ao_context->circ_buf.head = 0;
+		ao_context->circ_buf.tail = 0;
+
+		/* Set to transparent mode */
+		me4000_ao_simultaneous_disable(ao_context);
+
+		/* Set to single mode in order to set default voltage */
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		me4000_outl(0x0, ao_context->ctrl_reg);
+
+		/* Set voltage to 0V */
+		me4000_outl(0x8000, ao_context->single_reg);
+
+		/* Set to fastest sample rate */
+		me4000_outl(65, ao_context->timer_reg);
+
+		/* Set the original mode and enable FIFO */
+		me4000_outl(ME4000_AO_CONV_MODE_CONTINUOUS |
+			    ME4000_AO_CTRL_BIT_ENABLE_FIFO |
+			    ME4000_AO_CTRL_BIT_STOP |
+			    ME4000_AO_CTRL_BIT_IMMEDIATE_STOP,
+			    ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+	} else {
+		/* Set to transparent mode */
+		me4000_ao_simultaneous_disable(ao_context);
+
+		/* Set voltage to 0V */
+		me4000_outl(0x8000, ao_context->single_reg);
+	}
+
+	return 0;
+}
+
+static ssize_t me4000_ao_write_sing(struct file *filep, const char *buff,
+				    size_t cnt, loff_t * offp)
+{
+	me4000_ao_context_t *ao_context = filep->private_data;
+	u32 value;
+	const u16 *buffer = (const u16 *)buff;
+
+	CALL_PDEBUG("me4000_ao_write_sing() is executed\n");
+
+	if (cnt != 2) {
+		printk(KERN_ERR
+		       "me4000_ao_write_sing():Write count is not 2\n");
+		return -EINVAL;
+	}
+
+	if (get_user(value, buffer)) {
+		printk(KERN_ERR
+		       "me4000_ao_write_sing():Cannot copy data from user\n");
+		return -EFAULT;
+	}
+
+	me4000_outl(value, ao_context->single_reg);
+
+	return 2;
+}
+
+static ssize_t me4000_ao_write_wrap(struct file *filep, const char *buff,
+				    size_t cnt, loff_t * offp)
+{
+	me4000_ao_context_t *ao_context = filep->private_data;
+	size_t i;
+	u32 value;
+	u32 tmp;
+	const u16 *buffer = (const u16 *)buff;
+	size_t count = cnt / 2;
+
+	CALL_PDEBUG("me4000_ao_write_wrap() is executed\n");
+
+	/* Check if a conversion is already running */
+	if (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_write_wrap():There is already a conversion running\n");
+		return -EBUSY;
+	}
+
+	if (count > ME4000_AO_FIFO_COUNT) {
+		printk(KERN_ERR
+		       "me4000_ao_write_wrap():Can't load more than %d values\n",
+		       ME4000_AO_FIFO_COUNT);
+		return -ENOSPC;
+	}
+
+	/* Reset the FIFO */
+	tmp = inl(ao_context->ctrl_reg);
+	tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_FIFO;
+	outl(tmp, ao_context->ctrl_reg);
+	tmp |= ME4000_AO_CTRL_BIT_ENABLE_FIFO;
+	outl(tmp, ao_context->ctrl_reg);
+
+	for (i = 0; i < count; i++) {
+		if (get_user(value, buffer + i)) {
+			printk(KERN_ERR
+			       "me4000_ao_write_single():Cannot copy data from user\n");
+			return -EFAULT;
+		}
+		if (((ao_context->fifo_reg & 0xFF) == ME4000_AO_01_FIFO_REG)
+		    || ((ao_context->fifo_reg & 0xFF) == ME4000_AO_03_FIFO_REG))
+			value = value << 16;
+		outl(value, ao_context->fifo_reg);
+	}
+	CALL_PDEBUG("me4000_ao_write_wrap() is leaved with %d\n", i * 2);
+
+	return i * 2;
+}
+
+static ssize_t me4000_ao_write_cont(struct file *filep, const char *buff,
+				    size_t cnt, loff_t * offp)
+{
+	me4000_ao_context_t *ao_context = filep->private_data;
+	const u16 *buffer = (const u16 *)buff;
+	size_t count = cnt / 2;
+	unsigned long flags;
+	u32 tmp;
+	int c = 0;
+	int k = 0;
+	int ret = 0;
+	u16 svalue;
+	u32 lvalue;
+	int i;
+	wait_queue_head_t queue;
+
+	CALL_PDEBUG("me4000_ao_write_cont() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/* Check count */
+	if (count <= 0) {
+		PDEBUG("me4000_ao_write_cont():Count is 0\n");
+		return 0;
+	}
+
+	if (filep->f_flags & O_APPEND) {
+		PDEBUG("me4000_ao_write_cont():Append data to data stream\n");
+		while (count > 0) {
+			if (filep->f_flags & O_NONBLOCK) {
+				if (ao_context->pipe_flag) {
+					printk(KERN_ERR
+					       "ME4000:me4000_ao_write_cont():Broken pipe in nonblocking write\n");
+					return -EPIPE;
+				}
+				c = me4000_space_to_end(ao_context->circ_buf,
+							ME4000_AO_BUFFER_COUNT);
+				if (!c) {
+					PDEBUG
+					    ("me4000_ao_write_cont():Returning from nonblocking write\n");
+					break;
+				}
+			} else {
+				wait_event_interruptible(ao_context->wait_queue,
+							 (c =
+							  me4000_space_to_end
+							  (ao_context->circ_buf,
+							   ME4000_AO_BUFFER_COUNT)));
+				if (ao_context->pipe_flag) {
+					printk(KERN_ERR
+					       "me4000_ao_write_cont():Broken pipe in blocking write\n");
+					return -EPIPE;
+				}
+				if (signal_pending(current)) {
+					printk(KERN_ERR
+					       "me4000_ao_write_cont():Wait for free buffer interrupted from signal\n");
+					return -EINTR;
+				}
+			}
+
+			PDEBUG("me4000_ao_write_cont():Space to end = %d\n", c);
+
+			/* Only able to write size of free buffer or size of count */
+			if (count < c)
+				c = count;
+
+			k = 2 * c;
+			k -= copy_from_user(ao_context->circ_buf.buf +
+					    ao_context->circ_buf.head, buffer,
+					    k);
+			c = k / 2;
+			PDEBUG
+			    ("me4000_ao_write_cont():Copy %d values from user space\n",
+			     c);
+
+			if (!c)
+				return -EFAULT;
+
+			ao_context->circ_buf.head =
+			    (ao_context->circ_buf.head +
+			     c) & (ME4000_AO_BUFFER_COUNT - 1);
+			buffer += c;
+			count -= c;
+			ret += c;
+
+			/* Values are now available so enable interrupts */
+			spin_lock_irqsave(&ao_context->int_lock, flags);
+			if (me4000_buf_count
+			    (ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) {
+				tmp = me4000_inl(ao_context->ctrl_reg);
+				tmp |= ME4000_AO_CTRL_BIT_ENABLE_IRQ;
+				me4000_outl(tmp, ao_context->ctrl_reg);
+			}
+			spin_unlock_irqrestore(&ao_context->int_lock, flags);
+		}
+
+		/* Wait until the state machine is stopped if O_SYNC is set */
+		if (filep->f_flags & O_SYNC) {
+			while (inl(ao_context->status_reg) &
+			       ME4000_AO_STATUS_BIT_FSM) {
+				interruptible_sleep_on_timeout(&queue, 1);
+				if (ao_context->pipe_flag) {
+					PDEBUG
+					    ("me4000_ao_write_cont():Broken pipe detected after sync\n");
+					return -EPIPE;
+				}
+				if (signal_pending(current)) {
+					printk(KERN_ERR
+					       "me4000_ao_write_cont():Wait on state machine after sync interrupted\n");
+					return -EINTR;
+				}
+			}
+		}
+	} else {
+		PDEBUG("me4000_ao_write_cont():Preload DAC FIFO\n");
+		if ((me4000_inl(ao_context->status_reg) &
+		     ME4000_AO_STATUS_BIT_FSM)) {
+			printk(KERN_ERR
+			       "me4000_ao_write_cont():Can't Preload DAC FIFO while conversion is running\n");
+			return -EBUSY;
+		}
+
+		/* Clear the FIFO */
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp &=
+		    ~(ME4000_AO_CTRL_BIT_ENABLE_FIFO |
+		      ME4000_AO_CTRL_BIT_ENABLE_IRQ);
+		me4000_outl(tmp, ao_context->ctrl_reg);
+		tmp |= ME4000_AO_CTRL_BIT_ENABLE_FIFO;
+		me4000_outl(tmp, ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+		/* Clear the circular buffer */
+		ao_context->circ_buf.head = 0;
+		ao_context->circ_buf.tail = 0;
+
+		/* Reset the broken pipe flag */
+		ao_context->pipe_flag = 0;
+
+		/* Only able to write size of fifo or count */
+		c = ME4000_AO_FIFO_COUNT;
+		if (count < c)
+			c = count;
+
+		PDEBUG
+		    ("me4000_ao_write_cont():Write %d values to DAC on 0x%lX\n",
+		     c, ao_context->fifo_reg);
+
+		/* Write values to the fifo */
+		for (i = 0; i < c; i++) {
+			if (get_user(svalue, buffer))
+				return -EFAULT;
+
+			if (((ao_context->fifo_reg & 0xFF) ==
+			     ME4000_AO_01_FIFO_REG)
+			    || ((ao_context->fifo_reg & 0xFF) ==
+				ME4000_AO_03_FIFO_REG)) {
+				lvalue = ((u32) svalue) << 16;
+			} else
+				lvalue = (u32) svalue;
+
+			outl(lvalue, ao_context->fifo_reg);
+			buffer++;
+		}
+		count -= c;
+		ret += c;
+
+		while (1) {
+			/* Get free buffer */
+			c = me4000_space_to_end(ao_context->circ_buf,
+						ME4000_AO_BUFFER_COUNT);
+
+			if (c == 0)
+				return (2 * ret);
+
+			/* Only able to write size of free buffer or size of count */
+			if (count < c)
+				c = count;
+
+			/* If count = 0 return to user */
+			if (c <= 0) {
+				PDEBUG
+				    ("me4000_ao_write_cont():Count reached 0\n");
+				break;
+			}
+
+			k = 2 * c;
+			k -= copy_from_user(ao_context->circ_buf.buf +
+					    ao_context->circ_buf.head, buffer,
+					    k);
+			c = k / 2;
+			PDEBUG
+			    ("me4000_ao_write_cont():Wrote %d values to buffer\n",
+			     c);
+
+			if (!c)
+				return -EFAULT;
+
+			ao_context->circ_buf.head =
+			    (ao_context->circ_buf.head +
+			     c) & (ME4000_AO_BUFFER_COUNT - 1);
+			buffer += c;
+			count -= c;
+			ret += c;
+
+			/* If values in the buffer are available so enable interrupts */
+			spin_lock_irqsave(&ao_context->int_lock, flags);
+			if (me4000_buf_count
+			    (ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) {
+				PDEBUG
+				    ("me4000_ao_write_cont():Enable Interrupts\n");
+				tmp = me4000_inl(ao_context->ctrl_reg);
+				tmp |= ME4000_AO_CTRL_BIT_ENABLE_IRQ;
+				me4000_outl(tmp, ao_context->ctrl_reg);
+			}
+			spin_unlock_irqrestore(&ao_context->int_lock, flags);
+		}
+	}
+
+	if (filep->f_flags & O_NONBLOCK) {
+		return (ret == 0) ? -EAGAIN : 2 * ret;
+	}
+
+	return 2 * ret;
+}
+
+static unsigned int me4000_ao_poll_cont(struct file *file_p, poll_table * wait)
+{
+	me4000_ao_context_t *ao_context;
+	unsigned long mask = 0;
+
+	CALL_PDEBUG("me4000_ao_poll_cont() is executed\n");
+
+	ao_context = file_p->private_data;
+
+	poll_wait(file_p, &ao_context->wait_queue, wait);
+
+	/* Get free buffer */
+	if (me4000_space_to_end(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT))
+		mask |= POLLOUT | POLLWRNORM;
+
+	CALL_PDEBUG("me4000_ao_poll_cont():Return mask %lX\n", mask);
+
+	return mask;
+}
+
+static int me4000_ao_fsync_cont(struct file *file_p, struct dentry *dentry_p,
+				int datasync)
+{
+	me4000_ao_context_t *ao_context;
+	wait_queue_head_t queue;
+
+	CALL_PDEBUG("me4000_ao_fsync_cont() is executed\n");
+
+	ao_context = file_p->private_data;
+	init_waitqueue_head(&queue);
+
+	while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) {
+		interruptible_sleep_on_timeout(&queue, 1);
+		if (ao_context->pipe_flag) {
+			printk(KERN_ERR
+			       "me4000_ao_fsync_cont():Broken pipe detected\n");
+			return -EPIPE;
+		}
+
+		if (signal_pending(current)) {
+			printk(KERN_ERR
+			       "me4000_ao_fsync_cont():Wait on state machine interrupted\n");
+			return -EINTR;
+		}
+	}
+
+	return 0;
+}
+
+static int me4000_ao_ioctl_sing(struct inode *inode_p, struct file *file_p,
+				unsigned int service, unsigned long arg)
+{
+	me4000_ao_context_t *ao_context;
+
+	CALL_PDEBUG("me4000_ao_ioctl_sing() is executed\n");
+
+	ao_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		return -ENOTTY;
+		PDEBUG("me4000_ao_ioctl_sing():Wrong magic number\n");
+	}
+
+	switch (service) {
+	case ME4000_AO_EX_TRIG_SETUP:
+		return me4000_ao_ex_trig_set_edge((int *)arg, ao_context);
+	case ME4000_AO_EX_TRIG_ENABLE:
+		return me4000_ao_ex_trig_enable(ao_context);
+	case ME4000_AO_EX_TRIG_DISABLE:
+		return me4000_ao_ex_trig_disable(ao_context);
+	case ME4000_AO_PRELOAD:
+		return me4000_ao_preload(ao_context);
+	case ME4000_AO_PRELOAD_UPDATE:
+		return me4000_ao_preload_update(ao_context);
+	case ME4000_GET_USER_INFO:
+		return me4000_get_user_info((me4000_user_info_t *) arg,
+					    ao_context->board_info);
+	case ME4000_AO_SIMULTANEOUS_EX_TRIG:
+		return me4000_ao_simultaneous_ex_trig(ao_context);
+	case ME4000_AO_SIMULTANEOUS_SW:
+		return me4000_ao_simultaneous_sw(ao_context);
+	case ME4000_AO_SIMULTANEOUS_DISABLE:
+		return me4000_ao_simultaneous_disable(ao_context);
+	case ME4000_AO_SIMULTANEOUS_UPDATE:
+		return
+		    me4000_ao_simultaneous_update((me4000_ao_channel_list_t *)
+						  arg, ao_context);
+	case ME4000_AO_EX_TRIG_TIMEOUT:
+		return me4000_ao_ex_trig_timeout((unsigned long *)arg,
+						 ao_context);
+	case ME4000_AO_DISABLE_DO:
+		return me4000_ao_disable_do(ao_context);
+	default:
+		printk(KERN_ERR
+		       "me4000_ao_ioctl_sing():Service number invalid\n");
+		return -ENOTTY;
+	}
+
+	return 0;
+}
+
+static int me4000_ao_ioctl_wrap(struct inode *inode_p, struct file *file_p,
+				unsigned int service, unsigned long arg)
+{
+	me4000_ao_context_t *ao_context;
+
+	CALL_PDEBUG("me4000_ao_ioctl_wrap() is executed\n");
+
+	ao_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		return -ENOTTY;
+		PDEBUG("me4000_ao_ioctl_wrap():Wrong magic number\n");
+	}
+
+	switch (service) {
+	case ME4000_AO_START:
+		return me4000_ao_start((unsigned long *)arg, ao_context);
+	case ME4000_AO_STOP:
+		return me4000_ao_stop(ao_context);
+	case ME4000_AO_IMMEDIATE_STOP:
+		return me4000_ao_immediate_stop(ao_context);
+	case ME4000_AO_RESET:
+		return me4000_ao_reset(ao_context);
+	case ME4000_AO_TIMER_SET_DIVISOR:
+		return me4000_ao_timer_set_divisor((u32 *) arg, ao_context);
+	case ME4000_AO_EX_TRIG_SETUP:
+		return me4000_ao_ex_trig_set_edge((int *)arg, ao_context);
+	case ME4000_AO_EX_TRIG_ENABLE:
+		return me4000_ao_ex_trig_enable(ao_context);
+	case ME4000_AO_EX_TRIG_DISABLE:
+		return me4000_ao_ex_trig_disable(ao_context);
+	case ME4000_GET_USER_INFO:
+		return me4000_get_user_info((me4000_user_info_t *) arg,
+					    ao_context->board_info);
+	case ME4000_AO_FSM_STATE:
+		return me4000_ao_fsm_state((int *)arg, ao_context);
+	case ME4000_AO_ENABLE_DO:
+		return me4000_ao_enable_do(ao_context);
+	case ME4000_AO_DISABLE_DO:
+		return me4000_ao_disable_do(ao_context);
+	case ME4000_AO_SYNCHRONOUS_EX_TRIG:
+		return me4000_ao_synchronous_ex_trig(ao_context);
+	case ME4000_AO_SYNCHRONOUS_SW:
+		return me4000_ao_synchronous_sw(ao_context);
+	case ME4000_AO_SYNCHRONOUS_DISABLE:
+		return me4000_ao_synchronous_disable(ao_context);
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_ao_ioctl_cont(struct inode *inode_p, struct file *file_p,
+				unsigned int service, unsigned long arg)
+{
+	me4000_ao_context_t *ao_context;
+
+	CALL_PDEBUG("me4000_ao_ioctl_cont() is executed\n");
+
+	ao_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		return -ENOTTY;
+		PDEBUG("me4000_ao_ioctl_cont():Wrong magic number\n");
+	}
+
+	switch (service) {
+	case ME4000_AO_START:
+		return me4000_ao_start((unsigned long *)arg, ao_context);
+	case ME4000_AO_STOP:
+		return me4000_ao_stop(ao_context);
+	case ME4000_AO_IMMEDIATE_STOP:
+		return me4000_ao_immediate_stop(ao_context);
+	case ME4000_AO_RESET:
+		return me4000_ao_reset(ao_context);
+	case ME4000_AO_TIMER_SET_DIVISOR:
+		return me4000_ao_timer_set_divisor((u32 *) arg, ao_context);
+	case ME4000_AO_EX_TRIG_SETUP:
+		return me4000_ao_ex_trig_set_edge((int *)arg, ao_context);
+	case ME4000_AO_EX_TRIG_ENABLE:
+		return me4000_ao_ex_trig_enable(ao_context);
+	case ME4000_AO_EX_TRIG_DISABLE:
+		return me4000_ao_ex_trig_disable(ao_context);
+	case ME4000_AO_ENABLE_DO:
+		return me4000_ao_enable_do(ao_context);
+	case ME4000_AO_DISABLE_DO:
+		return me4000_ao_disable_do(ao_context);
+	case ME4000_AO_FSM_STATE:
+		return me4000_ao_fsm_state((int *)arg, ao_context);
+	case ME4000_GET_USER_INFO:
+		return me4000_get_user_info((me4000_user_info_t *) arg,
+					    ao_context->board_info);
+	case ME4000_AO_SYNCHRONOUS_EX_TRIG:
+		return me4000_ao_synchronous_ex_trig(ao_context);
+	case ME4000_AO_SYNCHRONOUS_SW:
+		return me4000_ao_synchronous_sw(ao_context);
+	case ME4000_AO_SYNCHRONOUS_DISABLE:
+		return me4000_ao_synchronous_disable(ao_context);
+	case ME4000_AO_GET_FREE_BUFFER:
+		return me4000_ao_get_free_buffer((unsigned long *)arg,
+						 ao_context);
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_ao_start(unsigned long *arg, me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	wait_queue_head_t queue;
+	unsigned long ref;
+	unsigned long timeout;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_start() is executed\n");
+
+	if (get_user(timeout, arg)) {
+		printk(KERN_ERR
+		       "me4000_ao_start():Cannot copy data from user\n");
+		return -EFAULT;
+	}
+
+	init_waitqueue_head(&queue);
+
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = inl(ao_context->ctrl_reg);
+	tmp &= ~(ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP);
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	if ((tmp & ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG)) {
+		if (timeout) {
+			ref = jiffies;
+			while (!
+			       (inl(ao_context->status_reg) &
+				ME4000_AO_STATUS_BIT_FSM)) {
+				interruptible_sleep_on_timeout(&queue, 1);
+				if (signal_pending(current)) {
+					printk(KERN_ERR
+					       "ME4000:me4000_ao_start():Wait on start of state machine interrupted\n");
+					return -EINTR;
+				}
+				if (((jiffies - ref) > (timeout * HZ / USER_HZ))) {	// 2.6 has diffrent definitions for HZ in user and kernel space
+					printk(KERN_ERR
+					       "ME4000:me4000_ao_start():Timeout reached\n");
+					return -EIO;
+				}
+			}
+		}
+	} else {
+		me4000_outl(0x8000, ao_context->single_reg);
+	}
+
+	return 0;
+}
+
+static int me4000_ao_stop(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	wait_queue_head_t queue;
+	unsigned long flags;
+
+	init_waitqueue_head(&queue);
+
+	CALL_PDEBUG("me4000_ao_stop() is executed\n");
+
+	/* Set the stop bit */
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = inl(ao_context->ctrl_reg);
+	tmp |= ME4000_AO_CTRL_BIT_STOP;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) {
+		interruptible_sleep_on_timeout(&queue, 1);
+		if (signal_pending(current)) {
+			printk(KERN_ERR
+			       "me4000_ao_stop():Wait on state machine after stop interrupted\n");
+			return -EINTR;
+		}
+	}
+
+	/* Clear the stop bit */
+	//tmp &= ~ME4000_AO_CTRL_BIT_STOP;
+	//me4000_outl(tmp, ao_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ao_immediate_stop(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	wait_queue_head_t queue;
+	unsigned long flags;
+
+	init_waitqueue_head(&queue);
+
+	CALL_PDEBUG("me4000_ao_immediate_stop() is executed\n");
+
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = inl(ao_context->ctrl_reg);
+	tmp |= ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	while (inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) {
+		interruptible_sleep_on_timeout(&queue, 1);
+		if (signal_pending(current)) {
+			printk(KERN_ERR
+			       "me4000_ao_immediate_stop():Wait on state machine after stop interrupted\n");
+			return -EINTR;
+		}
+	}
+
+	/* Clear the stop bits */
+	//tmp &= ~(ME4000_AO_CTRL_BIT_STOP | ME4000_AO_CTRL_BIT_IMMEDIATE_STOP);
+	//me4000_outl(tmp, ao_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ao_timer_set_divisor(u32 * arg,
+				       me4000_ao_context_t * ao_context)
+{
+	u32 divisor;
+	u32 tmp;
+
+	CALL_PDEBUG("me4000_ao_timer set_divisor() is executed\n");
+
+	if (get_user(divisor, arg))
+		return -EFAULT;
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "me4000_ao_timer_set_divisor():Can't set timer while DAC is running\n");
+		return -EBUSY;
+	}
+
+	PDEBUG("me4000_ao_timer set_divisor():Divisor from user = %d\n",
+	       divisor);
+
+	/* Check if the divisor is right. ME4000_AO_MIN_TICKS is the lowest */
+	if (divisor < ME4000_AO_MIN_TICKS) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_timer set_divisor():Divisor to low\n");
+		return -EINVAL;
+	}
+
+	/* Fix bug in Firmware */
+	divisor -= 2;
+
+	PDEBUG("me4000_ao_timer set_divisor():Divisor to HW = %d\n", divisor);
+
+	/* Write the divisor */
+	me4000_outl(divisor, ao_context->timer_reg);
+
+	return 0;
+}
+
+static int me4000_ao_ex_trig_set_edge(int *arg,
+				      me4000_ao_context_t * ao_context)
+{
+	int mode;
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_ex_trig_set_edge() is executed\n");
+
+	if (get_user(mode, arg))
+		return -EFAULT;
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "me4000_ao_ex_trig_set_edge():Can't set trigger while DAC is running\n");
+		return -EBUSY;
+	}
+
+	if (mode == ME4000_AO_TRIGGER_EXT_EDGE_RISING) {
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp &=
+		    ~(ME4000_AO_CTRL_BIT_EX_TRIG_EDGE |
+		      ME4000_AO_CTRL_BIT_EX_TRIG_BOTH);
+		me4000_outl(tmp, ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+	} else if (mode == ME4000_AO_TRIGGER_EXT_EDGE_FALLING) {
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp &= ~ME4000_AO_CTRL_BIT_EX_TRIG_BOTH;
+		tmp |= ME4000_AO_CTRL_BIT_EX_TRIG_EDGE;
+		me4000_outl(tmp, ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+	} else if (mode == ME4000_AO_TRIGGER_EXT_EDGE_BOTH) {
+		spin_lock_irqsave(&ao_context->int_lock, flags);
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp |=
+		    ME4000_AO_CTRL_BIT_EX_TRIG_EDGE |
+		    ME4000_AO_CTRL_BIT_EX_TRIG_BOTH;
+		me4000_outl(tmp, ao_context->ctrl_reg);
+		spin_unlock_irqrestore(&ao_context->int_lock, flags);
+	} else {
+		printk(KERN_ERR
+		       "me4000_ao_ex_trig_set_edge():Invalid trigger mode\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int me4000_ao_ex_trig_enable(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_ex_trig_enable() is executed\n");
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "me4000_ao_ex_trig_enable():Can't enable trigger while DAC is running\n");
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = me4000_inl(ao_context->ctrl_reg);
+	tmp |= ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ao_ex_trig_disable(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_ex_trig_disable() is executed\n");
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "me4000_ao_ex_trig_disable():Can't disable trigger while DAC is running\n");
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = me4000_inl(ao_context->ctrl_reg);
+	tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ao_simultaneous_disable(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+
+	CALL_PDEBUG("me4000_ao_simultaneous_disable() is executed\n");
+
+	/* Check if the state machine is stopped */
+	/* Be careful here because this function is called from
+	   me4000_ao_synchronous disable */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "me4000_ao_simultaneous_disable():Can't disable while DAC is running\n");
+		return -EBUSY;
+	}
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	tmp &= ~(0x1 << ao_context->index);	// Disable preload bit
+	tmp &= ~(0x1 << (ao_context->index + 16));	// Disable hw simultaneous bit
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+
+	return 0;
+}
+
+static int me4000_ao_simultaneous_ex_trig(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+
+	CALL_PDEBUG("me4000_ao_simultaneous_ex_trig() is executed\n");
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	tmp |= (0x1 << ao_context->index);	// Enable preload bit
+	tmp |= (0x1 << (ao_context->index + 16));	// Enable hw simultaneous bit
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+
+	return 0;
+}
+
+static int me4000_ao_simultaneous_sw(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+
+	CALL_PDEBUG("me4000_ao_simultaneous_sw() is executed\n");
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	tmp |= (0x1 << ao_context->index);	// Enable preload bit
+	tmp &= ~(0x1 << (ao_context->index + 16));	// Disable hw simultaneous bit
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+
+	return 0;
+}
+
+static int me4000_ao_preload(me4000_ao_context_t * ao_context)
+{
+	CALL_PDEBUG("me4000_ao_preload() is executed\n");
+	return me4000_ao_simultaneous_sw(ao_context);
+}
+
+static int me4000_ao_preload_update(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	u32 ctrl;
+	struct list_head *entry;
+
+	CALL_PDEBUG("me4000_ao_preload_update() is executed\n");
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	list_for_each(entry, &ao_context->board_info->ao_context_list) {
+		/* The channels we update must be in the following state :
+		   - Mode A
+		   - Hardware trigger is disabled
+		   - Corresponding simultaneous bit is reset
+		 */
+		ctrl = me4000_inl(ao_context->ctrl_reg);
+		if (!
+		    (ctrl &
+		     (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1 |
+		      ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG))) {
+			if (!
+			    (tmp &
+			     (0x1 <<
+			      (((me4000_ao_context_t *) entry)->index + 16)))) {
+				tmp &=
+				    ~(0x1 <<
+				      (((me4000_ao_context_t *) entry)->index));
+			}
+		}
+	}
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+
+	return 0;
+}
+
+static int me4000_ao_simultaneous_update(me4000_ao_channel_list_t * arg,
+					 me4000_ao_context_t * ao_context)
+{
+	int err;
+	int i;
+	u32 tmp;
+	me4000_ao_channel_list_t channels;
+
+	CALL_PDEBUG("me4000_ao_simultaneous_update() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&channels, arg, sizeof(me4000_ao_channel_list_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_simultaneous_update():Can't copy command\n");
+		return -EFAULT;
+	}
+
+	channels.list =
+	    kmalloc(sizeof(unsigned long) * channels.count, GFP_KERNEL);
+	if (!channels.list) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_simultaneous_update():Can't get buffer\n");
+		return -ENOMEM;
+	}
+	memset(channels.list, 0, sizeof(unsigned long) * channels.count);
+
+	/* Copy channel list from user */
+	err =
+	    copy_from_user(channels.list, arg->list,
+			   sizeof(unsigned long) * channels.count);
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_simultaneous_update():Can't copy list\n");
+		kfree(channels.list);
+		return -EFAULT;
+	}
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	for (i = 0; i < channels.count; i++) {
+		if (channels.list[i] >
+		    ao_context->board_info->board_p->ao.count) {
+			spin_unlock(&ao_context->board_info->preload_lock);
+			kfree(channels.list);
+			printk(KERN_ERR
+			       "ME4000:me4000_ao_simultaneous_update():Invalid board number specified\n");
+			return -EFAULT;
+		}
+		tmp &= ~(0x1 << channels.list[i]);	// Clear the preload bit
+		tmp &= ~(0x1 << (channels.list[i] + 16));	// Clear the hw simultaneous bit
+	}
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+	kfree(channels.list);
+
+	return 0;
+}
+
+static int me4000_ao_synchronous_ex_trig(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_synchronous_ex_trig() is executed\n");
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "me4000_ao_synchronous_ex_trig(): DAC is running\n");
+		return -EBUSY;
+	}
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	tmp &= ~(0x1 << ao_context->index);	// Disable synchronous sw bit
+	tmp |= 0x1 << (ao_context->index + 16);	// Enable synchronous hw bit
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+
+	/* Make runnable */
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = me4000_inl(ao_context->ctrl_reg);
+	if (tmp & (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1)) {
+		tmp &=
+		    ~(ME4000_AO_CTRL_BIT_STOP |
+		      ME4000_AO_CTRL_BIT_IMMEDIATE_STOP);
+		me4000_outl(tmp, ao_context->ctrl_reg);
+	}
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ao_synchronous_sw(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_synchronous_sw() is executed\n");
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR "me4000_ao_synchronous_sw(): DAC is running\n");
+		return -EBUSY;
+	}
+
+	spin_lock(&ao_context->board_info->preload_lock);
+	tmp = me4000_inl(ao_context->preload_reg);
+	tmp |= 0x1 << ao_context->index;	// Enable synchronous sw bit
+	tmp &= ~(0x1 << (ao_context->index + 16));	// Disable synchronous hw bit
+	me4000_outl(tmp, ao_context->preload_reg);
+	spin_unlock(&ao_context->board_info->preload_lock);
+
+	/* Make runnable */
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = me4000_inl(ao_context->ctrl_reg);
+	if (tmp & (ME4000_AO_CTRL_BIT_MODE_0 | ME4000_AO_CTRL_BIT_MODE_1)) {
+		tmp &=
+		    ~(ME4000_AO_CTRL_BIT_STOP |
+		      ME4000_AO_CTRL_BIT_IMMEDIATE_STOP);
+		me4000_outl(tmp, ao_context->ctrl_reg);
+	}
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ao_synchronous_disable(me4000_ao_context_t * ao_context)
+{
+	return me4000_ao_simultaneous_disable(ao_context);
+}
+
+static int me4000_ao_get_free_buffer(unsigned long *arg,
+				     me4000_ao_context_t * ao_context)
+{
+	unsigned long c;
+	int err;
+
+	c = me4000_buf_space(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT);
+
+	err = copy_to_user(arg, &c, sizeof(unsigned long));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_get_free_buffer():Can't copy to user space\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int me4000_ao_ex_trig_timeout(unsigned long *arg,
+				     me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	wait_queue_head_t queue;
+	unsigned long ref;
+	unsigned long timeout;
+
+	CALL_PDEBUG("me4000_ao_ex_trig_timeout() is executed\n");
+
+	if (get_user(timeout, arg)) {
+		printk(KERN_ERR
+		       "me4000_ao_ex_trig_timeout():Cannot copy data from user\n");
+		return -EFAULT;
+	}
+
+	init_waitqueue_head(&queue);
+
+	tmp = inl(ao_context->ctrl_reg);
+
+	if ((tmp & ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG)) {
+		if (timeout) {
+			ref = jiffies;
+			while ((inl(ao_context->status_reg) &
+				ME4000_AO_STATUS_BIT_FSM)) {
+				interruptible_sleep_on_timeout(&queue, 1);
+				if (signal_pending(current)) {
+					printk(KERN_ERR
+					       "ME4000:me4000_ao_ex_trig_timeout():Wait on start of state machine interrupted\n");
+					return -EINTR;
+				}
+				if (((jiffies - ref) > (timeout * HZ / USER_HZ))) {	// 2.6 has diffrent definitions for HZ in user and kernel space
+					printk(KERN_ERR
+					       "ME4000:me4000_ao_ex_trig_timeout():Timeout reached\n");
+					return -EIO;
+				}
+			}
+		} else {
+			while ((inl(ao_context->status_reg) &
+				ME4000_AO_STATUS_BIT_FSM)) {
+				interruptible_sleep_on_timeout(&queue, 1);
+				if (signal_pending(current)) {
+					printk(KERN_ERR
+					       "ME4000:me4000_ao_ex_trig_timeout():Wait on start of state machine interrupted\n");
+					return -EINTR;
+				}
+			}
+		}
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_ao_ex_trig_timeout():External Trigger is not enabled\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int me4000_ao_enable_do(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_enable_do() is executed\n");
+
+	/* Only available for analog output 3 */
+	if (ao_context->index != 3) {
+		printk(KERN_ERR
+		       "me4000_ao_enable_do():Only available for analog output 3\n");
+		return -ENOTTY;
+	}
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR "me4000_ao_enable_do(): DAC is running\n");
+		return -EBUSY;
+	}
+
+	/* Set the stop bit */
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = inl(ao_context->ctrl_reg);
+	tmp |= ME4000_AO_CTRL_BIT_ENABLE_DO;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ao_disable_do(me4000_ao_context_t * ao_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ao_disable_do() is executed\n");
+
+	/* Only available for analog output 3 */
+	if (ao_context->index != 3) {
+		printk(KERN_ERR
+		       "me4000_ao_disable():Only available for analog output 3\n");
+		return -ENOTTY;
+	}
+
+	/* Check if the state machine is stopped */
+	tmp = me4000_inl(ao_context->status_reg);
+	if (tmp & ME4000_AO_STATUS_BIT_FSM) {
+		printk(KERN_ERR "me4000_ao_disable_do(): DAC is running\n");
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&ao_context->int_lock, flags);
+	tmp = inl(ao_context->ctrl_reg);
+	tmp &= ~(ME4000_AO_CTRL_BIT_ENABLE_DO);
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock_irqrestore(&ao_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ao_fsm_state(int *arg, me4000_ao_context_t * ao_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ao_fsm_state() is executed\n");
+
+	tmp =
+	    (me4000_inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM) ? 1
+	    : 0;
+
+	if (ao_context->pipe_flag) {
+		printk(KERN_ERR "me4000_ao_fsm_state():Broken pipe detected\n");
+		return -EPIPE;
+	}
+
+	if (put_user(tmp, arg)) {
+		printk(KERN_ERR "me4000_ao_fsm_state():Cannot copy to user\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+/*------------------------------- Analog input stuff --------------------------------------*/
+
+static int me4000_ai_prepare(me4000_ai_context_t * ai_context)
+{
+	wait_queue_head_t queue;
+	int err;
+
+	CALL_PDEBUG("me4000_ai_prepare() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/* Set the new mode and stop bits */
+	me4000_outl(ai_context->
+		    mode | ME4000_AI_CTRL_BIT_STOP |
+		    ME4000_AI_CTRL_BIT_IMMEDIATE_STOP, ai_context->ctrl_reg);
+
+	/* Set the timer registers */
+	ai_context->chan_timer = 66;
+	ai_context->chan_pre_timer = 66;
+	ai_context->scan_timer_low = 0;
+	ai_context->scan_timer_high = 0;
+
+	me4000_outl(65, ai_context->chan_timer_reg);
+	me4000_outl(65, ai_context->chan_pre_timer_reg);
+	me4000_outl(0, ai_context->scan_timer_low_reg);
+	me4000_outl(0, ai_context->scan_timer_high_reg);
+	me4000_outl(0, ai_context->scan_pre_timer_low_reg);
+	me4000_outl(0, ai_context->scan_pre_timer_high_reg);
+
+	ai_context->channel_list_count = 0;
+
+	if (ai_context->mode) {
+		/* Request the interrupt line */
+		err =
+		    request_irq(ai_context->irq, me4000_ai_isr,
+				IRQF_DISABLED | IRQF_SHARED, ME4000_NAME,
+				ai_context);
+		if (err) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_prepare():Can't get interrupt line");
+			return -ENODEV;
+		}
+
+		/* Allocate circular buffer */
+		ai_context->circ_buf.buf =
+		    kmalloc(ME4000_AI_BUFFER_SIZE, GFP_KERNEL);
+		if (!ai_context->circ_buf.buf) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_prepare():Can't get circular buffer\n");
+			free_irq(ai_context->irq, ai_context);
+			return -ENOMEM;
+		}
+		memset(ai_context->circ_buf.buf, 0, ME4000_AI_BUFFER_SIZE);
+
+		/* Clear the circular buffer */
+		ai_context->circ_buf.head = 0;
+		ai_context->circ_buf.tail = 0;
+	}
+
+	return 0;
+}
+
+static int me4000_ai_reset(me4000_ai_context_t * ai_context)
+{
+	wait_queue_head_t queue;
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ai_reset() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/*
+	 * First stop conversion of the state machine before reconfigure.
+	 * If not stopped before configuring mode, it could
+	 * walk in a undefined state.
+	 */
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+
+	while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) {
+		interruptible_sleep_on_timeout(&queue, 1);
+		if (signal_pending(current)) {
+			printk(KERN_ERR
+			       "me4000_ai_reset():Wait on state machine after stop interrupted\n");
+			return -EINTR;
+		}
+	}
+
+	/* Clear the control register and set the stop bits */
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	me4000_outl(ME4000_AI_CTRL_BIT_IMMEDIATE_STOP | ME4000_AI_CTRL_BIT_STOP,
+		    ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+
+	/* Reset timer registers */
+	ai_context->chan_timer = 66;
+	ai_context->chan_pre_timer = 66;
+	ai_context->scan_timer_low = 0;
+	ai_context->scan_timer_high = 0;
+	ai_context->sample_counter = 0;
+	ai_context->sample_counter_reload = 0;
+
+	me4000_outl(65, ai_context->chan_timer_reg);
+	me4000_outl(65, ai_context->chan_pre_timer_reg);
+	me4000_outl(0, ai_context->scan_timer_low_reg);
+	me4000_outl(0, ai_context->scan_timer_high_reg);
+	me4000_outl(0, ai_context->scan_pre_timer_low_reg);
+	me4000_outl(0, ai_context->scan_pre_timer_high_reg);
+	me4000_outl(0, ai_context->sample_counter_reg);
+
+	ai_context->channel_list_count = 0;
+
+	/* Clear the circular buffer */
+	ai_context->circ_buf.head = 0;
+	ai_context->circ_buf.tail = 0;
+
+	return 0;
+}
+
+static int me4000_ai_ioctl_sing(struct inode *inode_p, struct file *file_p,
+				unsigned int service, unsigned long arg)
+{
+	me4000_ai_context_t *ai_context;
+
+	CALL_PDEBUG("me4000_ai_ioctl_sing() is executed\n");
+
+	ai_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		printk(KERN_ERR "me4000_ai_ioctl_sing():Wrong magic number\n");
+		return -ENOTTY;
+	}
+	if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) {
+		printk(KERN_ERR
+		       "me4000_ai_ioctl_sing():Service number to high\n");
+		return -ENOTTY;
+	}
+
+	switch (service) {
+	case ME4000_AI_SINGLE:
+		return me4000_ai_single((me4000_ai_single_t *) arg, ai_context);
+	case ME4000_AI_EX_TRIG_ENABLE:
+		return me4000_ai_ex_trig_enable(ai_context);
+	case ME4000_AI_EX_TRIG_DISABLE:
+		return me4000_ai_ex_trig_disable(ai_context);
+	case ME4000_AI_EX_TRIG_SETUP:
+		return me4000_ai_ex_trig_setup((me4000_ai_trigger_t *) arg,
+					       ai_context);
+	case ME4000_GET_USER_INFO:
+		return me4000_get_user_info((me4000_user_info_t *) arg,
+					    ai_context->board_info);
+	case ME4000_AI_OFFSET_ENABLE:
+		return me4000_ai_offset_enable(ai_context);
+	case ME4000_AI_OFFSET_DISABLE:
+		return me4000_ai_offset_disable(ai_context);
+	case ME4000_AI_FULLSCALE_ENABLE:
+		return me4000_ai_fullscale_enable(ai_context);
+	case ME4000_AI_FULLSCALE_DISABLE:
+		return me4000_ai_fullscale_disable(ai_context);
+	case ME4000_AI_EEPROM_READ:
+		return me4000_eeprom_read((me4000_eeprom_t *) arg, ai_context);
+	case ME4000_AI_EEPROM_WRITE:
+		return me4000_eeprom_write((me4000_eeprom_t *) arg, ai_context);
+	default:
+		printk(KERN_ERR
+		       "me4000_ai_ioctl_sing():Invalid service number\n");
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_ai_single(me4000_ai_single_t * arg,
+			    me4000_ai_context_t * ai_context)
+{
+	me4000_ai_single_t cmd;
+	int err;
+	u32 tmp;
+	wait_queue_head_t queue;
+	unsigned long jiffy;
+
+	CALL_PDEBUG("me4000_ai_single() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_ai_single_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_single():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Check range parameter */
+	switch (cmd.range) {
+	case ME4000_AI_LIST_RANGE_BIPOLAR_10:
+	case ME4000_AI_LIST_RANGE_BIPOLAR_2_5:
+	case ME4000_AI_LIST_RANGE_UNIPOLAR_10:
+	case ME4000_AI_LIST_RANGE_UNIPOLAR_2_5:
+		break;
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_single():Invalid range specified\n");
+		return -EINVAL;
+	}
+
+	/* Check mode and channel number */
+	switch (cmd.mode) {
+	case ME4000_AI_LIST_INPUT_SINGLE_ENDED:
+		if (cmd.channel >= ai_context->board_info->board_p->ai.count) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_single():Analog input is not available\n");
+			return -EINVAL;
+		}
+		break;
+	case ME4000_AI_LIST_INPUT_DIFFERENTIAL:
+		if (cmd.channel >=
+		    ai_context->board_info->board_p->ai.diff_count) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_single():Analog input is not available in differential mode\n");
+			return -EINVAL;
+		}
+		break;
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_single():Invalid mode specified\n");
+		return -EINVAL;
+	}
+
+	/* Clear channel list, data fifo and both stop bits */
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &=
+	    ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO |
+	      ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	/* Enable channel list and data fifo */
+	tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_DATA_FIFO;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	/* Generate channel list entry */
+	me4000_outl(cmd.channel | cmd.range | cmd.
+		    mode | ME4000_AI_LIST_LAST_ENTRY,
+		    ai_context->channel_list_reg);
+
+	/* Set the timer to maximum */
+	me4000_outl(66, ai_context->chan_timer_reg);
+	me4000_outl(66, ai_context->chan_pre_timer_reg);
+
+	if (tmp & ME4000_AI_CTRL_BIT_EX_TRIG) {
+		jiffy = jiffies;
+		while (!
+		       (me4000_inl(ai_context->status_reg) &
+			ME4000_AI_STATUS_BIT_EF_DATA)) {
+			interruptible_sleep_on_timeout(&queue, 1);
+			if (signal_pending(current)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_single():Wait on start of state machine interrupted\n");
+				return -EINTR;
+			}
+			if (((jiffies - jiffy) > (cmd.timeout * HZ / USER_HZ)) && cmd.timeout) {	// 2.6 has diffrent definitions for HZ in user and kernel space
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_single():Timeout reached\n");
+				return -EIO;
+			}
+		}
+	} else {
+		/* Start conversion */
+		me4000_inl(ai_context->start_reg);
+
+		/* Wait until ready */
+		udelay(10);
+		if (!
+		    (me4000_inl(ai_context->status_reg) &
+		     ME4000_AI_STATUS_BIT_EF_DATA)) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_single():Value not available after wait\n");
+			return -EIO;
+		}
+	}
+
+	/* Read value from data fifo */
+	cmd.value = me4000_inl(ai_context->data_reg) & 0xFFFF;
+
+	/* Copy result back to user */
+	err = copy_to_user(arg, &cmd, sizeof(me4000_ai_single_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_single():Can't copy to user space\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int me4000_ai_ioctl_sw(struct inode *inode_p, struct file *file_p,
+			      unsigned int service, unsigned long arg)
+{
+	me4000_ai_context_t *ai_context;
+
+	CALL_PDEBUG("me4000_ai_ioctl_sw() is executed\n");
+
+	ai_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		printk(KERN_ERR "me4000_ai_ioctl_sw():Wrong magic number\n");
+		return -ENOTTY;
+	}
+	if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) {
+		printk(KERN_ERR
+		       "me4000_ai_ioctl_sw():Service number to high\n");
+		return -ENOTTY;
+	}
+
+	switch (service) {
+	case ME4000_AI_SC_SETUP:
+		return me4000_ai_sc_setup((me4000_ai_sc_t *) arg, ai_context);
+	case ME4000_AI_CONFIG:
+		return me4000_ai_config((me4000_ai_config_t *) arg, ai_context);
+	case ME4000_AI_START:
+		return me4000_ai_start(ai_context);
+	case ME4000_AI_STOP:
+		return me4000_ai_stop(ai_context);
+	case ME4000_AI_IMMEDIATE_STOP:
+		return me4000_ai_immediate_stop(ai_context);
+	case ME4000_AI_FSM_STATE:
+		return me4000_ai_fsm_state((int *)arg, ai_context);
+	case ME4000_GET_USER_INFO:
+		return me4000_get_user_info((me4000_user_info_t *) arg,
+					    ai_context->board_info);
+	case ME4000_AI_EEPROM_READ:
+		return me4000_eeprom_read((me4000_eeprom_t *) arg, ai_context);
+	case ME4000_AI_EEPROM_WRITE:
+		return me4000_eeprom_write((me4000_eeprom_t *) arg, ai_context);
+	case ME4000_AI_GET_COUNT_BUFFER:
+		return me4000_ai_get_count_buffer((unsigned long *)arg,
+						  ai_context);
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_ioctl_sw():Invalid service number %d\n",
+		       service);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_ai_ioctl_ext(struct inode *inode_p, struct file *file_p,
+			       unsigned int service, unsigned long arg)
+{
+	me4000_ai_context_t *ai_context;
+
+	CALL_PDEBUG("me4000_ai_ioctl_ext() is executed\n");
+
+	ai_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		printk(KERN_ERR "me4000_ai_ioctl_ext():Wrong magic number\n");
+		return -ENOTTY;
+	}
+	if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) {
+		printk(KERN_ERR
+		       "me4000_ai_ioctl_ext():Service number to high\n");
+		return -ENOTTY;
+	}
+
+	switch (service) {
+	case ME4000_AI_SC_SETUP:
+		return me4000_ai_sc_setup((me4000_ai_sc_t *) arg, ai_context);
+	case ME4000_AI_CONFIG:
+		return me4000_ai_config((me4000_ai_config_t *) arg, ai_context);
+	case ME4000_AI_START:
+		return me4000_ai_start_ex((unsigned long *)arg, ai_context);
+	case ME4000_AI_STOP:
+		return me4000_ai_stop(ai_context);
+	case ME4000_AI_IMMEDIATE_STOP:
+		return me4000_ai_immediate_stop(ai_context);
+	case ME4000_AI_EX_TRIG_ENABLE:
+		return me4000_ai_ex_trig_enable(ai_context);
+	case ME4000_AI_EX_TRIG_DISABLE:
+		return me4000_ai_ex_trig_disable(ai_context);
+	case ME4000_AI_EX_TRIG_SETUP:
+		return me4000_ai_ex_trig_setup((me4000_ai_trigger_t *) arg,
+					       ai_context);
+	case ME4000_AI_FSM_STATE:
+		return me4000_ai_fsm_state((int *)arg, ai_context);
+	case ME4000_GET_USER_INFO:
+		return me4000_get_user_info((me4000_user_info_t *) arg,
+					    ai_context->board_info);
+	case ME4000_AI_GET_COUNT_BUFFER:
+		return me4000_ai_get_count_buffer((unsigned long *)arg,
+						  ai_context);
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_ioctl_ext():Invalid service number %d\n",
+		       service);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_ai_fasync(int fd, struct file *file_p, int mode)
+{
+	me4000_ai_context_t *ai_context;
+
+	CALL_PDEBUG("me4000_ao_fasync_cont() is executed\n");
+
+	ai_context = file_p->private_data;
+	return fasync_helper(fd, file_p, mode, &ai_context->fasync_p);
+}
+
+static int me4000_ai_config(me4000_ai_config_t * arg,
+			    me4000_ai_context_t * ai_context)
+{
+	me4000_ai_config_t cmd;
+	u32 *list = NULL;
+	u32 mode;
+	int i;
+	int err;
+	wait_queue_head_t queue;
+	u64 scan;
+	u32 tmp;
+
+	CALL_PDEBUG("me4000_ai_config() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/* Check if conversion is stopped */
+	if (inl(ai_context->ctrl_reg) & ME4000_AI_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_config():Conversion is not stopped\n");
+		err = -EBUSY;
+		goto AI_CONFIG_ERR;
+	}
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_ai_config_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_config():Can't copy from user space\n");
+		err = -EFAULT;
+		goto AI_CONFIG_ERR;
+	}
+
+	PDEBUG
+	    ("me4000_ai_config():chan = %ld, pre_chan = %ld, scan_low = %ld, scan_high = %ld, count = %ld\n",
+	     cmd.timer.chan, cmd.timer.pre_chan, cmd.timer.scan_low,
+	     cmd.timer.scan_high, cmd.channel_list.count);
+
+	/* Check whether sample and hold is available for this board */
+	if (cmd.sh) {
+		if (!ai_context->board_info->board_p->ai.sh_count) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_config():Sample and Hold is not available for this board\n");
+			err = -ENODEV;
+			goto AI_CONFIG_ERR;
+		}
+	}
+
+	/* Check the channel list size */
+	if (cmd.channel_list.count > ME4000_AI_CHANNEL_LIST_COUNT) {
+		printk(KERN_ERR
+		       "me4000_ai_config():Channel list is to large\n");
+		err = -EINVAL;
+		goto AI_CONFIG_ERR;
+	}
+
+	/* Copy channel list from user */
+	list = kmalloc(sizeof(u32) * cmd.channel_list.count, GFP_KERNEL);
+	if (!list) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_config():Can't get memory for channel list\n");
+		err = -ENOMEM;
+		goto AI_CONFIG_ERR;
+	}
+	err =
+	    copy_from_user(list, cmd.channel_list.list,
+			   sizeof(u32) * cmd.channel_list.count);
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_config():Can't copy from user space\n");
+		err = -EFAULT;
+		goto AI_CONFIG_ERR;
+	}
+
+	/* Check if last entry bit is set */
+	if (!(list[cmd.channel_list.count - 1] & ME4000_AI_LIST_LAST_ENTRY)) {
+		printk(KERN_WARNING
+		       "me4000_ai_config():Last entry bit is not set\n");
+		list[cmd.channel_list.count - 1] |= ME4000_AI_LIST_LAST_ENTRY;
+	}
+
+	/* Check whether mode is equal for all entries */
+	mode = list[0] & 0x20;
+	for (i = 0; i < cmd.channel_list.count; i++) {
+		if ((list[i] & 0x20) != mode) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_config():Mode is not equal for all entries\n");
+			err = -EINVAL;
+			goto AI_CONFIG_ERR;
+		}
+	}
+
+	/* Check whether channels are available for this mode */
+	if (mode == ME4000_AI_LIST_INPUT_SINGLE_ENDED) {
+		for (i = 0; i < cmd.channel_list.count; i++) {
+			if ((list[i] & 0x1F) >=
+			    ai_context->board_info->board_p->ai.count) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_config():Channel is not available for single ended\n");
+				err = -EINVAL;
+				goto AI_CONFIG_ERR;
+			}
+		}
+	} else if (mode == ME4000_AI_LIST_INPUT_DIFFERENTIAL) {
+		for (i = 0; i < cmd.channel_list.count; i++) {
+			if ((list[i] & 0x1F) >=
+			    ai_context->board_info->board_p->ai.diff_count) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_config():Channel is not available for differential\n");
+				err = -EINVAL;
+				goto AI_CONFIG_ERR;
+			}
+		}
+	}
+
+	/* Check if bipolar is set for all entries when in differential mode */
+	if (mode == ME4000_AI_LIST_INPUT_DIFFERENTIAL) {
+		for (i = 0; i < cmd.channel_list.count; i++) {
+			if ((list[i] & 0xC0) != ME4000_AI_LIST_RANGE_BIPOLAR_10
+			    && (list[i] & 0xC0) !=
+			    ME4000_AI_LIST_RANGE_BIPOLAR_2_5) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_config():Bipolar is not selected in differential mode\n");
+				err = -EINVAL;
+				goto AI_CONFIG_ERR;
+			}
+		}
+	}
+
+	if (ai_context->mode != ME4000_AI_ACQ_MODE_EXT_SINGLE_VALUE) {
+		/* Check for minimum channel divisor */
+		if (cmd.timer.chan < ME4000_AI_MIN_TICKS) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_config():Channel timer divisor is to low\n");
+			err = -EINVAL;
+			goto AI_CONFIG_ERR;
+		}
+
+		/* Check if minimum channel divisor is adjusted when sample and hold is activated */
+		if ((cmd.sh) && (cmd.timer.chan != ME4000_AI_MIN_TICKS)) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_config():Channel timer divisor must be at minimum when sample and hold is activated\n");
+			err = -EINVAL;
+			goto AI_CONFIG_ERR;
+		}
+
+		/* Check for minimum channel pre divisor */
+		if (cmd.timer.pre_chan < ME4000_AI_MIN_TICKS) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_config():Channel pre timer divisor is to low\n");
+			err = -EINVAL;
+			goto AI_CONFIG_ERR;
+		}
+
+		/* Write the channel timers */
+		me4000_outl(cmd.timer.chan - 1, ai_context->chan_timer_reg);
+		me4000_outl(cmd.timer.pre_chan - 1,
+			    ai_context->chan_pre_timer_reg);
+
+		/* Save the timer values in the board context */
+		ai_context->chan_timer = cmd.timer.chan;
+		ai_context->chan_pre_timer = cmd.timer.pre_chan;
+
+		if (ai_context->mode != ME4000_AI_ACQ_MODE_EXT_SINGLE_CHANLIST) {
+			/* Check for scan timer divisor */
+			scan =
+			    (u64) cmd.timer.scan_low | ((u64) cmd.timer.
+							scan_high << 32);
+			if (scan != 0) {
+				if (scan <
+				    cmd.channel_list.count * cmd.timer.chan +
+				    1) {
+					printk(KERN_ERR
+					       "ME4000:me4000_ai_config():Scan timer divisor is to low\n");
+					err = -EINVAL;
+					goto AI_CONFIG_ERR;
+				}
+			}
+
+			/* Write the scan timers */
+			if (scan != 0) {
+				scan--;
+				tmp = (u32) (scan & 0xFFFFFFFF);
+				me4000_outl(tmp,
+					    ai_context->scan_timer_low_reg);
+				tmp = (u32) ((scan >> 32) & 0xFFFFFFFF);
+				me4000_outl(tmp,
+					    ai_context->scan_timer_high_reg);
+
+				scan =
+				    scan - (cmd.timer.chan - 1) +
+				    (cmd.timer.pre_chan - 1);
+				tmp = (u32) (scan & 0xFFFFFFFF);
+				me4000_outl(tmp,
+					    ai_context->scan_pre_timer_low_reg);
+				tmp = (u32) ((scan >> 32) & 0xFFFFFFFF);
+				me4000_outl(tmp,
+					    ai_context->
+					    scan_pre_timer_high_reg);
+			} else {
+				me4000_outl(0x0,
+					    ai_context->scan_timer_low_reg);
+				me4000_outl(0x0,
+					    ai_context->scan_timer_high_reg);
+
+				me4000_outl(0x0,
+					    ai_context->scan_pre_timer_low_reg);
+				me4000_outl(0x0,
+					    ai_context->
+					    scan_pre_timer_high_reg);
+			}
+
+			ai_context->scan_timer_low = cmd.timer.scan_low;
+			ai_context->scan_timer_high = cmd.timer.scan_high;
+		}
+	}
+
+	/* Clear the channel list */
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &= ~ME4000_AI_CTRL_BIT_CHANNEL_FIFO;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	tmp |= ME4000_AI_CTRL_BIT_CHANNEL_FIFO;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	/* Write the channel list */
+	for (i = 0; i < cmd.channel_list.count; i++) {
+		me4000_outl(list[i], ai_context->channel_list_reg);
+	}
+
+	/* Setup sample and hold */
+	if (cmd.sh) {
+		tmp |= ME4000_AI_CTRL_BIT_SAMPLE_HOLD;
+		me4000_outl(tmp, ai_context->ctrl_reg);
+	} else {
+		tmp &= ~ME4000_AI_CTRL_BIT_SAMPLE_HOLD;
+		me4000_outl(tmp, ai_context->ctrl_reg);
+	}
+
+	/* Save the channel list size in the board context */
+	ai_context->channel_list_count = cmd.channel_list.count;
+
+	kfree(list);
+
+	return 0;
+
+      AI_CONFIG_ERR:
+
+	/* Reset the timers */
+	ai_context->chan_timer = 66;
+	ai_context->chan_pre_timer = 66;
+	ai_context->scan_timer_low = 0;
+	ai_context->scan_timer_high = 0;
+
+	me4000_outl(65, ai_context->chan_timer_reg);
+	me4000_outl(65, ai_context->chan_pre_timer_reg);
+	me4000_outl(0, ai_context->scan_timer_high_reg);
+	me4000_outl(0, ai_context->scan_timer_low_reg);
+	me4000_outl(0, ai_context->scan_pre_timer_high_reg);
+	me4000_outl(0, ai_context->scan_pre_timer_low_reg);
+
+	ai_context->channel_list_count = 0;
+
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &=
+	    ~(ME4000_AI_CTRL_BIT_CHANNEL_FIFO | ME4000_AI_CTRL_BIT_SAMPLE_HOLD);
+
+	if (list)
+		kfree(list);
+
+	return err;
+
+}
+
+static int ai_common_start(me4000_ai_context_t * ai_context)
+{
+	u32 tmp;
+	CALL_PDEBUG("ai_common_start() is executed\n");
+
+	tmp = me4000_inl(ai_context->ctrl_reg);
+
+	/* Check if conversion is stopped */
+	if (tmp & ME4000_AI_STATUS_BIT_FSM) {
+		printk(KERN_ERR
+		       "ME4000:ai_common_start():Conversion is not stopped\n");
+		return -EBUSY;
+	}
+
+	/* Clear data fifo, disable all interrupts, clear sample counter reload */
+	tmp &= ~(ME4000_AI_CTRL_BIT_DATA_FIFO | ME4000_AI_CTRL_BIT_LE_IRQ |
+		 ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ |
+		 ME4000_AI_CTRL_BIT_SC_RELOAD);
+
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	/* Clear circular buffer */
+	ai_context->circ_buf.head = 0;
+	ai_context->circ_buf.tail = 0;
+
+	/* Enable data fifo */
+	tmp |= ME4000_AI_CTRL_BIT_DATA_FIFO;
+
+	/* Determine interrupt setup */
+	if (ai_context->sample_counter && !ai_context->sample_counter_reload) {
+		/* Enable Half Full Interrupt and Sample Counter Interrupt */
+		tmp |= ME4000_AI_CTRL_BIT_SC_IRQ | ME4000_AI_CTRL_BIT_HF_IRQ;
+	} else if (ai_context->sample_counter
+		   && ai_context->sample_counter_reload) {
+		if (ai_context->sample_counter <= ME4000_AI_FIFO_COUNT / 2) {
+			/* Enable only Sample Counter Interrupt */
+			tmp |=
+			    ME4000_AI_CTRL_BIT_SC_IRQ |
+			    ME4000_AI_CTRL_BIT_SC_RELOAD;
+		} else {
+			/* Enable Half Full Interrupt and Sample Counter Interrupt */
+			tmp |=
+			    ME4000_AI_CTRL_BIT_SC_IRQ |
+			    ME4000_AI_CTRL_BIT_HF_IRQ |
+			    ME4000_AI_CTRL_BIT_SC_RELOAD;
+		}
+	} else {
+		/* Enable only Half Full Interrupt */
+		tmp |= ME4000_AI_CTRL_BIT_HF_IRQ;
+	}
+
+	/* Clear the stop bits */
+	tmp &= ~(ME4000_AI_CTRL_BIT_STOP | ME4000_AI_CTRL_BIT_IMMEDIATE_STOP);
+
+	/* Write setup to hardware */
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	/* Write sample counter */
+	me4000_outl(ai_context->sample_counter, ai_context->sample_counter_reg);
+
+	return 0;
+}
+
+static int me4000_ai_start(me4000_ai_context_t * ai_context)
+{
+	int err;
+	CALL_PDEBUG("me4000_ai_start() is executed\n");
+
+	/* Prepare Hardware */
+	err = ai_common_start(ai_context);
+	if (err)
+		return err;
+
+	/* Start conversion by dummy read */
+	me4000_inl(ai_context->start_reg);
+
+	return 0;
+}
+
+static int me4000_ai_start_ex(unsigned long *arg,
+			      me4000_ai_context_t * ai_context)
+{
+	int err;
+	wait_queue_head_t queue;
+	unsigned long ref;
+	unsigned long timeout;
+
+	CALL_PDEBUG("me4000_ai_start_ex() is executed\n");
+
+	if (get_user(timeout, arg)) {
+		printk(KERN_ERR
+		       "me4000_ai_start_ex():Cannot copy data from user\n");
+		return -EFAULT;
+	}
+
+	init_waitqueue_head(&queue);
+
+	/* Prepare Hardware */
+	err = ai_common_start(ai_context);
+	if (err)
+		return err;
+
+	if (timeout) {
+		ref = jiffies;
+		while (!
+		       (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM))
+		{
+			interruptible_sleep_on_timeout(&queue, 1);
+			if (signal_pending(current)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_start_ex():Wait on start of state machine interrupted\n");
+				return -EINTR;
+			}
+			if (((jiffies - ref) > (timeout * HZ / USER_HZ))) {	// 2.6 has diffrent definitions for HZ in user and kernel space
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_start_ex():Timeout reached\n");
+				return -EIO;
+			}
+		}
+	} else {
+		while (!
+		       (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM))
+		{
+			interruptible_sleep_on_timeout(&queue, 1);
+			if (signal_pending(current)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_start_ex():Wait on start of state machine interrupted\n");
+				return -EINTR;
+			}
+		}
+	}
+
+	return 0;
+}
+
+static int me4000_ai_stop(me4000_ai_context_t * ai_context)
+{
+	wait_queue_head_t queue;
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ai_stop() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/* Disable irqs and clear data fifo */
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &=
+	    ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ |
+	      ME4000_AI_CTRL_BIT_DATA_FIFO);
+	/* Stop conversion of the state machine */
+	tmp |= ME4000_AI_CTRL_BIT_STOP;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+
+	/* Clear circular buffer */
+	ai_context->circ_buf.head = 0;
+	ai_context->circ_buf.tail = 0;
+
+	while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) {
+		interruptible_sleep_on_timeout(&queue, 1);
+		if (signal_pending(current)) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_stop():Wait on state machine after stop interrupted\n");
+			return -EINTR;
+		}
+	}
+
+	return 0;
+}
+
+static int me4000_ai_immediate_stop(me4000_ai_context_t * ai_context)
+{
+	wait_queue_head_t queue;
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ai_stop() is executed\n");
+
+	init_waitqueue_head(&queue);
+
+	/* Disable irqs and clear data fifo */
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &=
+	    ~(ME4000_AI_CTRL_BIT_HF_IRQ | ME4000_AI_CTRL_BIT_SC_IRQ |
+	      ME4000_AI_CTRL_BIT_DATA_FIFO);
+	/* Stop conversion of the state machine */
+	tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+
+	/* Clear circular buffer */
+	ai_context->circ_buf.head = 0;
+	ai_context->circ_buf.tail = 0;
+
+	while (inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) {
+		interruptible_sleep_on_timeout(&queue, 1);
+		if (signal_pending(current)) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_stop():Wait on state machine after stop interrupted\n");
+			return -EINTR;
+		}
+	}
+
+	return 0;
+}
+
+static int me4000_ai_ex_trig_enable(me4000_ai_context_t * ai_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ai_ex_trig_enable() is executed\n");
+
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp |= ME4000_AI_CTRL_BIT_EX_TRIG;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ai_ex_trig_disable(me4000_ai_context_t * ai_context)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ai_ex_trig_disable() is executed\n");
+
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+
+	return 0;
+}
+
+static int me4000_ai_ex_trig_setup(me4000_ai_trigger_t * arg,
+				   me4000_ai_context_t * ai_context)
+{
+	me4000_ai_trigger_t cmd;
+	int err;
+	u32 tmp;
+	unsigned long flags;
+
+	CALL_PDEBUG("me4000_ai_ex_trig_setup() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_ai_trigger_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_ex_trig_setup():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	spin_lock_irqsave(&ai_context->int_lock, flags);
+	tmp = me4000_inl(ai_context->ctrl_reg);
+
+	if (cmd.mode == ME4000_AI_TRIGGER_EXT_DIGITAL) {
+		tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG;
+	} else if (cmd.mode == ME4000_AI_TRIGGER_EXT_ANALOG) {
+		if (!ai_context->board_info->board_p->ai.ex_trig_analog) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_ex_trig_setup():No analog trigger available\n");
+			return -EINVAL;
+		}
+		tmp |= ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG;
+	} else {
+		spin_unlock_irqrestore(&ai_context->int_lock, flags);
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_ex_trig_setup():Invalid trigger mode specified\n");
+		return -EINVAL;
+	}
+
+	if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_RISING) {
+		tmp &=
+		    ~(ME4000_AI_CTRL_BIT_EX_TRIG_BOTH |
+		      ME4000_AI_CTRL_BIT_EX_TRIG_FALLING);
+	} else if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_FALLING) {
+		tmp |= ME4000_AI_CTRL_BIT_EX_TRIG_FALLING;
+		tmp &= ~ME4000_AI_CTRL_BIT_EX_TRIG_BOTH;
+	} else if (cmd.edge == ME4000_AI_TRIGGER_EXT_EDGE_BOTH) {
+		tmp |=
+		    ME4000_AI_CTRL_BIT_EX_TRIG_BOTH |
+		    ME4000_AI_CTRL_BIT_EX_TRIG_FALLING;
+	} else {
+		spin_unlock_irqrestore(&ai_context->int_lock, flags);
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_ex_trig_setup():Invalid trigger edge specified\n");
+		return -EINVAL;
+	}
+
+	me4000_outl(tmp, ai_context->ctrl_reg);
+	spin_unlock_irqrestore(&ai_context->int_lock, flags);
+	return 0;
+}
+
+static int me4000_ai_sc_setup(me4000_ai_sc_t * arg,
+			      me4000_ai_context_t * ai_context)
+{
+	me4000_ai_sc_t cmd;
+	int err;
+
+	CALL_PDEBUG("me4000_ai_sc_setup() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_ai_sc_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_sc_setup():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	ai_context->sample_counter = cmd.value;
+	ai_context->sample_counter_reload = cmd.reload;
+
+	return 0;
+}
+
+static ssize_t me4000_ai_read(struct file *filep, char *buff, size_t cnt,
+			      loff_t * offp)
+{
+	me4000_ai_context_t *ai_context = filep->private_data;
+	s16 *buffer = (s16 *) buff;
+	size_t count = cnt / 2;
+	unsigned long flags;
+	int tmp;
+	int c = 0;
+	int k = 0;
+	int ret = 0;
+	wait_queue_t wait;
+
+	CALL_PDEBUG("me4000_ai_read() is executed\n");
+
+	init_waitqueue_entry(&wait, current);
+
+	/* Check count */
+	if (count <= 0) {
+		PDEBUG("me4000_ai_read():Count is 0\n");
+		return 0;
+	}
+
+	while (count > 0) {
+		if (filep->f_flags & O_NONBLOCK) {
+			c = me4000_values_to_end(ai_context->circ_buf,
+						 ME4000_AI_BUFFER_COUNT);
+			if (!c) {
+				PDEBUG
+				    ("me4000_ai_read():Returning from nonblocking read\n");
+				break;
+			}
+		} else {
+			/* Check if conversion is still running */
+			if (!
+			    (me4000_inl(ai_context->status_reg) &
+			     ME4000_AI_STATUS_BIT_FSM)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_read():Conversion interrupted\n");
+				return -EPIPE;
+			}
+
+			wait_event_interruptible(ai_context->wait_queue,
+						 (me4000_values_to_end
+						  (ai_context->circ_buf,
+						   ME4000_AI_BUFFER_COUNT)));
+			if (signal_pending(current)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_ai_read():Wait on values interrupted from signal\n");
+				return -EINTR;
+			}
+		}
+
+		/* Only read count values or as much as available */
+		c = me4000_values_to_end(ai_context->circ_buf,
+					 ME4000_AI_BUFFER_COUNT);
+		PDEBUG("me4000_ai_read():%d values to end\n", c);
+		if (count < c)
+			c = count;
+
+		PDEBUG("me4000_ai_read():Copy %d values to user space\n", c);
+		k = 2 * c;
+		k -= copy_to_user(buffer,
+				  ai_context->circ_buf.buf +
+				  ai_context->circ_buf.tail, k);
+		c = k / 2;
+		if (!c) {
+			printk(KERN_ERR
+			       "ME4000:me4000_ai_read():Cannot copy new values to user\n");
+			return -EFAULT;
+		}
+
+		ai_context->circ_buf.tail =
+		    (ai_context->circ_buf.tail + c) & (ME4000_AI_BUFFER_COUNT -
+						       1);
+		buffer += c;
+		count -= c;
+		ret += c;
+
+		spin_lock_irqsave(&ai_context->int_lock, flags);
+		if (me4000_buf_space
+		    (ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) {
+			tmp = me4000_inl(ai_context->ctrl_reg);
+
+			/* Determine interrupt setup */
+			if (ai_context->sample_counter
+			    && !ai_context->sample_counter_reload) {
+				/* Enable Half Full Interrupt and Sample Counter Interrupt */
+				tmp |=
+				    ME4000_AI_CTRL_BIT_SC_IRQ |
+				    ME4000_AI_CTRL_BIT_HF_IRQ;
+			} else if (ai_context->sample_counter
+				   && ai_context->sample_counter_reload) {
+				if (ai_context->sample_counter <
+				    ME4000_AI_FIFO_COUNT / 2) {
+					/* Enable only Sample Counter Interrupt */
+					tmp |= ME4000_AI_CTRL_BIT_SC_IRQ;
+				} else {
+					/* Enable Half Full Interrupt and Sample Counter Interrupt */
+					tmp |=
+					    ME4000_AI_CTRL_BIT_SC_IRQ |
+					    ME4000_AI_CTRL_BIT_HF_IRQ;
+				}
+			} else {
+				/* Enable only Half Full Interrupt */
+				tmp |= ME4000_AI_CTRL_BIT_HF_IRQ;
+			}
+
+			me4000_outl(tmp, ai_context->ctrl_reg);
+		}
+		spin_unlock_irqrestore(&ai_context->int_lock, flags);
+	}
+
+	/* Check if conversion is still running */
+	if (!(me4000_inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM)) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_read():Conversion not running after complete read\n");
+		return -EPIPE;
+	}
+
+	if (filep->f_flags & O_NONBLOCK) {
+		return (k == 0) ? -EAGAIN : 2 * ret;
+	}
+
+	CALL_PDEBUG("me4000_ai_read() is leaved\n");
+	return ret * 2;
+}
+
+static unsigned int me4000_ai_poll(struct file *file_p, poll_table * wait)
+{
+	me4000_ai_context_t *ai_context;
+	unsigned long mask = 0;
+
+	CALL_PDEBUG("me4000_ai_poll() is executed\n");
+
+	ai_context = file_p->private_data;
+
+	/* Register wait queue */
+	poll_wait(file_p, &ai_context->wait_queue, wait);
+
+	/* Get available values */
+	if (me4000_values_to_end(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT))
+		mask |= POLLIN | POLLRDNORM;
+
+	PDEBUG("me4000_ai_poll():Return mask %lX\n", mask);
+
+	return mask;
+}
+
+static int me4000_ai_offset_enable(me4000_ai_context_t * ai_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ai_offset_enable() is executed\n");
+
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp |= ME4000_AI_CTRL_BIT_OFFSET;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ai_offset_disable(me4000_ai_context_t * ai_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ai_offset_disable() is executed\n");
+
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &= ~ME4000_AI_CTRL_BIT_OFFSET;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ai_fullscale_enable(me4000_ai_context_t * ai_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ai_fullscale_enable() is executed\n");
+
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp |= ME4000_AI_CTRL_BIT_FULLSCALE;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ai_fullscale_disable(me4000_ai_context_t * ai_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ai_fullscale_disable() is executed\n");
+
+	tmp = me4000_inl(ai_context->ctrl_reg);
+	tmp &= ~ME4000_AI_CTRL_BIT_FULLSCALE;
+	me4000_outl(tmp, ai_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ai_fsm_state(int *arg, me4000_ai_context_t * ai_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ai_fsm_state() is executed\n");
+
+	tmp =
+	    (me4000_inl(ai_context->status_reg) & ME4000_AI_STATUS_BIT_FSM) ? 1
+	    : 0;
+
+	if (put_user(tmp, arg)) {
+		printk(KERN_ERR "me4000_ai_fsm_state():Cannot copy to user\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int me4000_ai_get_count_buffer(unsigned long *arg,
+				      me4000_ai_context_t * ai_context)
+{
+	unsigned long c;
+	int err;
+
+	c = me4000_buf_count(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT);
+
+	err = copy_to_user(arg, &c, sizeof(unsigned long));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_ai_get_count_buffer():Can't copy to user space\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+/*---------------------------------- EEPROM stuff ---------------------------*/
+
+static int eeprom_write_cmd(me4000_ai_context_t * ai_context, unsigned long cmd,
+			    int length)
+{
+	int i;
+	unsigned long value;
+
+	CALL_PDEBUG("eeprom_write_cmd() is executed\n");
+
+	PDEBUG("eeprom_write_cmd():Write command 0x%08lX with length = %d\n",
+	       cmd, length);
+
+	/* Get the ICR register and clear the related bits */
+	value = me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR);
+	value &= ~(PLX_ICR_MASK_EEPROM);
+	me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR);
+
+	/* Raise the chip select */
+	value |= PLX_ICR_BIT_EEPROM_CHIP_SELECT;
+	me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR);
+	udelay(EEPROM_DELAY);
+
+	for (i = 0; i < length; i++) {
+		if (cmd & ((0x1 << (length - 1)) >> i)) {
+			value |= PLX_ICR_BIT_EEPROM_WRITE;
+		} else {
+			value &= ~PLX_ICR_BIT_EEPROM_WRITE;
+		}
+
+		/* Write to EEPROM */
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+
+		/* Raising edge of the clock */
+		value |= PLX_ICR_BIT_EEPROM_CLOCK_SET;
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+
+		/* Falling edge of the clock */
+		value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET;
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+	}
+
+	/* Clear the chip select */
+	value &= ~PLX_ICR_BIT_EEPROM_CHIP_SELECT;
+	me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR);
+	udelay(EEPROM_DELAY);
+
+	/* Wait until hardware is ready for sure */
+	mdelay(10);
+
+	return 0;
+}
+
+static unsigned short eeprom_read_cmd(me4000_ai_context_t * ai_context,
+				      unsigned long cmd, int length)
+{
+	int i;
+	unsigned long value;
+	unsigned short id = 0;
+
+	CALL_PDEBUG("eeprom_read_cmd() is executed\n");
+
+	PDEBUG("eeprom_read_cmd():Read command 0x%08lX with length = %d\n", cmd,
+	       length);
+
+	/* Get the ICR register and clear the related bits */
+	value = me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR);
+	value &= ~(PLX_ICR_MASK_EEPROM);
+
+	me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR);
+
+	/* Raise the chip select */
+	value |= PLX_ICR_BIT_EEPROM_CHIP_SELECT;
+	me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR);
+	udelay(EEPROM_DELAY);
+
+	/* Write the read command to the eeprom */
+	for (i = 0; i < length; i++) {
+		if (cmd & ((0x1 << (length - 1)) >> i)) {
+			value |= PLX_ICR_BIT_EEPROM_WRITE;
+		} else {
+			value &= ~PLX_ICR_BIT_EEPROM_WRITE;
+		}
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+
+		/* Raising edge of the clock */
+		value |= PLX_ICR_BIT_EEPROM_CLOCK_SET;
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+
+		/* Falling edge of the clock */
+		value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET;
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+	}
+
+	/* Read the value from the eeprom */
+	for (i = 0; i < 16; i++) {
+		/* Raising edge of the clock */
+		value |= PLX_ICR_BIT_EEPROM_CLOCK_SET;
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+
+		if (me4000_inl(ai_context->board_info->plx_regbase + PLX_ICR) &
+		    PLX_ICR_BIT_EEPROM_READ) {
+			id |= (0x8000 >> i);
+			PDEBUG("eeprom_read_cmd():OR with 0x%04X\n",
+			       (0x8000 >> i));
+		} else {
+			PDEBUG("eeprom_read_cmd():Dont't OR\n");
+		}
+
+		/* Falling edge of the clock */
+		value &= ~PLX_ICR_BIT_EEPROM_CLOCK_SET;
+		me4000_outl(value,
+			    ai_context->board_info->plx_regbase + PLX_ICR);
+		udelay(EEPROM_DELAY);
+	}
+
+	/* Clear the chip select */
+	value &= ~PLX_ICR_BIT_EEPROM_CHIP_SELECT;
+	me4000_outl(value, ai_context->board_info->plx_regbase + PLX_ICR);
+	udelay(EEPROM_DELAY);
+
+	return id;
+}
+
+static int me4000_eeprom_write(me4000_eeprom_t * arg,
+			       me4000_ai_context_t * ai_context)
+{
+	int err;
+	me4000_eeprom_t setup;
+	unsigned long cmd;
+	unsigned long date_high;
+	unsigned long date_low;
+
+	CALL_PDEBUG("me4000_eeprom_write() is executed\n");
+
+	err = copy_from_user(&setup, arg, sizeof(setup));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_eeprom_write():Cannot copy from user\n");
+		return err;
+	}
+
+	/* Enable writing */
+	eeprom_write_cmd(ai_context, ME4000_EEPROM_CMD_WRITE_ENABLE,
+			 ME4000_EEPROM_CMD_LENGTH_WRITE_ENABLE);
+
+	/* Command for date */
+	date_high = (setup.date & 0xFFFF0000) >> 16;
+	date_low = (setup.date & 0x0000FFFF);
+
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_DATE_HIGH <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     date_high);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_DATE_LOW <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     date_low);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for unipolar 10V offset */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     uni_10_offset);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for unipolar 10V fullscale */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     uni_10_fullscale);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for unipolar 2,5V offset */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     uni_2_5_offset);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for unipolar 2,5V fullscale */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     uni_2_5_fullscale);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for bipolar 10V offset */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     bi_10_offset);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for bipolar 10V fullscale */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     bi_10_fullscale);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for bipolar 2,5V offset */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     bi_2_5_offset);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for bipolar 2,5V fullscale */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     bi_2_5_fullscale);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for differential 10V offset */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     diff_10_offset);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for differential 10V fullscale */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE
+				       << ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+									(unsigned
+									 long)
+									setup.
+									diff_10_fullscale);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for differential 2,5V offset */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET <<
+				       ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+								     (unsigned
+								      long)
+								     setup.
+								     diff_2_5_offset);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Command for differential 2,5V fullscale */
+	cmd =
+	    ME4000_EEPROM_CMD_WRITE | (ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE
+				       << ME4000_EEPROM_DATA_LENGTH) | (0xFFFF &
+									(unsigned
+									 long)
+									setup.
+									diff_2_5_fullscale);
+	err = eeprom_write_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_WRITE);
+	if (err)
+		return err;
+
+	/* Disable writing */
+	eeprom_write_cmd(ai_context, ME4000_EEPROM_CMD_WRITE_DISABLE,
+			 ME4000_EEPROM_CMD_LENGTH_WRITE_DISABLE);
+
+	return 0;
+}
+
+static int me4000_eeprom_read(me4000_eeprom_t * arg,
+			      me4000_ai_context_t * ai_context)
+{
+	int err;
+	unsigned long cmd;
+	me4000_eeprom_t setup;
+
+	CALL_PDEBUG("me4000_eeprom_read() is executed\n");
+
+	/* Command for date */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_DATE_HIGH;
+	setup.date =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+	setup.date <<= 16;
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_DATE_LOW;
+	setup.date |=
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for unipolar 10V offset */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET;
+	setup.uni_10_offset =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for unipolar 10V fullscale */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE;
+	setup.uni_10_fullscale =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for unipolar 2,5V offset */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET;
+	setup.uni_2_5_offset =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for unipolar 2,5V fullscale */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE;
+	setup.uni_2_5_fullscale =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for bipolar 10V offset */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET;
+	setup.bi_10_offset =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for bipolar 10V fullscale */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE;
+	setup.bi_10_fullscale =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for bipolar 2,5V offset */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET;
+	setup.bi_2_5_offset =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for bipolar 2,5V fullscale */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE;
+	setup.bi_2_5_fullscale =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for differntial 10V offset */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET;
+	setup.diff_10_offset =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for differential 10V fullscale */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE;
+	setup.diff_10_fullscale =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for differntial 2,5V offset */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET;
+	setup.diff_2_5_offset =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	/* Command for differential 2,5V fullscale */
+	cmd = ME4000_EEPROM_CMD_READ | ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE;
+	setup.diff_2_5_fullscale =
+	    eeprom_read_cmd(ai_context, cmd, ME4000_EEPROM_CMD_LENGTH_READ);
+
+	err = copy_to_user(arg, &setup, sizeof(setup));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_eeprom_read():Cannot copy to user\n");
+		return err;
+	}
+
+	return 0;
+}
+
+/*------------------------------------ DIO stuff ----------------------------------------------*/
+
+static int me4000_dio_ioctl(struct inode *inode_p, struct file *file_p,
+			    unsigned int service, unsigned long arg)
+{
+	me4000_dio_context_t *dio_context;
+
+	CALL_PDEBUG("me4000_dio_ioctl() is executed\n");
+
+	dio_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		printk(KERN_ERR "me4000_dio_ioctl():Wrong magic number\n");
+		return -ENOTTY;
+	}
+	if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) {
+		printk(KERN_ERR "me4000_dio_ioctl():Service number to high\n");
+		return -ENOTTY;
+	}
+
+	switch (service) {
+	case ME4000_DIO_CONFIG:
+		return me4000_dio_config((me4000_dio_config_t *) arg,
+					 dio_context);
+	case ME4000_DIO_SET_BYTE:
+		return me4000_dio_set_byte((me4000_dio_byte_t *) arg,
+					   dio_context);
+	case ME4000_DIO_GET_BYTE:
+		return me4000_dio_get_byte((me4000_dio_byte_t *) arg,
+					   dio_context);
+	case ME4000_DIO_RESET:
+		return me4000_dio_reset(dio_context);
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_ioctl():Invalid service number %d\n",
+		       service);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_dio_config(me4000_dio_config_t * arg,
+			     me4000_dio_context_t * dio_context)
+{
+	me4000_dio_config_t cmd;
+	u32 tmp;
+	int err;
+
+	CALL_PDEBUG("me4000_dio_config() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_dio_config_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_config():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Check port parameter */
+	if (cmd.port >= dio_context->dio_count) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_config():Port %d is not available\n",
+		       cmd.port);
+		return -EINVAL;
+	}
+
+	PDEBUG("me4000_dio_config(): port %d, mode %d, function %d\n", cmd.port,
+	       cmd.mode, cmd.function);
+
+	if (cmd.port == ME4000_DIO_PORT_A) {
+		if (cmd.mode == ME4000_DIO_PORT_INPUT) {
+			/* Check if opto isolated version */
+			if (!(me4000_inl(dio_context->dir_reg) & 0x1)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_dio_config():Cannot set to input on opto isolated versions\n");
+				return -EIO;
+			}
+
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_0 |
+			      ME4000_DIO_CTRL_BIT_MODE_1);
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_0 |
+			      ME4000_DIO_CTRL_BIT_MODE_1);
+			tmp |= ME4000_DIO_CTRL_BIT_MODE_0;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_LOW) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_0 |
+			      ME4000_DIO_CTRL_BIT_MODE_1 |
+			      ME4000_DIO_CTRL_BIT_FIFO_HIGH_0);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_0 |
+			    ME4000_DIO_CTRL_BIT_MODE_1;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_HIGH) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_0 |
+			    ME4000_DIO_CTRL_BIT_MODE_1 |
+			    ME4000_DIO_CTRL_BIT_FIFO_HIGH_0;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_config():Mode %d is not available\n",
+			       cmd.mode);
+			return -EINVAL;
+		}
+	} else if (cmd.port == ME4000_DIO_PORT_B) {
+		if (cmd.mode == ME4000_DIO_PORT_INPUT) {
+			/* Only do anything when TTL version is installed */
+			if ((me4000_inl(dio_context->dir_reg) & 0x1)) {
+				tmp = me4000_inl(dio_context->ctrl_reg);
+				tmp &=
+				    ~(ME4000_DIO_CTRL_BIT_MODE_2 |
+				      ME4000_DIO_CTRL_BIT_MODE_3);
+				me4000_outl(tmp, dio_context->ctrl_reg);
+			}
+		} else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) {
+			/* Check if opto isolated version */
+			if (!(me4000_inl(dio_context->dir_reg) & 0x1)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_dio_config():Cannot set to output on opto isolated versions\n");
+				return -EIO;
+			}
+
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_2 |
+			      ME4000_DIO_CTRL_BIT_MODE_3);
+			tmp |= ME4000_DIO_CTRL_BIT_MODE_2;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_LOW) {
+			/* Check if opto isolated version */
+			if (!(me4000_inl(dio_context->dir_reg) & 0x1)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_dio_config():Cannot set to FIFO low output on opto isolated versions\n");
+				return -EIO;
+			}
+
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_2 |
+			      ME4000_DIO_CTRL_BIT_MODE_3 |
+			      ME4000_DIO_CTRL_BIT_FIFO_HIGH_1);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_2 |
+			    ME4000_DIO_CTRL_BIT_MODE_3;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_HIGH) {
+			/* Check if opto isolated version */
+			if (!(me4000_inl(dio_context->dir_reg) & 0x1)) {
+				printk(KERN_ERR
+				       "ME4000:me4000_dio_config():Cannot set to FIFO high output on opto isolated versions\n");
+				return -EIO;
+			}
+
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_2 |
+			    ME4000_DIO_CTRL_BIT_MODE_3 |
+			    ME4000_DIO_CTRL_BIT_FIFO_HIGH_1;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_config():Mode %d is not available\n",
+			       cmd.mode);
+			return -EINVAL;
+		}
+	} else if (cmd.port == ME4000_DIO_PORT_C) {
+		if (cmd.mode == ME4000_DIO_PORT_INPUT) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_4 |
+			      ME4000_DIO_CTRL_BIT_MODE_5);
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_4 |
+			      ME4000_DIO_CTRL_BIT_MODE_5);
+			tmp |= ME4000_DIO_CTRL_BIT_MODE_4;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_LOW) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_4 |
+			      ME4000_DIO_CTRL_BIT_MODE_5 |
+			      ME4000_DIO_CTRL_BIT_FIFO_HIGH_2);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_4 |
+			    ME4000_DIO_CTRL_BIT_MODE_5;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_HIGH) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_4 |
+			    ME4000_DIO_CTRL_BIT_MODE_5 |
+			    ME4000_DIO_CTRL_BIT_FIFO_HIGH_2;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_config():Mode %d is not available\n",
+			       cmd.mode);
+			return -EINVAL;
+		}
+	} else if (cmd.port == ME4000_DIO_PORT_D) {
+		if (cmd.mode == ME4000_DIO_PORT_INPUT) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_6 |
+			      ME4000_DIO_CTRL_BIT_MODE_7);
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_PORT_OUTPUT) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_6 |
+			      ME4000_DIO_CTRL_BIT_MODE_7);
+			tmp |= ME4000_DIO_CTRL_BIT_MODE_6;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_LOW) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp &=
+			    ~(ME4000_DIO_CTRL_BIT_MODE_6 |
+			      ME4000_DIO_CTRL_BIT_MODE_7 |
+			      ME4000_DIO_CTRL_BIT_FIFO_HIGH_3);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_6 |
+			    ME4000_DIO_CTRL_BIT_MODE_7;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.mode == ME4000_DIO_FIFO_HIGH) {
+			tmp = me4000_inl(dio_context->ctrl_reg);
+			tmp |=
+			    ME4000_DIO_CTRL_BIT_MODE_6 |
+			    ME4000_DIO_CTRL_BIT_MODE_7 |
+			    ME4000_DIO_CTRL_BIT_FIFO_HIGH_3;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_config():Mode %d is not available\n",
+			       cmd.mode);
+			return -EINVAL;
+		}
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_config():Port %d is not available\n",
+		       cmd.port);
+		return -EINVAL;
+	}
+
+	PDEBUG("me4000_dio_config(): port %d, mode %d, function %d\n", cmd.port,
+	       cmd.mode, cmd.function);
+
+	if ((cmd.mode == ME4000_DIO_FIFO_HIGH)
+	    || (cmd.mode == ME4000_DIO_FIFO_LOW)) {
+		tmp = me4000_inl(dio_context->ctrl_reg);
+		tmp &=
+		    ~(ME4000_DIO_CTRL_BIT_FUNCTION_0 |
+		      ME4000_DIO_CTRL_BIT_FUNCTION_1);
+		if (cmd.function == ME4000_DIO_FUNCTION_PATTERN) {
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.function == ME4000_DIO_FUNCTION_DEMUX) {
+			tmp |= ME4000_DIO_CTRL_BIT_FUNCTION_0;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else if (cmd.function == ME4000_DIO_FUNCTION_MUX) {
+			tmp |= ME4000_DIO_CTRL_BIT_FUNCTION_1;
+			me4000_outl(tmp, dio_context->ctrl_reg);
+		} else {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_config():Invalid port function specified\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int me4000_dio_set_byte(me4000_dio_byte_t * arg,
+			       me4000_dio_context_t * dio_context)
+{
+	me4000_dio_byte_t cmd;
+	int err;
+
+	CALL_PDEBUG("me4000_dio_set_byte() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_dio_byte_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_set_byte():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Check port parameter */
+	if (cmd.port >= dio_context->dio_count) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_set_byte():Port %d is not available\n",
+		       cmd.port);
+		return -EINVAL;
+	}
+
+	if (cmd.port == ME4000_DIO_PORT_A) {
+		if ((me4000_inl(dio_context->ctrl_reg) & 0x3) != 0x1) {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n",
+			       cmd.port);
+			return -EIO;
+		}
+		me4000_outl(cmd.byte, dio_context->port_0_reg);
+	} else if (cmd.port == ME4000_DIO_PORT_B) {
+		if ((me4000_inl(dio_context->ctrl_reg) & 0xC) != 0x4) {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n",
+			       cmd.port);
+			return -EIO;
+		}
+		me4000_outl(cmd.byte, dio_context->port_1_reg);
+	} else if (cmd.port == ME4000_DIO_PORT_C) {
+		if ((me4000_inl(dio_context->ctrl_reg) & 0x30) != 0x10) {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n",
+			       cmd.port);
+			return -EIO;
+		}
+		me4000_outl(cmd.byte, dio_context->port_2_reg);
+	} else if (cmd.port == ME4000_DIO_PORT_D) {
+		if ((me4000_inl(dio_context->ctrl_reg) & 0xC0) != 0x40) {
+			printk(KERN_ERR
+			       "ME4000:me4000_dio_set_byte():Port %d is not in output mode\n",
+			       cmd.port);
+			return -EIO;
+		}
+		me4000_outl(cmd.byte, dio_context->port_3_reg);
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_set_byte():Port %d is not available\n",
+		       cmd.port);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int me4000_dio_get_byte(me4000_dio_byte_t * arg,
+			       me4000_dio_context_t * dio_context)
+{
+	me4000_dio_byte_t cmd;
+	int err;
+
+	CALL_PDEBUG("me4000_dio_get_byte() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_dio_byte_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_get_byte():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Check port parameter */
+	if (cmd.port >= dio_context->dio_count) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_get_byte():Port %d is not available\n",
+		       cmd.port);
+		return -EINVAL;
+	}
+
+	if (cmd.port == ME4000_DIO_PORT_A) {
+		cmd.byte = me4000_inl(dio_context->port_0_reg) & 0xFF;
+	} else if (cmd.port == ME4000_DIO_PORT_B) {
+		cmd.byte = me4000_inl(dio_context->port_1_reg) & 0xFF;
+	} else if (cmd.port == ME4000_DIO_PORT_C) {
+		cmd.byte = me4000_inl(dio_context->port_2_reg) & 0xFF;
+	} else if (cmd.port == ME4000_DIO_PORT_D) {
+		cmd.byte = me4000_inl(dio_context->port_3_reg) & 0xFF;
+	} else {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_get_byte():Port %d is not available\n",
+		       cmd.port);
+		return -EINVAL;
+	}
+
+	/* Copy result back to user */
+	err = copy_to_user(arg, &cmd, sizeof(me4000_dio_byte_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_get_byte():Can't copy to user space\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int me4000_dio_reset(me4000_dio_context_t * dio_context)
+{
+	CALL_PDEBUG("me4000_dio_reset() is executed\n");
+
+	/* Clear the control register */
+	me4000_outl(0, dio_context->ctrl_reg);
+
+	/* Check for opto isolated version */
+	if (!(me4000_inl(dio_context->dir_reg) & 0x1)) {
+		me4000_outl(0x1, dio_context->ctrl_reg);
+		me4000_outl(0x0, dio_context->port_0_reg);
+	}
+
+	return 0;
+}
+
+/*------------------------------------ COUNTER STUFF ------------------------------------*/
+
+static int me4000_cnt_ioctl(struct inode *inode_p, struct file *file_p,
+			    unsigned int service, unsigned long arg)
+{
+	me4000_cnt_context_t *cnt_context;
+
+	CALL_PDEBUG("me4000_cnt_ioctl() is executed\n");
+
+	cnt_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		printk(KERN_ERR "me4000_dio_ioctl():Wrong magic number\n");
+		return -ENOTTY;
+	}
+	if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) {
+		printk(KERN_ERR "me4000_dio_ioctl():Service number to high\n");
+		return -ENOTTY;
+	}
+
+	switch (service) {
+	case ME4000_CNT_READ:
+		return me4000_cnt_read((me4000_cnt_t *) arg, cnt_context);
+	case ME4000_CNT_WRITE:
+		return me4000_cnt_write((me4000_cnt_t *) arg, cnt_context);
+	case ME4000_CNT_CONFIG:
+		return me4000_cnt_config((me4000_cnt_config_t *) arg,
+					 cnt_context);
+	case ME4000_CNT_RESET:
+		return me4000_cnt_reset(cnt_context);
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_dio_ioctl():Invalid service number %d\n",
+		       service);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_cnt_config(me4000_cnt_config_t * arg,
+			     me4000_cnt_context_t * cnt_context)
+{
+	me4000_cnt_config_t cmd;
+	u8 counter;
+	u8 mode;
+	int err;
+
+	CALL_PDEBUG("me4000_cnt_config() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_cnt_config_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_config():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Check counter parameter */
+	switch (cmd.counter) {
+	case ME4000_CNT_COUNTER_0:
+		counter = ME4000_CNT_CTRL_BIT_COUNTER_0;
+		break;
+	case ME4000_CNT_COUNTER_1:
+		counter = ME4000_CNT_CTRL_BIT_COUNTER_1;
+		break;
+	case ME4000_CNT_COUNTER_2:
+		counter = ME4000_CNT_CTRL_BIT_COUNTER_2;
+		break;
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_config():Counter %d is not available\n",
+		       cmd.counter);
+		return -EINVAL;
+	}
+
+	/* Check mode parameter */
+	switch (cmd.mode) {
+	case ME4000_CNT_MODE_0:
+		mode = ME4000_CNT_CTRL_BIT_MODE_0;
+		break;
+	case ME4000_CNT_MODE_1:
+		mode = ME4000_CNT_CTRL_BIT_MODE_1;
+		break;
+	case ME4000_CNT_MODE_2:
+		mode = ME4000_CNT_CTRL_BIT_MODE_2;
+		break;
+	case ME4000_CNT_MODE_3:
+		mode = ME4000_CNT_CTRL_BIT_MODE_3;
+		break;
+	case ME4000_CNT_MODE_4:
+		mode = ME4000_CNT_CTRL_BIT_MODE_4;
+		break;
+	case ME4000_CNT_MODE_5:
+		mode = ME4000_CNT_CTRL_BIT_MODE_5;
+		break;
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_config():Mode %d is not available\n",
+		       cmd.mode);
+		return -EINVAL;
+	}
+
+	/* Write the control word */
+	me4000_outb((counter | mode | 0x30), cnt_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_cnt_read(me4000_cnt_t * arg,
+			   me4000_cnt_context_t * cnt_context)
+{
+	me4000_cnt_t cmd;
+	u8 tmp;
+	int err;
+
+	CALL_PDEBUG("me4000_cnt_read() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_cnt_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_read():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Read counter */
+	switch (cmd.counter) {
+	case ME4000_CNT_COUNTER_0:
+		tmp = me4000_inb(cnt_context->counter_0_reg);
+		cmd.value = tmp;
+		tmp = me4000_inb(cnt_context->counter_0_reg);
+		cmd.value |= ((u16) tmp) << 8;
+		break;
+	case ME4000_CNT_COUNTER_1:
+		tmp = me4000_inb(cnt_context->counter_1_reg);
+		cmd.value = tmp;
+		tmp = me4000_inb(cnt_context->counter_1_reg);
+		cmd.value |= ((u16) tmp) << 8;
+		break;
+	case ME4000_CNT_COUNTER_2:
+		tmp = me4000_inb(cnt_context->counter_2_reg);
+		cmd.value = tmp;
+		tmp = me4000_inb(cnt_context->counter_2_reg);
+		cmd.value |= ((u16) tmp) << 8;
+		break;
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_read():Counter %d is not available\n",
+		       cmd.counter);
+		return -EINVAL;
+	}
+
+	/* Copy result back to user */
+	err = copy_to_user(arg, &cmd, sizeof(me4000_cnt_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_read():Can't copy to user space\n");
+		return -EFAULT;
+	}
+
+	return 0;
+}
+
+static int me4000_cnt_write(me4000_cnt_t * arg,
+			    me4000_cnt_context_t * cnt_context)
+{
+	me4000_cnt_t cmd;
+	u8 tmp;
+	int err;
+
+	CALL_PDEBUG("me4000_cnt_write() is executed\n");
+
+	/* Copy data from user */
+	err = copy_from_user(&cmd, arg, sizeof(me4000_cnt_t));
+	if (err) {
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_write():Can't copy from user space\n");
+		return -EFAULT;
+	}
+
+	/* Write counter */
+	switch (cmd.counter) {
+	case ME4000_CNT_COUNTER_0:
+		tmp = cmd.value & 0xFF;
+		me4000_outb(tmp, cnt_context->counter_0_reg);
+		tmp = (cmd.value >> 8) & 0xFF;
+		me4000_outb(tmp, cnt_context->counter_0_reg);
+		break;
+	case ME4000_CNT_COUNTER_1:
+		tmp = cmd.value & 0xFF;
+		me4000_outb(tmp, cnt_context->counter_1_reg);
+		tmp = (cmd.value >> 8) & 0xFF;
+		me4000_outb(tmp, cnt_context->counter_1_reg);
+		break;
+	case ME4000_CNT_COUNTER_2:
+		tmp = cmd.value & 0xFF;
+		me4000_outb(tmp, cnt_context->counter_2_reg);
+		tmp = (cmd.value >> 8) & 0xFF;
+		me4000_outb(tmp, cnt_context->counter_2_reg);
+		break;
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_cnt_write():Counter %d is not available\n",
+		       cmd.counter);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int me4000_cnt_reset(me4000_cnt_context_t * cnt_context)
+{
+	CALL_PDEBUG("me4000_cnt_reset() is executed\n");
+
+	/* Set the mode and value for counter 0 */
+	me4000_outb(0x30, cnt_context->ctrl_reg);
+	me4000_outb(0x00, cnt_context->counter_0_reg);
+	me4000_outb(0x00, cnt_context->counter_0_reg);
+
+	/* Set the mode and value for counter 1 */
+	me4000_outb(0x70, cnt_context->ctrl_reg);
+	me4000_outb(0x00, cnt_context->counter_1_reg);
+	me4000_outb(0x00, cnt_context->counter_1_reg);
+
+	/* Set the mode and value for counter 2 */
+	me4000_outb(0xB0, cnt_context->ctrl_reg);
+	me4000_outb(0x00, cnt_context->counter_2_reg);
+	me4000_outb(0x00, cnt_context->counter_2_reg);
+
+	return 0;
+}
+
+/*------------------------------------ External Interrupt stuff ------------------------------------*/
+
+static int me4000_ext_int_ioctl(struct inode *inode_p, struct file *file_p,
+				unsigned int service, unsigned long arg)
+{
+	me4000_ext_int_context_t *ext_int_context;
+
+	CALL_PDEBUG("me4000_ext_int_ioctl() is executed\n");
+
+	ext_int_context = file_p->private_data;
+
+	if (_IOC_TYPE(service) != ME4000_MAGIC) {
+		printk(KERN_ERR "me4000_ext_int_ioctl():Wrong magic number\n");
+		return -ENOTTY;
+	}
+	if (_IOC_NR(service) > ME4000_IOCTL_MAXNR) {
+		printk(KERN_ERR
+		       "me4000_ext_int_ioctl():Service number to high\n");
+		return -ENOTTY;
+	}
+
+	switch (service) {
+	case ME4000_EXT_INT_ENABLE:
+		return me4000_ext_int_enable(ext_int_context);
+	case ME4000_EXT_INT_DISABLE:
+		return me4000_ext_int_disable(ext_int_context);
+	case ME4000_EXT_INT_COUNT:
+		return me4000_ext_int_count((unsigned long *)arg,
+					    ext_int_context);
+	default:
+		printk(KERN_ERR
+		       "ME4000:me4000_ext_int_ioctl():Invalid service number %d\n",
+		       service);
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static int me4000_ext_int_enable(me4000_ext_int_context_t * ext_int_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ext_int_enable() is executed\n");
+
+	tmp = me4000_inl(ext_int_context->ctrl_reg);
+	tmp |= ME4000_AI_CTRL_BIT_EX_IRQ;
+	me4000_outl(tmp, ext_int_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ext_int_disable(me4000_ext_int_context_t * ext_int_context)
+{
+	unsigned long tmp;
+
+	CALL_PDEBUG("me4000_ext_int_disable() is executed\n");
+
+	tmp = me4000_inl(ext_int_context->ctrl_reg);
+	tmp &= ~ME4000_AI_CTRL_BIT_EX_IRQ;
+	me4000_outl(tmp, ext_int_context->ctrl_reg);
+
+	return 0;
+}
+
+static int me4000_ext_int_count(unsigned long *arg,
+				me4000_ext_int_context_t * ext_int_context)
+{
+
+	CALL_PDEBUG("me4000_ext_int_count() is executed\n");
+
+	put_user(ext_int_context->int_count, arg);
+	return 0;
+}
+
+/*------------------------------------ General stuff ------------------------------------*/
+
+static int me4000_get_user_info(me4000_user_info_t * arg,
+				me4000_info_t * board_info)
+{
+	me4000_user_info_t user_info;
+
+	CALL_PDEBUG("me4000_get_user_info() is executed\n");
+
+	user_info.board_count = board_info->board_count;
+	user_info.plx_regbase = board_info->plx_regbase;
+	user_info.plx_regbase_size = board_info->plx_regbase_size;
+	user_info.me4000_regbase = board_info->me4000_regbase;
+	user_info.me4000_regbase_size = board_info->me4000_regbase_size;
+	user_info.serial_no = board_info->serial_no;
+	user_info.hw_revision = board_info->hw_revision;
+	user_info.vendor_id = board_info->vendor_id;
+	user_info.device_id = board_info->device_id;
+	user_info.pci_bus_no = board_info->pci_bus_no;
+	user_info.pci_dev_no = board_info->pci_dev_no;
+	user_info.pci_func_no = board_info->pci_func_no;
+	user_info.irq = board_info->irq;
+	user_info.irq_count = board_info->irq_count;
+	user_info.driver_version = ME4000_DRIVER_VERSION;
+	user_info.ao_count = board_info->board_p->ao.count;
+	user_info.ao_fifo_count = board_info->board_p->ao.fifo_count;
+
+	user_info.ai_count = board_info->board_p->ai.count;
+	user_info.ai_sh_count = board_info->board_p->ai.sh_count;
+	user_info.ai_ex_trig_analog = board_info->board_p->ai.ex_trig_analog;
+
+	user_info.dio_count = board_info->board_p->dio.count;
+
+	user_info.cnt_count = board_info->board_p->cnt.count;
+
+	if (copy_to_user(arg, &user_info, sizeof(me4000_user_info_t)))
+		return -EFAULT;
+
+	return 0;
+}
+
+/*------------------------------------ ISR STUFF ------------------------------------*/
+
+static int me4000_ext_int_fasync(int fd, struct file *file_ptr, int mode)
+{
+	int result = 0;
+	me4000_ext_int_context_t *ext_int_context;
+
+	CALL_PDEBUG("me4000_ext_int_fasync() is executed\n");
+
+	ext_int_context = file_ptr->private_data;
+
+	result =
+	    fasync_helper(fd, file_ptr, mode, &ext_int_context->fasync_ptr);
+
+	CALL_PDEBUG("me4000_ext_int_fasync() is leaved\n");
+	return result;
+}
+
+static irqreturn_t me4000_ao_isr(int irq, void *dev_id)
+{
+	u32 tmp;
+	u32 value;
+	me4000_ao_context_t *ao_context;
+	int i;
+	int c = 0;
+	int c1 = 0;
+	//unsigned long before;
+	//unsigned long after;
+
+	ISR_PDEBUG("me4000_ao_isr() is executed\n");
+
+	ao_context = dev_id;
+
+	/* Check if irq number is right */
+	if (irq != ao_context->irq) {
+		ISR_PDEBUG("me4000_ao_isr():incorrect interrupt num: %d\n",
+			   irq);
+		return IRQ_NONE;
+	}
+
+	/* Check if this DAC rised an interrupt */
+	if (!
+	    ((0x1 << (ao_context->index + 3)) &
+	     me4000_inl(ao_context->irq_status_reg))) {
+		ISR_PDEBUG("me4000_ao_isr():Not this DAC\n");
+		return IRQ_NONE;
+	}
+
+	/* Read status register to find out what happened */
+	tmp = me4000_inl(ao_context->status_reg);
+
+	if (!(tmp & ME4000_AO_STATUS_BIT_EF) && (tmp & ME4000_AO_STATUS_BIT_HF)
+	    && (tmp & ME4000_AO_STATUS_BIT_HF)) {
+		c = ME4000_AO_FIFO_COUNT;
+		ISR_PDEBUG("me4000_ao_isr():Fifo empty\n");
+	} else if ((tmp & ME4000_AO_STATUS_BIT_EF)
+		   && (tmp & ME4000_AO_STATUS_BIT_HF)
+		   && (tmp & ME4000_AO_STATUS_BIT_HF)) {
+		c = ME4000_AO_FIFO_COUNT / 2;
+		ISR_PDEBUG("me4000_ao_isr():Fifo under half full\n");
+	} else {
+		c = 0;
+		ISR_PDEBUG("me4000_ao_isr():Fifo full\n");
+	}
+
+	ISR_PDEBUG("me4000_ao_isr():Try to write 0x%04X values\n", c);
+
+	while (1) {
+		c1 = me4000_values_to_end(ao_context->circ_buf,
+					  ME4000_AO_BUFFER_COUNT);
+		ISR_PDEBUG("me4000_ao_isr():Values to end = %d\n", c1);
+		if (c1 > c)
+			c1 = c;
+
+		if (c1 <= 0) {
+			ISR_PDEBUG
+			    ("me4000_ao_isr():Work done or buffer empty\n");
+			break;
+		}
+		//rdtscl(before);
+		if (((ao_context->fifo_reg & 0xFF) == ME4000_AO_01_FIFO_REG) ||
+		    ((ao_context->fifo_reg & 0xFF) == ME4000_AO_03_FIFO_REG)) {
+			for (i = 0; i < c1; i++) {
+				value =
+				    ((u32)
+				     (*
+				      (ao_context->circ_buf.buf +
+				       ao_context->circ_buf.tail + i))) << 16;
+				outl(value, ao_context->fifo_reg);
+			}
+		} else
+			outsw(ao_context->fifo_reg,
+			      ao_context->circ_buf.buf +
+			      ao_context->circ_buf.tail, c1);
+
+		//rdtscl(after);
+		//printk(KERN_ERR"ME4000:me4000_ao_isr():Time lapse = %lu\n", after - before);
+
+		ao_context->circ_buf.tail =
+		    (ao_context->circ_buf.tail + c1) & (ME4000_AO_BUFFER_COUNT -
+							1);
+		ISR_PDEBUG("me4000_ao_isr():%d values wrote to port 0x%04X\n",
+			   c1, ao_context->fifo_reg);
+		c -= c1;
+	}
+
+	/* If there are no values left in the buffer, disable interrupts */
+	spin_lock(&ao_context->int_lock);
+	if (!me4000_buf_count(ao_context->circ_buf, ME4000_AO_BUFFER_COUNT)) {
+		ISR_PDEBUG
+		    ("me4000_ao_isr():Disable Interrupt because no values left in buffer\n");
+		tmp = me4000_inl(ao_context->ctrl_reg);
+		tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_IRQ;
+		me4000_outl(tmp, ao_context->ctrl_reg);
+	}
+	spin_unlock(&ao_context->int_lock);
+
+	/* Reset the interrupt */
+	spin_lock(&ao_context->int_lock);
+	tmp = me4000_inl(ao_context->ctrl_reg);
+	tmp |= ME4000_AO_CTRL_BIT_RESET_IRQ;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	tmp &= ~ME4000_AO_CTRL_BIT_RESET_IRQ;
+	me4000_outl(tmp, ao_context->ctrl_reg);
+
+	/* If state machine is stopped, flow was interrupted */
+	if (!(me4000_inl(ao_context->status_reg) & ME4000_AO_STATUS_BIT_FSM)) {
+		printk(KERN_ERR "ME4000:me4000_ao_isr():Broken pipe\n");
+		ao_context->pipe_flag = 1;	// Set flag in order to inform write routine
+		tmp &= ~ME4000_AO_CTRL_BIT_ENABLE_IRQ;	// Disable interrupt
+	}
+	me4000_outl(tmp, ao_context->ctrl_reg);
+	spin_unlock(&ao_context->int_lock);
+
+	/* Wake up waiting process */
+	wake_up_interruptible(&(ao_context->wait_queue));
+
+	/* Count the interrupt */
+	ao_context->board_info->irq_count++;
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t me4000_ai_isr(int irq, void *dev_id)
+{
+	u32 tmp;
+	me4000_ai_context_t *ai_context;
+	int i;
+	int c = 0;
+	int c1 = 0;
+#ifdef ME4000_ISR_DEBUG
+	unsigned long before;
+	unsigned long after;
+#endif
+
+	ISR_PDEBUG("me4000_ai_isr() is executed\n");
+
+#ifdef ME4000_ISR_DEBUG
+	rdtscl(before);
+#endif
+
+	ai_context = dev_id;
+
+	/* Check if irq number is right */
+	if (irq != ai_context->irq) {
+		ISR_PDEBUG("me4000_ai_isr():incorrect interrupt num: %d\n",
+			   irq);
+		return IRQ_NONE;
+	}
+
+	if (me4000_inl(ai_context->irq_status_reg) &
+	    ME4000_IRQ_STATUS_BIT_AI_HF) {
+		ISR_PDEBUG
+		    ("me4000_ai_isr():Fifo half full interrupt occured\n");
+
+		/* Read status register to find out what happened */
+		tmp = me4000_inl(ai_context->ctrl_reg);
+
+		if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
+		    !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
+		    && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
+			ISR_PDEBUG("me4000_ai_isr():Fifo full\n");
+			c = ME4000_AI_FIFO_COUNT;
+
+			/* FIFO overflow, so stop conversion and disable all interrupts */
+			spin_lock(&ai_context->int_lock);
+			tmp = me4000_inl(ai_context->ctrl_reg);
+			tmp |= ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+			tmp &=
+			    ~(ME4000_AI_CTRL_BIT_HF_IRQ |
+			      ME4000_AI_CTRL_BIT_SC_IRQ);
+			outl(tmp, ai_context->ctrl_reg);
+			spin_unlock(&ai_context->int_lock);
+		} else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
+			   !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
+			   && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
+			ISR_PDEBUG("me4000_ai_isr():Fifo half full\n");
+			c = ME4000_AI_FIFO_COUNT / 2;
+		} else {
+			c = 0;
+			ISR_PDEBUG
+			    ("me4000_ai_isr():Can't determine state of fifo\n");
+		}
+
+		ISR_PDEBUG("me4000_ai_isr():Try to read %d values\n", c);
+
+		while (1) {
+			c1 = me4000_space_to_end(ai_context->circ_buf,
+						 ME4000_AI_BUFFER_COUNT);
+			ISR_PDEBUG("me4000_ai_isr():Space to end = %d\n", c1);
+			if (c1 > c)
+				c1 = c;
+
+			if (c1 <= 0) {
+				ISR_PDEBUG
+				    ("me4000_ai_isr():Work done or buffer full\n");
+				break;
+			}
+
+			insw(ai_context->data_reg,
+			     ai_context->circ_buf.buf +
+			     ai_context->circ_buf.head, c1);
+			ai_context->circ_buf.head =
+			    (ai_context->circ_buf.head +
+			     c1) & (ME4000_AI_BUFFER_COUNT - 1);
+			c -= c1;
+		}
+
+		/* Work is done, so reset the interrupt */
+		ISR_PDEBUG
+		    ("me4000_ai_isr():reset interrupt fifo half full interrupt\n");
+		spin_lock(&ai_context->int_lock);
+		tmp = me4000_inl(ai_context->ctrl_reg);
+		tmp |= ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
+		me4000_outl(tmp, ai_context->ctrl_reg);
+		tmp &= ~ME4000_AI_CTRL_BIT_HF_IRQ_RESET;
+		me4000_outl(tmp, ai_context->ctrl_reg);
+		spin_unlock(&ai_context->int_lock);
+	}
+
+	if (me4000_inl(ai_context->irq_status_reg) & ME4000_IRQ_STATUS_BIT_SC) {
+		ISR_PDEBUG
+		    ("me4000_ai_isr():Sample counter interrupt occured\n");
+
+		if (!ai_context->sample_counter_reload) {
+			ISR_PDEBUG
+			    ("me4000_ai_isr():Single data block available\n");
+
+			/* Poll data until fifo empty */
+			for (i = 0;
+			     (i < ME4000_AI_FIFO_COUNT / 2)
+			     && (inl(ai_context->ctrl_reg) &
+				 ME4000_AI_STATUS_BIT_EF_DATA); i++) {
+				if (me4000_space_to_end
+				    (ai_context->circ_buf,
+				     ME4000_AI_BUFFER_COUNT)) {
+					*(ai_context->circ_buf.buf +
+					  ai_context->circ_buf.head) =
+		 inw(ai_context->data_reg);
+					ai_context->circ_buf.head =
+					    (ai_context->circ_buf.head +
+					     1) & (ME4000_AI_BUFFER_COUNT - 1);
+				} else
+					break;
+			}
+			ISR_PDEBUG("me4000_ai_isr():%d values read\n", i);
+		} else {
+			if (ai_context->sample_counter <=
+			    ME4000_AI_FIFO_COUNT / 2) {
+				ISR_PDEBUG
+				    ("me4000_ai_isr():Interrupt from adjustable half full threshold\n");
+
+				/* Read status register to find out what happened */
+				tmp = me4000_inl(ai_context->ctrl_reg);
+
+				if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
+				    !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
+				    && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
+					ISR_PDEBUG
+					    ("me4000_ai_isr():Fifo full\n");
+					c = ME4000_AI_FIFO_COUNT;
+
+					/* FIFO overflow, so stop conversion */
+					spin_lock(&ai_context->int_lock);
+					tmp = me4000_inl(ai_context->ctrl_reg);
+					tmp |=
+					    ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+					outl(tmp, ai_context->ctrl_reg);
+					spin_unlock(&ai_context->int_lock);
+				} else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
+					   && !(tmp &
+						ME4000_AI_STATUS_BIT_HF_DATA)
+					   && (tmp &
+					       ME4000_AI_STATUS_BIT_EF_DATA)) {
+					ISR_PDEBUG
+					    ("me4000_ai_isr():Fifo half full\n");
+					c = ME4000_AI_FIFO_COUNT / 2;
+				} else {
+					c = ai_context->sample_counter;
+					ISR_PDEBUG
+					    ("me4000_ai_isr():Sample count values\n");
+				}
+
+				ISR_PDEBUG
+				    ("me4000_ai_isr():Try to read %d values\n",
+				     c);
+
+				while (1) {
+					c1 = me4000_space_to_end(ai_context->
+								 circ_buf,
+								 ME4000_AI_BUFFER_COUNT);
+					ISR_PDEBUG
+					    ("me4000_ai_isr():Space to end = %d\n",
+					     c1);
+					if (c1 > c)
+						c1 = c;
+
+					if (c1 <= 0) {
+						ISR_PDEBUG
+						    ("me4000_ai_isr():Work done or buffer full\n");
+						break;
+					}
+
+					insw(ai_context->data_reg,
+					     ai_context->circ_buf.buf +
+					     ai_context->circ_buf.head, c1);
+					ai_context->circ_buf.head =
+					    (ai_context->circ_buf.head +
+					     c1) & (ME4000_AI_BUFFER_COUNT - 1);
+					c -= c1;
+				}
+			} else {
+				ISR_PDEBUG
+				    ("me4000_ai_isr():Multiple data block available\n");
+
+				/* Read status register to find out what happened */
+				tmp = me4000_inl(ai_context->ctrl_reg);
+
+				if (!(tmp & ME4000_AI_STATUS_BIT_FF_DATA) &&
+				    !(tmp & ME4000_AI_STATUS_BIT_HF_DATA)
+				    && (tmp & ME4000_AI_STATUS_BIT_EF_DATA)) {
+					ISR_PDEBUG
+					    ("me4000_ai_isr():Fifo full\n");
+					c = ME4000_AI_FIFO_COUNT;
+
+					/* FIFO overflow, so stop conversion */
+					spin_lock(&ai_context->int_lock);
+					tmp = me4000_inl(ai_context->ctrl_reg);
+					tmp |=
+					    ME4000_AI_CTRL_BIT_IMMEDIATE_STOP;
+					outl(tmp, ai_context->ctrl_reg);
+					spin_unlock(&ai_context->int_lock);
+
+					while (1) {
+						c1 = me4000_space_to_end
+						    (ai_context->circ_buf,
+						     ME4000_AI_BUFFER_COUNT);
+						ISR_PDEBUG
+						    ("me4000_ai_isr():Space to end = %d\n",
+						     c1);
+						if (c1 > c)
+							c1 = c;
+
+						if (c1 <= 0) {
+							ISR_PDEBUG
+							    ("me4000_ai_isr():Work done or buffer full\n");
+							break;
+						}
+
+						insw(ai_context->data_reg,
+						     ai_context->circ_buf.buf +
+						     ai_context->circ_buf.head,
+						     c1);
+						ai_context->circ_buf.head =
+						    (ai_context->circ_buf.head +
+						     c1) &
+						    (ME4000_AI_BUFFER_COUNT -
+						     1);
+						c -= c1;
+					}
+				} else if ((tmp & ME4000_AI_STATUS_BIT_FF_DATA)
+					   && !(tmp &
+						ME4000_AI_STATUS_BIT_HF_DATA)
+					   && (tmp &
+					       ME4000_AI_STATUS_BIT_EF_DATA)) {
+					ISR_PDEBUG
+					    ("me4000_ai_isr():Fifo half full\n");
+					c = ME4000_AI_FIFO_COUNT / 2;
+
+					while (1) {
+						c1 = me4000_space_to_end
+						    (ai_context->circ_buf,
+						     ME4000_AI_BUFFER_COUNT);
+						ISR_PDEBUG
+						    ("me4000_ai_isr():Space to end = %d\n",
+						     c1);
+						if (c1 > c)
+							c1 = c;
+
+						if (c1 <= 0) {
+							ISR_PDEBUG
+							    ("me4000_ai_isr():Work done or buffer full\n");
+							break;
+						}
+
+						insw(ai_context->data_reg,
+						     ai_context->circ_buf.buf +
+						     ai_context->circ_buf.head,
+						     c1);
+						ai_context->circ_buf.head =
+						    (ai_context->circ_buf.head +
+						     c1) &
+						    (ME4000_AI_BUFFER_COUNT -
+						     1);
+						c -= c1;
+					}
+				} else {
+					/* Poll data until fifo empty */
+					for (i = 0;
+					     (i < ME4000_AI_FIFO_COUNT / 2)
+					     && (inl(ai_context->ctrl_reg) &
+						 ME4000_AI_STATUS_BIT_EF_DATA);
+					     i++) {
+						if (me4000_space_to_end
+						    (ai_context->circ_buf,
+						     ME4000_AI_BUFFER_COUNT)) {
+							*(ai_context->circ_buf.
+							  buf +
+							  ai_context->circ_buf.
+							  head) =
+				       inw(ai_context->data_reg);
+							ai_context->circ_buf.
+							    head =
+							    (ai_context->
+							     circ_buf.head +
+							     1) &
+							    (ME4000_AI_BUFFER_COUNT
+							     - 1);
+						} else
+							break;
+					}
+					ISR_PDEBUG
+					    ("me4000_ai_isr():%d values read\n",
+					     i);
+				}
+			}
+		}
+
+		/* Work is done, so reset the interrupt */
+		ISR_PDEBUG
+		    ("me4000_ai_isr():reset interrupt from sample counter\n");
+		spin_lock(&ai_context->int_lock);
+		tmp = me4000_inl(ai_context->ctrl_reg);
+		tmp |= ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
+		me4000_outl(tmp, ai_context->ctrl_reg);
+		tmp &= ~ME4000_AI_CTRL_BIT_SC_IRQ_RESET;
+		me4000_outl(tmp, ai_context->ctrl_reg);
+		spin_unlock(&ai_context->int_lock);
+	}
+
+	/* Values are now available, so wake up waiting process */
+	if (me4000_buf_count(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) {
+		ISR_PDEBUG("me4000_ai_isr():Wake up waiting process\n");
+		wake_up_interruptible(&(ai_context->wait_queue));
+	}
+
+	/* If there is no space left in the buffer, disable interrupts */
+	spin_lock(&ai_context->int_lock);
+	if (!me4000_buf_space(ai_context->circ_buf, ME4000_AI_BUFFER_COUNT)) {
+		ISR_PDEBUG
+		    ("me4000_ai_isr():Disable Interrupt because no space left in buffer\n");
+		tmp = me4000_inl(ai_context->ctrl_reg);
+		tmp &=
+		    ~(ME4000_AI_CTRL_BIT_SC_IRQ | ME4000_AI_CTRL_BIT_HF_IRQ |
+		      ME4000_AI_CTRL_BIT_LE_IRQ);
+		me4000_outl(tmp, ai_context->ctrl_reg);
+	}
+	spin_unlock(&ai_context->int_lock);
+
+#ifdef ME4000_ISR_DEBUG
+	rdtscl(after);
+	printk(KERN_ERR "ME4000:me4000_ai_isr():Time lapse = %lu\n",
+	       after - before);
+#endif
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t me4000_ext_int_isr(int irq, void *dev_id)
+{
+	me4000_ext_int_context_t *ext_int_context;
+	unsigned long tmp;
+
+	ISR_PDEBUG("me4000_ext_int_isr() is executed\n");
+
+	ext_int_context = dev_id;
+
+	/* Check if irq number is right */
+	if (irq != ext_int_context->irq) {
+		ISR_PDEBUG("me4000_ext_int_isr():incorrect interrupt num: %d\n",
+			   irq);
+		return IRQ_NONE;
+	}
+
+	if (me4000_inl(ext_int_context->irq_status_reg) &
+	    ME4000_IRQ_STATUS_BIT_EX) {
+		ISR_PDEBUG("me4000_ext_int_isr():External interrupt occured\n");
+		tmp = me4000_inl(ext_int_context->ctrl_reg);
+		tmp |= ME4000_AI_CTRL_BIT_EX_IRQ_RESET;
+		me4000_outl(tmp, ext_int_context->ctrl_reg);
+		tmp &= ~ME4000_AI_CTRL_BIT_EX_IRQ_RESET;
+		me4000_outl(tmp, ext_int_context->ctrl_reg);
+
+		ext_int_context->int_count++;
+
+		if (ext_int_context->fasync_ptr) {
+			ISR_PDEBUG
+			    ("me2600_ext_int_isr():Send signal to process\n");
+			kill_fasync(&ext_int_context->fasync_ptr, SIGIO,
+				    POLL_IN);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
+void __exit me4000_module_exit(void)
+{
+	struct list_head *board_p;
+	me4000_info_t *board_info;
+
+	CALL_PDEBUG("cleanup_module() is executed\n");
+
+	unregister_chrdev(me4000_ext_int_major_driver_no, ME4000_EXT_INT_NAME);
+
+	unregister_chrdev(me4000_cnt_major_driver_no, ME4000_CNT_NAME);
+
+	unregister_chrdev(me4000_dio_major_driver_no, ME4000_DIO_NAME);
+
+	unregister_chrdev(me4000_ai_major_driver_no, ME4000_AI_NAME);
+
+	unregister_chrdev(me4000_ao_major_driver_no, ME4000_AO_NAME);
+
+	remove_proc_entry("me4000", NULL);
+
+	pci_unregister_driver(&me4000_driver);
+
+	/* Reset the boards */
+	for (board_p = me4000_board_info_list.next;
+	     board_p != &me4000_board_info_list; board_p = board_p->next) {
+		board_info = list_entry(board_p, me4000_info_t, list);
+		me4000_reset_board(board_info);
+	}
+
+	clear_board_info_list();
+}
+
+module_exit(me4000_module_exit);
+
+static int me4000_read_procmem(char *buf, char **start, off_t offset, int count,
+			       int *eof, void *data)
+{
+	int len = 0;
+	int limit = count - 1000;
+	me4000_info_t *board_info;
+	struct list_head *ptr;
+
+	len += sprintf(buf + len, "\nME4000 DRIVER VERSION %X.%X.%X\n\n",
+		       (ME4000_DRIVER_VERSION & 0xFF0000) >> 16,
+		       (ME4000_DRIVER_VERSION & 0xFF00) >> 8,
+		       (ME4000_DRIVER_VERSION & 0xFF));
+
+	/* Search for the board context */
+	for (ptr = me4000_board_info_list.next;
+	     (ptr != &me4000_board_info_list) && (len < limit);
+	     ptr = ptr->next) {
+		board_info = list_entry(ptr, me4000_info_t, list);
+
+		len +=
+		    sprintf(buf + len, "Board number %d:\n",
+			    board_info->board_count);
+		len += sprintf(buf + len, "---------------\n");
+		len +=
+		    sprintf(buf + len, "PLX base register = 0x%lX\n",
+			    board_info->plx_regbase);
+		len +=
+		    sprintf(buf + len, "PLX base register size = 0x%lX\n",
+			    board_info->plx_regbase_size);
+		len +=
+		    sprintf(buf + len, "ME4000 base register = 0x%lX\n",
+			    board_info->me4000_regbase);
+		len +=
+		    sprintf(buf + len, "ME4000 base register size = 0x%lX\n",
+			    board_info->me4000_regbase_size);
+		len +=
+		    sprintf(buf + len, "Serial number = 0x%X\n",
+			    board_info->serial_no);
+		len +=
+		    sprintf(buf + len, "Hardware revision = 0x%X\n",
+			    board_info->hw_revision);
+		len +=
+		    sprintf(buf + len, "Vendor id = 0x%X\n",
+			    board_info->vendor_id);
+		len +=
+		    sprintf(buf + len, "Device id = 0x%X\n",
+			    board_info->device_id);
+		len +=
+		    sprintf(buf + len, "PCI bus number = %d\n",
+			    board_info->pci_bus_no);
+		len +=
+		    sprintf(buf + len, "PCI device number = %d\n",
+			    board_info->pci_dev_no);
+		len +=
+		    sprintf(buf + len, "PCI function number = %d\n",
+			    board_info->pci_func_no);
+		len += sprintf(buf + len, "IRQ = %u\n", board_info->irq);
+		len +=
+		    sprintf(buf + len,
+			    "Count of interrupts since module was loaded = %d\n",
+			    board_info->irq_count);
+
+		len +=
+		    sprintf(buf + len, "Count of analog outputs = %d\n",
+			    board_info->board_p->ao.count);
+		len +=
+		    sprintf(buf + len, "Count of analog output fifos = %d\n",
+			    board_info->board_p->ao.fifo_count);
+
+		len +=
+		    sprintf(buf + len, "Count of analog inputs = %d\n",
+			    board_info->board_p->ai.count);
+		len +=
+		    sprintf(buf + len,
+			    "Count of sample and hold devices for analog input = %d\n",
+			    board_info->board_p->ai.sh_count);
+		len +=
+		    sprintf(buf + len,
+			    "Analog external trigger available for analog input = %d\n",
+			    board_info->board_p->ai.ex_trig_analog);
+
+		len +=
+		    sprintf(buf + len, "Count of digital ports = %d\n",
+			    board_info->board_p->dio.count);
+
+		len +=
+		    sprintf(buf + len, "Count of counter devices = %d\n",
+			    board_info->board_p->cnt.count);
+		len +=
+		    sprintf(buf + len, "AI control register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AI_CTRL_REG));
+
+		len += sprintf(buf + len, "AO 0 control register = 0x%08X\n",
+			       inl(board_info->me4000_regbase +
+				   ME4000_AO_00_CTRL_REG));
+		len +=
+		    sprintf(buf + len, "AO 0 status register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_00_STATUS_REG));
+		len +=
+		    sprintf(buf + len, "AO 1 control register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_01_CTRL_REG));
+		len +=
+		    sprintf(buf + len, "AO 1 status register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_01_STATUS_REG));
+		len +=
+		    sprintf(buf + len, "AO 2 control register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_02_CTRL_REG));
+		len +=
+		    sprintf(buf + len, "AO 2 status register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_02_STATUS_REG));
+		len +=
+		    sprintf(buf + len, "AO 3 control register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_03_CTRL_REG));
+		len +=
+		    sprintf(buf + len, "AO 3 status register = 0x%08X\n",
+			    inl(board_info->me4000_regbase +
+				ME4000_AO_03_STATUS_REG));
+	}
+
+	*eof = 1;
+	return len;
+}
diff --git a/drivers/staging/me4000/me4000.h b/drivers/staging/me4000/me4000.h
new file mode 100644
index 0000000..c35e4b9
--- /dev/null
+++ b/drivers/staging/me4000/me4000.h
@@ -0,0 +1,954 @@
+/*
+ * Copyright (C) 2003 Meilhaus Electronic GmbH (support@meilhaus.de)
+ *
+ * Source File : me4000.h
+ * Author      : GG (Guenter Gebhardt)  <g.gebhardt@meilhaus.de>
+ */
+
+#ifndef _ME4000_H_
+#define _ME4000_H_
+
+#ifdef __KERNEL__
+
+/*=============================================================================
+  The version of the driver release
+  ===========================================================================*/
+
+#define ME4000_DRIVER_VERSION  0x10009	// Version 1.00.09
+
+/*=============================================================================
+  Debug section
+  ===========================================================================*/
+
+#undef ME4000_CALL_DEBUG	// Debug function entry and exit
+#undef ME4000_ISR_DEBUG		// Debug the interrupt service routine
+#undef ME4000_PORT_DEBUG	// Debug port access
+#undef ME4000_DEBUG		// General purpose debug masseges
+
+#ifdef ME4000_CALL_DEBUG
+#undef CALL_PDEBUG
+#define CALL_PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args)
+#else
+# define CALL_PDEBUG(fmt, args...)	// no debugging, do nothing
+#endif
+
+#ifdef ME4000_ISR_DEBUG
+#undef ISR_PDEBUG
+#define ISR_PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args)
+#else
+#define ISR_PDEBUG(fmt, args...)	// no debugging, do nothing
+#endif
+
+#ifdef ME4000_PORT_DEBUG
+#undef PORT_PDEBUG
+#define PORT_PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args)
+#else
+#define PORT_PDEBUG(fmt, args...)	// no debugging, do nothing
+#endif
+
+#ifdef ME4000_DEBUG
+#undef PDEBUG
+#define PDEBUG(fmt, args...) printk(KERN_DEBUG"ME4000:" fmt, ##args)
+#else
+#define PDEBUG(fmt, args...)	// no debugging, do nothing
+#endif
+
+/*=============================================================================
+  PCI vendor and device IDs
+  ===========================================================================*/
+
+#define PCI_VENDOR_ID_MEILHAUS 0x1402
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4650	0x4650	// Low Cost version
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4660	0x4660	// Standard version
+#define PCI_DEVICE_ID_MEILHAUS_ME4660I	0x4661	// Isolated version
+#define PCI_DEVICE_ID_MEILHAUS_ME4660S	0x4662	// Standard version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4660IS	0x4663	// Isolated version with Sample and Hold
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4670	0x4670	// Standard version
+#define PCI_DEVICE_ID_MEILHAUS_ME4670I	0x4671	// Isolated version
+#define PCI_DEVICE_ID_MEILHAUS_ME4670S	0x4672	// Standard version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4670IS	0x4673	// Isolated version with Sample and Hold
+
+#define PCI_DEVICE_ID_MEILHAUS_ME4680	0x4680	// Standard version
+#define PCI_DEVICE_ID_MEILHAUS_ME4680I	0x4681	// Isolated version
+#define PCI_DEVICE_ID_MEILHAUS_ME4680S	0x4682	// Standard version with Sample and Hold
+#define PCI_DEVICE_ID_MEILHAUS_ME4680IS	0x4683	// Isolated version with Sample and Hold
+
+/*=============================================================================
+  Device names, for entries in /proc/..
+  ===========================================================================*/
+
+#define ME4000_NAME		"me4000"
+#define ME4000_AO_NAME		"me4000_ao"
+#define ME4000_AI_NAME		"me4000_ai"
+#define ME4000_DIO_NAME		"me4000_dio"
+#define ME4000_CNT_NAME		"me4000_cnt"
+#define ME4000_EXT_INT_NAME	"me4000_ext_int"
+
+/*=============================================================================
+  ME-4000 base register offsets
+  ===========================================================================*/
+
+#define ME4000_AO_00_CTRL_REG			0x00	// R/W
+#define ME4000_AO_00_STATUS_REG			0x04	// R/_
+#define ME4000_AO_00_FIFO_REG			0x08	// _/W
+#define ME4000_AO_00_SINGLE_REG			0x0C	// R/W
+#define ME4000_AO_00_TIMER_REG			0x10	// _/W
+
+#define ME4000_AO_01_CTRL_REG			0x18	// R/W
+#define ME4000_AO_01_STATUS_REG			0x1C	// R/_
+#define ME4000_AO_01_FIFO_REG			0x20	// _/W
+#define ME4000_AO_01_SINGLE_REG			0x24	// R/W
+#define ME4000_AO_01_TIMER_REG			0x28	// _/W
+
+#define ME4000_AO_02_CTRL_REG			0x30	// R/W
+#define ME4000_AO_02_STATUS_REG			0x34	// R/_
+#define ME4000_AO_02_FIFO_REG			0x38	// _/W
+#define ME4000_AO_02_SINGLE_REG			0x3C	// R/W
+#define ME4000_AO_02_TIMER_REG			0x40	// _/W
+
+#define ME4000_AO_03_CTRL_REG			0x48	// R/W
+#define ME4000_AO_03_STATUS_REG			0x4C	// R/_
+#define ME4000_AO_03_FIFO_REG			0x50	// _/W
+#define ME4000_AO_03_SINGLE_REG			0x54	// R/W
+#define ME4000_AO_03_TIMER_REG			0x58	// _/W
+
+#define ME4000_AI_CTRL_REG			0x74	// _/W
+#define ME4000_AI_STATUS_REG			0x74	// R/_
+#define ME4000_AI_CHANNEL_LIST_REG		0x78	// _/W
+#define ME4000_AI_DATA_REG			0x7C	// R/_
+#define ME4000_AI_CHAN_TIMER_REG		0x80	// _/W
+#define ME4000_AI_CHAN_PRE_TIMER_REG		0x84	// _/W
+#define ME4000_AI_SCAN_TIMER_LOW_REG		0x88	// _/W
+#define ME4000_AI_SCAN_TIMER_HIGH_REG		0x8C	// _/W
+#define ME4000_AI_SCAN_PRE_TIMER_LOW_REG	0x90	// _/W
+#define ME4000_AI_SCAN_PRE_TIMER_HIGH_REG	0x94	// _/W
+#define ME4000_AI_START_REG			0x98	// R/_
+
+#define ME4000_IRQ_STATUS_REG			0x9C	// R/_
+
+#define ME4000_DIO_PORT_0_REG			0xA0	// R/W
+#define ME4000_DIO_PORT_1_REG			0xA4	// R/W
+#define ME4000_DIO_PORT_2_REG			0xA8	// R/W
+#define ME4000_DIO_PORT_3_REG			0xAC	// R/W
+#define ME4000_DIO_DIR_REG			0xB0	// R/W
+
+#define ME4000_AO_LOADSETREG_XX			0xB4	// R/W
+
+#define ME4000_DIO_CTRL_REG			0xB8	// R/W
+
+#define ME4000_AO_DEMUX_ADJUST_REG		0xBC	// -/W
+
+#define ME4000_AI_SAMPLE_COUNTER_REG		0xC0	// _/W
+
+/*=============================================================================
+  Value to adjust Demux
+  ===========================================================================*/
+
+#define ME4000_AO_DEMUX_ADJUST_VALUE            0x4C
+
+/*=============================================================================
+  Counter base register offsets
+  ===========================================================================*/
+
+#define ME4000_CNT_COUNTER_0_REG		0x00
+#define ME4000_CNT_COUNTER_1_REG		0x01
+#define ME4000_CNT_COUNTER_2_REG		0x02
+#define ME4000_CNT_CTRL_REG			0x03
+
+/*=============================================================================
+  PLX base register offsets
+  ===========================================================================*/
+
+#define PLX_INTCSR	0x4C	// Interrupt control and status register
+#define PLX_ICR		0x50	// Initialization control register
+
+/*=============================================================================
+  Bits for the PLX_ICSR register
+  ===========================================================================*/
+
+#define PLX_INTCSR_LOCAL_INT1_EN             0x01	// If set, local interrupt 1 is enabled (r/w)
+#define PLX_INTCSR_LOCAL_INT1_POL            0x02	// If set, local interrupt 1 polarity is active high (r/w)
+#define PLX_INTCSR_LOCAL_INT1_STATE          0x04	// If set, local interrupt 1 is active (r/_)
+#define PLX_INTCSR_LOCAL_INT2_EN             0x08	// If set, local interrupt 2 is enabled (r/w)
+#define PLX_INTCSR_LOCAL_INT2_POL            0x10	// If set, local interrupt 2 polarity is active high (r/w)
+#define PLX_INTCSR_LOCAL_INT2_STATE          0x20	// If set, local interrupt 2 is active  (r/_)
+#define PLX_INTCSR_PCI_INT_EN                0x40	// If set, PCI interrupt is enabled (r/w)
+#define PLX_INTCSR_SOFT_INT                  0x80	// If set, a software interrupt is generated (r/w)
+
+/*=============================================================================
+  Bits for the PLX_ICR register
+  ===========================================================================*/
+
+#define PLX_ICR_BIT_EEPROM_CLOCK_SET		0x01000000
+#define PLX_ICR_BIT_EEPROM_CHIP_SELECT		0x02000000
+#define PLX_ICR_BIT_EEPROM_WRITE		0x04000000
+#define PLX_ICR_BIT_EEPROM_READ			0x08000000
+#define PLX_ICR_BIT_EEPROM_VALID		0x10000000
+
+#define PLX_ICR_MASK_EEPROM			0x1F000000
+
+#define EEPROM_DELAY				1
+
+/*=============================================================================
+  Bits for the ME4000_AO_CTRL_REG register
+  ===========================================================================*/
+
+#define ME4000_AO_CTRL_BIT_MODE_0		0x001
+#define ME4000_AO_CTRL_BIT_MODE_1		0x002
+#define ME4000_AO_CTRL_MASK_MODE		0x003
+#define ME4000_AO_CTRL_BIT_STOP			0x004
+#define ME4000_AO_CTRL_BIT_ENABLE_FIFO		0x008
+#define ME4000_AO_CTRL_BIT_ENABLE_EX_TRIG	0x010
+#define ME4000_AO_CTRL_BIT_EX_TRIG_EDGE		0x020
+#define ME4000_AO_CTRL_BIT_IMMEDIATE_STOP	0x080
+#define ME4000_AO_CTRL_BIT_ENABLE_DO		0x100
+#define ME4000_AO_CTRL_BIT_ENABLE_IRQ		0x200
+#define ME4000_AO_CTRL_BIT_RESET_IRQ		0x400
+#define ME4000_AO_CTRL_BIT_EX_TRIG_BOTH		0x800
+
+/*=============================================================================
+  Bits for the ME4000_AO_STATUS_REG register
+  ===========================================================================*/
+
+#define ME4000_AO_STATUS_BIT_FSM		0x01
+#define ME4000_AO_STATUS_BIT_FF			0x02
+#define ME4000_AO_STATUS_BIT_HF			0x04
+#define ME4000_AO_STATUS_BIT_EF			0x08
+
+/*=============================================================================
+  Bits for the ME4000_AI_CTRL_REG register
+  ===========================================================================*/
+
+#define ME4000_AI_CTRL_BIT_MODE_0		0x00000001
+#define ME4000_AI_CTRL_BIT_MODE_1		0x00000002
+#define ME4000_AI_CTRL_BIT_MODE_2		0x00000004
+#define ME4000_AI_CTRL_BIT_SAMPLE_HOLD		0x00000008
+#define ME4000_AI_CTRL_BIT_IMMEDIATE_STOP	0x00000010
+#define ME4000_AI_CTRL_BIT_STOP			0x00000020
+#define ME4000_AI_CTRL_BIT_CHANNEL_FIFO		0x00000040
+#define ME4000_AI_CTRL_BIT_DATA_FIFO		0x00000080
+#define ME4000_AI_CTRL_BIT_FULLSCALE		0x00000100
+#define ME4000_AI_CTRL_BIT_OFFSET		0x00000200
+#define ME4000_AI_CTRL_BIT_EX_TRIG_ANALOG	0x00000400
+#define ME4000_AI_CTRL_BIT_EX_TRIG		0x00000800
+#define ME4000_AI_CTRL_BIT_EX_TRIG_FALLING	0x00001000
+#define ME4000_AI_CTRL_BIT_EX_IRQ		0x00002000
+#define ME4000_AI_CTRL_BIT_EX_IRQ_RESET		0x00004000
+#define ME4000_AI_CTRL_BIT_LE_IRQ		0x00008000
+#define ME4000_AI_CTRL_BIT_LE_IRQ_RESET		0x00010000
+#define ME4000_AI_CTRL_BIT_HF_IRQ		0x00020000
+#define ME4000_AI_CTRL_BIT_HF_IRQ_RESET		0x00040000
+#define ME4000_AI_CTRL_BIT_SC_IRQ		0x00080000
+#define ME4000_AI_CTRL_BIT_SC_IRQ_RESET		0x00100000
+#define ME4000_AI_CTRL_BIT_SC_RELOAD		0x00200000
+#define ME4000_AI_CTRL_BIT_EX_TRIG_BOTH		0x80000000
+
+/*=============================================================================
+  Bits for the ME4000_AI_STATUS_REG register
+  ===========================================================================*/
+
+#define ME4000_AI_STATUS_BIT_EF_CHANNEL		0x00400000
+#define ME4000_AI_STATUS_BIT_HF_CHANNEL		0x00800000
+#define ME4000_AI_STATUS_BIT_FF_CHANNEL		0x01000000
+#define ME4000_AI_STATUS_BIT_EF_DATA		0x02000000
+#define ME4000_AI_STATUS_BIT_HF_DATA		0x04000000
+#define ME4000_AI_STATUS_BIT_FF_DATA		0x08000000
+#define ME4000_AI_STATUS_BIT_LE			0x10000000
+#define ME4000_AI_STATUS_BIT_FSM		0x20000000
+
+/*=============================================================================
+  Bits for the ME4000_IRQ_STATUS_REG register
+  ===========================================================================*/
+
+#define ME4000_IRQ_STATUS_BIT_EX		0x01
+#define ME4000_IRQ_STATUS_BIT_LE		0x02
+#define ME4000_IRQ_STATUS_BIT_AI_HF		0x04
+#define ME4000_IRQ_STATUS_BIT_AO_0_HF		0x08
+#define ME4000_IRQ_STATUS_BIT_AO_1_HF		0x10
+#define ME4000_IRQ_STATUS_BIT_AO_2_HF		0x20
+#define ME4000_IRQ_STATUS_BIT_AO_3_HF		0x40
+#define ME4000_IRQ_STATUS_BIT_SC		0x80
+
+/*=============================================================================
+  Bits for the ME4000_DIO_CTRL_REG register
+  ===========================================================================*/
+
+#define ME4000_DIO_CTRL_BIT_MODE_0		0X0001
+#define ME4000_DIO_CTRL_BIT_MODE_1		0X0002
+#define ME4000_DIO_CTRL_BIT_MODE_2		0X0004
+#define ME4000_DIO_CTRL_BIT_MODE_3		0X0008
+#define ME4000_DIO_CTRL_BIT_MODE_4		0X0010
+#define ME4000_DIO_CTRL_BIT_MODE_5		0X0020
+#define ME4000_DIO_CTRL_BIT_MODE_6		0X0040
+#define ME4000_DIO_CTRL_BIT_MODE_7		0X0080
+
+#define ME4000_DIO_CTRL_BIT_FUNCTION_0		0X0100
+#define ME4000_DIO_CTRL_BIT_FUNCTION_1		0X0200
+
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_0		0X0400
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_1		0X0800
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_2		0X1000
+#define ME4000_DIO_CTRL_BIT_FIFO_HIGH_3		0X2000
+
+/*=============================================================================
+  Bits for the ME4000_CNT_CTRL_REG register
+  ===========================================================================*/
+
+#define ME4000_CNT_CTRL_BIT_COUNTER_0  0x00
+#define ME4000_CNT_CTRL_BIT_COUNTER_1  0x40
+#define ME4000_CNT_CTRL_BIT_COUNTER_2  0x80
+
+#define ME4000_CNT_CTRL_BIT_MODE_0     0x00	// Change state if zero crossing
+#define ME4000_CNT_CTRL_BIT_MODE_1     0x02	// Retriggerable One-Shot
+#define ME4000_CNT_CTRL_BIT_MODE_2     0x04	// Asymmetrical divider
+#define ME4000_CNT_CTRL_BIT_MODE_3     0x06	// Symmetrical divider
+#define ME4000_CNT_CTRL_BIT_MODE_4     0x08	// Counter start by software trigger
+#define ME4000_CNT_CTRL_BIT_MODE_5     0x0A	// Counter start by hardware trigger
+
+/*=============================================================================
+  Extract information from minor device number
+  ===========================================================================*/
+
+#define AO_BOARD(dev) ((MINOR(dev) >> 6) & 0x3)
+#define AO_PORT(dev)  ((MINOR(dev) >> 2) & 0xF)
+#define AO_MODE(dev)  (MINOR(dev) & 0x3)
+
+#define AI_BOARD(dev) ((MINOR(dev) >> 3) & 0x1F)
+#define AI_MODE(dev)  (MINOR(dev) & 0x7)
+
+#define DIO_BOARD(dev) (MINOR(dev))
+
+#define CNT_BOARD(dev) (MINOR(dev))
+
+#define EXT_INT_BOARD(dev) (MINOR(dev))
+
+/*=============================================================================
+  Circular buffer used for analog input/output reads/writes.
+  ===========================================================================*/
+
+typedef struct me4000_circ_buf {
+	s16 *buf;
+	int volatile head;
+	int volatile tail;
+} me4000_circ_buf_t;
+
+/*=============================================================================
+  Information about the hardware capabilities
+  ===========================================================================*/
+
+typedef struct me4000_ao_info {
+	int count;
+	int fifo_count;
+} me4000_ao_info_t;
+
+typedef struct me4000_ai_info {
+	int count;
+	int sh_count;
+	int diff_count;
+	int ex_trig_analog;
+} me4000_ai_info_t;
+
+typedef struct me4000_dio_info {
+	int count;
+} me4000_dio_info_t;
+
+typedef struct me4000_cnt_info {
+	int count;
+} me4000_cnt_info_t;
+
+typedef struct me4000_board {
+	u16 vendor_id;
+	u16 device_id;
+	me4000_ao_info_t ao;
+	me4000_ai_info_t ai;
+	me4000_dio_info_t dio;
+	me4000_cnt_info_t cnt;
+} me4000_board_t;
+
+static me4000_board_t me4000_boards[] = {
+	{PCI_VENDOR_ID_MEILHAUS, 0x4610, {0, 0}, {16, 0, 0, 0}, {4}, {3}},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4650, {0, 0}, {16, 0, 0, 0}, {4}, {0}},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4660, {2, 0}, {16, 0, 0, 0}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4661, {2, 0}, {16, 0, 0, 0}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4662, {2, 0}, {16, 8, 0, 0}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4663, {2, 0}, {16, 8, 0, 0}, {4}, {3}},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4670, {4, 0}, {32, 0, 16, 1}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4671, {4, 0}, {32, 0, 16, 1}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4672, {4, 0}, {32, 8, 16, 1}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4673, {4, 0}, {32, 8, 16, 1}, {4}, {3}},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4680, {4, 4}, {32, 0, 16, 1}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4681, {4, 4}, {32, 0, 16, 1}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4682, {4, 4}, {32, 8, 16, 1}, {4}, {3}},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4683, {4, 4}, {32, 8, 16, 1}, {4}, {3}},
+
+	{0},
+};
+
+#define ME4000_BOARD_VERSIONS (sizeof(me4000_boards) / sizeof(me4000_board_t) - 1)
+
+/*=============================================================================
+  PCI device table.
+  This is used by modprobe to translate PCI IDs to drivers.
+  ===========================================================================*/
+
+static struct pci_device_id me4000_pci_table[] __devinitdata = {
+	{PCI_VENDOR_ID_MEILHAUS, 0x4610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4650, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4660, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4661, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4662, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4663, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+
+	{PCI_VENDOR_ID_MEILHAUS, 0x4680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4681, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4682, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{PCI_VENDOR_ID_MEILHAUS, 0x4683, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+
+	{0}
+};
+
+MODULE_DEVICE_TABLE(pci, me4000_pci_table);
+
+/*=============================================================================
+  Global board and subdevice information structures
+  ===========================================================================*/
+
+typedef struct me4000_info {
+	struct list_head list;	// List of all detected boards
+	int board_count;	// Index of the board after detection
+
+	unsigned long plx_regbase;	// PLX configuration space base address
+	unsigned long me4000_regbase;	// Base address of the ME4000
+	unsigned long timer_regbase;	// Base address of the timer circuit
+	unsigned long program_regbase;	// Base address to set the program pin for the xilinx
+
+	unsigned long plx_regbase_size;	// PLX register set space
+	unsigned long me4000_regbase_size;	// ME4000 register set space
+	unsigned long timer_regbase_size;	// Timer circuit register set space
+	unsigned long program_regbase_size;	// Size of program base address of the ME4000
+
+	unsigned int serial_no;	// Serial number of the board
+	unsigned char hw_revision;	// Hardware revision of the board
+	unsigned short vendor_id;	// Meilhaus vendor id (0x1402)
+	unsigned short device_id;	// Device ID
+
+	int pci_bus_no;		// PCI bus number
+	int pci_dev_no;		// PCI device number
+	int pci_func_no;	// PCI function number
+	struct pci_dev *pci_dev_p;	// General PCI information
+
+	me4000_board_t *board_p;	// Holds the board capabilities
+
+	unsigned int irq;	// IRQ assigned from the PCI BIOS
+	unsigned int irq_count;	// Count of external interrupts
+
+	spinlock_t preload_lock;	// Guards the analog output preload register
+	spinlock_t ai_ctrl_lock;	// Guards the analog input control register
+
+	struct list_head ao_context_list;	// List with analog output specific context
+	struct me4000_ai_context *ai_context;	// Analog input  specific context
+	struct me4000_dio_context *dio_context;	// Digital I/O specific context
+	struct me4000_cnt_context *cnt_context;	// Counter specific context
+	struct me4000_ext_int_context *ext_int_context;	// External interrupt specific context
+} me4000_info_t;
+
+typedef struct me4000_ao_context {
+	struct list_head list;	// linked list of me4000_ao_context_t
+	int index;		// Index in the list
+	int mode;		// Indicates mode (0 = single, 1 = wraparound, 2 = continous)
+	int dac_in_use;		// Indicates if already opend
+	spinlock_t use_lock;	// Guards in_use
+	spinlock_t int_lock;	// Used when locking out interrupts
+	me4000_circ_buf_t circ_buf;	// Circular buffer
+	wait_queue_head_t wait_queue;	// Wait queue to sleep while blocking write
+	me4000_info_t *board_info;
+	unsigned int irq;	// The irq associated with this ADC
+	int volatile pipe_flag;	// Indicates broken pipe set from me4000_ao_isr()
+	unsigned long ctrl_reg;
+	unsigned long status_reg;
+	unsigned long fifo_reg;
+	unsigned long single_reg;
+	unsigned long timer_reg;
+	unsigned long irq_status_reg;
+	unsigned long preload_reg;
+	struct fasync_struct *fasync_p;	// Queue for asynchronous notification
+} me4000_ao_context_t;
+
+typedef struct me4000_ai_context {
+	struct list_head list;	// linked list of me4000_ai_info_t
+	int mode;		// Indicates mode
+	int in_use;		// Indicates if already opend
+	spinlock_t use_lock;	// Guards in_use
+	spinlock_t int_lock;	// Used when locking out interrupts
+	int number;		// Number of the DAC
+	unsigned int irq;	// The irq associated with this ADC
+	me4000_circ_buf_t circ_buf;	// Circular buffer
+	wait_queue_head_t wait_queue;	// Wait queue to sleep while blocking read
+	me4000_info_t *board_info;
+
+	struct fasync_struct *fasync_p;	// Queue for asynchronous notification
+
+	unsigned long ctrl_reg;
+	unsigned long status_reg;
+	unsigned long channel_list_reg;
+	unsigned long data_reg;
+	unsigned long chan_timer_reg;
+	unsigned long chan_pre_timer_reg;
+	unsigned long scan_timer_low_reg;
+	unsigned long scan_timer_high_reg;
+	unsigned long scan_pre_timer_low_reg;
+	unsigned long scan_pre_timer_high_reg;
+	unsigned long start_reg;
+	unsigned long irq_status_reg;
+	unsigned long sample_counter_reg;
+
+	unsigned long chan_timer;
+	unsigned long chan_pre_timer;
+	unsigned long scan_timer_low;
+	unsigned long scan_timer_high;
+	unsigned long channel_list_count;
+	unsigned long sample_counter;
+	int sample_counter_reload;
+} me4000_ai_context_t;
+
+typedef struct me4000_dio_context {
+	struct list_head list;	// linked list of me4000_dio_context_t
+	int in_use;		// Indicates if already opend
+	spinlock_t use_lock;	// Guards in_use
+	int number;
+	int dio_count;
+	me4000_info_t *board_info;
+	unsigned long dir_reg;
+	unsigned long ctrl_reg;
+	unsigned long port_0_reg;
+	unsigned long port_1_reg;
+	unsigned long port_2_reg;
+	unsigned long port_3_reg;
+} me4000_dio_context_t;
+
+typedef struct me4000_cnt_context {
+	struct list_head list;	// linked list of me4000_dio_context_t
+	int in_use;		// Indicates if already opend
+	spinlock_t use_lock;	// Guards in_use
+	int number;
+	int cnt_count;
+	me4000_info_t *board_info;
+	unsigned long ctrl_reg;
+	unsigned long counter_0_reg;
+	unsigned long counter_1_reg;
+	unsigned long counter_2_reg;
+} me4000_cnt_context_t;
+
+typedef struct me4000_ext_int_context {
+	struct list_head list;	// linked list of me4000_dio_context_t
+	int in_use;		// Indicates if already opend
+	spinlock_t use_lock;	// Guards in_use
+	int number;
+	me4000_info_t *board_info;
+	unsigned int irq;
+	unsigned long int_count;
+	struct fasync_struct *fasync_ptr;
+	unsigned long ctrl_reg;
+	unsigned long irq_status_reg;
+} me4000_ext_int_context_t;
+
+#endif
+
+/*=============================================================================
+  Application include section starts here
+  ===========================================================================*/
+
+/*-----------------------------------------------------------------------------
+  Defines for analog input
+  ----------------------------------------------------------------------------*/
+
+/* General stuff */
+#define ME4000_AI_FIFO_COUNT		2048
+
+#define ME4000_AI_MIN_TICKS		66
+#define ME4000_AI_MAX_SCAN_TICKS	0xFFFFFFFFFFLL
+
+#define ME4000_AI_BUFFER_SIZE 		(32 * 1024)	// Size in bytes
+
+#define ME4000_AI_BUFFER_COUNT		((ME4000_AI_BUFFER_SIZE) / 2)	// Size in values
+
+/* Channel list defines and masks */
+#define ME4000_AI_CHANNEL_LIST_COUNT		1024
+
+#define ME4000_AI_LIST_INPUT_SINGLE_ENDED	0x000
+#define ME4000_AI_LIST_INPUT_DIFFERENTIAL	0x020
+
+#define ME4000_AI_LIST_RANGE_BIPOLAR_10		0x000
+#define ME4000_AI_LIST_RANGE_BIPOLAR_2_5	0x040
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_10	0x080
+#define ME4000_AI_LIST_RANGE_UNIPOLAR_2_5	0x0C0
+
+#define ME4000_AI_LIST_LAST_ENTRY		0x100
+
+/* External trigger defines */
+#define ME4000_AI_TRIGGER_SOFTWARE		0x0	// Use only with API
+#define ME4000_AI_TRIGGER_EXT_DIGITAL		0x1
+#define ME4000_AI_TRIGGER_EXT_ANALOG		0x2
+
+#define ME4000_AI_TRIGGER_EXT_EDGE_RISING	0x0
+#define ME4000_AI_TRIGGER_EXT_EDGE_FALLING	0x1
+#define ME4000_AI_TRIGGER_EXT_EDGE_BOTH		0x2
+
+/* Sample and Hold */
+#define ME4000_AI_SIMULTANEOUS_DISABLE		0x0
+#define ME4000_AI_SIMULTANEOUS_ENABLE		0x1
+
+/* Defines for the Sample Counter */
+#define ME4000_AI_SC_RELOAD			0x0
+#define ME4000_AI_SC_ONCE			0x1
+
+/* Modes for analog input */
+#define ME4000_AI_ACQ_MODE_SINGLE		0x00	// Catch one single value
+#define ME4000_AI_ACQ_MODE_SOFTWARE		0x01	// Continous sampling with software start
+#define ME4000_AI_ACQ_MODE_EXT			0x02	// Continous sampling with external trigger start
+#define ME4000_AI_ACQ_MODE_EXT_SINGLE_VALUE	0x03	// Sample one value by external trigger
+#define ME4000_AI_ACQ_MODE_EXT_SINGLE_CHANLIST	0x04	// Sample one channel list by external trigger
+
+/* Staus of AI FSM */
+#define ME4000_AI_STATUS_IDLE			0x0
+#define ME4000_AI_STATUS_BUSY			0x1
+
+/* Voltages for calibration */
+#define ME4000_AI_GAIN_1_UNI_OFFSET		10.0E-3
+#define ME4000_AI_GAIN_1_UNI_FULLSCALE		9950.0E-3
+#define ME4000_AI_GAIN_1_BI_OFFSET		0.0
+#define ME4000_AI_GAIN_1_BI_FULLSCALE		9950.0E-3
+#define ME4000_AI_GAIN_4_UNI_OFFSET		10.0E-3
+#define ME4000_AI_GAIN_4_UNI_FULLSCALE		2450.0E-3
+#define ME4000_AI_GAIN_4_BI_OFFSET		0.0
+#define ME4000_AI_GAIN_4_BI_FULLSCALE		2450.0E-3
+
+/* Ideal digits for calibration */
+#define ME4000_AI_GAIN_1_UNI_OFFSET_DIGITS	(-32702)
+#define ME4000_AI_GAIN_1_UNI_FULLSCALE_DIGITS	32440
+#define ME4000_AI_GAIN_1_BI_OFFSET_DIGITS	0
+#define ME4000_AI_GAIN_1_BI_FULLSCALE_DIGITS	32604
+#define ME4000_AI_GAIN_4_UNI_OFFSET_DIGITS	(-32505)
+#define ME4000_AI_GAIN_4_UNI_FULLSCALE_DIGITS	31457
+#define ME4000_AI_GAIN_4_BI_OFFSET_DIGITS	0
+#define ME4000_AI_GAIN_4_BI_FULLSCALE_DIGITS	32113
+
+/*-----------------------------------------------------------------------------
+  Defines for analog output
+  ----------------------------------------------------------------------------*/
+
+/* General stuff */
+#define ME4000_AO_FIFO_COUNT			(4 * 1024)
+
+#define ME4000_AO_MIN_TICKS			66
+
+#define ME4000_AO_BUFFER_SIZE 			(32 * 1024)	// Size in bytes
+
+#define ME4000_AO_BUFFER_COUNT 			((ME4000_AO_BUFFER_SIZE) / 2)	// Size in values
+
+/* Conversion modes for analog output */
+#define ME4000_AO_CONV_MODE_SINGLE		0x0
+#define ME4000_AO_CONV_MODE_WRAPAROUND		0x1
+#define ME4000_AO_CONV_MODE_CONTINUOUS		0x2
+
+/* Trigger setup */
+#define ME4000_AO_TRIGGER_EXT_EDGE_RISING	0x0
+#define ME4000_AO_TRIGGER_EXT_EDGE_FALLING	0x1
+#define ME4000_AO_TRIGGER_EXT_EDGE_BOTH		0x2
+
+/* Status of AO FSM */
+#define ME4000_AO_STATUS_IDLE			0x0
+#define ME4000_AO_STATUS_BUSY			0x1
+
+/*-----------------------------------------------------------------------------
+  Defines for eeprom
+  ----------------------------------------------------------------------------*/
+
+#define ME4000_EEPROM_CMD_READ			0x180
+#define ME4000_EEPROM_CMD_WRITE_ENABLE		0x130
+#define ME4000_EEPROM_CMD_WRITE_DISABLE		0x100
+#define ME4000_EEPROM_CMD_WRITE			0x1400000
+
+#define ME4000_EEPROM_CMD_LENGTH_READ		9
+#define ME4000_EEPROM_CMD_LENGTH_WRITE_ENABLE	9
+#define ME4000_EEPROM_CMD_LENGTH_WRITE_DISABLE	9
+#define ME4000_EEPROM_CMD_LENGTH_WRITE		25
+
+#define ME4000_EEPROM_ADR_DATE_HIGH		0x32
+#define ME4000_EEPROM_ADR_DATE_LOW		0x33
+
+#define ME4000_EEPROM_ADR_GAIN_1_UNI_OFFSET	0x34
+#define ME4000_EEPROM_ADR_GAIN_1_UNI_FULLSCALE	0x35
+#define ME4000_EEPROM_ADR_GAIN_1_BI_OFFSET	0x36
+#define ME4000_EEPROM_ADR_GAIN_1_BI_FULLSCALE	0x37
+#define ME4000_EEPROM_ADR_GAIN_1_DIFF_OFFSET	0x38
+#define ME4000_EEPROM_ADR_GAIN_1_DIFF_FULLSCALE	0x39
+
+#define ME4000_EEPROM_ADR_GAIN_4_UNI_OFFSET	0x3A
+#define ME4000_EEPROM_ADR_GAIN_4_UNI_FULLSCALE	0x3B
+#define ME4000_EEPROM_ADR_GAIN_4_BI_OFFSET	0x3C
+#define ME4000_EEPROM_ADR_GAIN_4_BI_FULLSCALE	0x3D
+#define ME4000_EEPROM_ADR_GAIN_4_DIFF_OFFSET	0x3E
+#define ME4000_EEPROM_ADR_GAIN_4_DIFF_FULLSCALE	0x3F
+
+#define ME4000_EEPROM_ADR_LENGTH		6
+#define ME4000_EEPROM_DATA_LENGTH		16
+
+/*-----------------------------------------------------------------------------
+  Defines for digital I/O
+  ----------------------------------------------------------------------------*/
+
+#define ME4000_DIO_PORT_A		0x0
+#define ME4000_DIO_PORT_B		0x1
+#define ME4000_DIO_PORT_C		0x2
+#define ME4000_DIO_PORT_D		0x3
+
+#define ME4000_DIO_PORT_INPUT		0x0
+#define ME4000_DIO_PORT_OUTPUT		0x1
+#define ME4000_DIO_FIFO_LOW		0x2
+#define ME4000_DIO_FIFO_HIGH		0x3
+
+#define ME4000_DIO_FUNCTION_PATTERN	0x0
+#define ME4000_DIO_FUNCTION_DEMUX	0x1
+#define ME4000_DIO_FUNCTION_MUX		0x2
+
+/*-----------------------------------------------------------------------------
+  Defines for counters
+  ----------------------------------------------------------------------------*/
+
+#define ME4000_CNT_COUNTER_0  0
+#define ME4000_CNT_COUNTER_1  1
+#define ME4000_CNT_COUNTER_2  2
+
+#define ME4000_CNT_MODE_0     0	// Change state if zero crossing
+#define ME4000_CNT_MODE_1     1	// Retriggerable One-Shot
+#define ME4000_CNT_MODE_2     2	// Asymmetrical divider
+#define ME4000_CNT_MODE_3     3	// Symmetrical divider
+#define ME4000_CNT_MODE_4     4	// Counter start by software trigger
+#define ME4000_CNT_MODE_5     5	// Counter start by hardware trigger
+
+/*-----------------------------------------------------------------------------
+  General type definitions
+  ----------------------------------------------------------------------------*/
+
+typedef struct me4000_user_info {
+	int board_count;	// Index of the board after detection
+	unsigned long plx_regbase;	// PLX configuration space base address
+	unsigned long me4000_regbase;	// Base address of the ME4000
+	unsigned long plx_regbase_size;	// PLX register set space
+	unsigned long me4000_regbase_size;	// ME4000 register set space
+	unsigned long serial_no;	// Serial number of the board
+	unsigned char hw_revision;	// Hardware revision of the board
+	unsigned short vendor_id;	// Meilhaus vendor id (0x1402)
+	unsigned short device_id;	// Device ID
+	int pci_bus_no;		// PCI bus number
+	int pci_dev_no;		// PCI device number
+	int pci_func_no;	// PCI function number
+	char irq;		// IRQ assigned from the PCI BIOS
+	int irq_count;		// Count of external interrupts
+
+	int driver_version;	// Version of the driver release
+
+	int ao_count;		// Count of analog output channels
+	int ao_fifo_count;	// Count fo analog output fifos
+
+	int ai_count;		// Count of analog input channels
+	int ai_sh_count;	// Count of sample and hold devices
+	int ai_ex_trig_analog;	// Flag to indicate if analogous external trigger is available
+
+	int dio_count;		// Count of digital I/O ports
+
+	int cnt_count;		// Count of counters
+} me4000_user_info_t;
+
+/*-----------------------------------------------------------------------------
+  Type definitions for analog output
+  ----------------------------------------------------------------------------*/
+
+typedef struct me4000_ao_channel_list {
+	unsigned long count;
+	unsigned long *list;
+} me4000_ao_channel_list_t;
+
+/*-----------------------------------------------------------------------------
+  Type definitions for analog input
+  ----------------------------------------------------------------------------*/
+
+typedef struct me4000_ai_channel_list {
+	unsigned long count;
+	unsigned long *list;
+} me4000_ai_channel_list_t;
+
+typedef struct me4000_ai_timer {
+	unsigned long pre_chan;
+	unsigned long chan;
+	unsigned long scan_low;
+	unsigned long scan_high;
+} me4000_ai_timer_t;
+
+typedef struct me4000_ai_config {
+	me4000_ai_timer_t timer;
+	me4000_ai_channel_list_t channel_list;
+	int sh;
+} me4000_ai_config_t;
+
+typedef struct me4000_ai_single {
+	int channel;
+	int range;
+	int mode;
+	short value;
+	unsigned long timeout;
+} me4000_ai_single_t;
+
+typedef struct me4000_ai_trigger {
+	int mode;
+	int edge;
+} me4000_ai_trigger_t;
+
+typedef struct me4000_ai_sc {
+	unsigned long value;
+	int reload;
+} me4000_ai_sc_t;
+
+/*-----------------------------------------------------------------------------
+  Type definitions for eeprom
+  ----------------------------------------------------------------------------*/
+
+typedef struct me4000_eeprom {
+	unsigned long date;
+	short uni_10_offset;
+	short uni_10_fullscale;
+	short uni_2_5_offset;
+	short uni_2_5_fullscale;
+	short bi_10_offset;
+	short bi_10_fullscale;
+	short bi_2_5_offset;
+	short bi_2_5_fullscale;
+	short diff_10_offset;
+	short diff_10_fullscale;
+	short diff_2_5_offset;
+	short diff_2_5_fullscale;
+} me4000_eeprom_t;
+
+/*-----------------------------------------------------------------------------
+  Type definitions for digital I/O
+  ----------------------------------------------------------------------------*/
+
+typedef struct me4000_dio_config {
+	int port;
+	int mode;
+	int function;
+} me4000_dio_config_t;
+
+typedef struct me4000_dio_byte {
+	int port;
+	unsigned char byte;
+} me4000_dio_byte_t;
+
+/*-----------------------------------------------------------------------------
+  Type definitions for counters
+  ----------------------------------------------------------------------------*/
+
+typedef struct me4000_cnt {
+	int counter;
+	unsigned short value;
+} me4000_cnt_t;
+
+typedef struct me4000_cnt_config {
+	int counter;
+	int mode;
+} me4000_cnt_config_t;
+
+/*-----------------------------------------------------------------------------
+  Type definitions for external interrupt
+  ----------------------------------------------------------------------------*/
+
+typedef struct {
+	int int1_count;
+	int int2_count;
+} me4000_int_type;
+
+/*-----------------------------------------------------------------------------
+  The ioctls of the board
+  ----------------------------------------------------------------------------*/
+
+#define ME4000_IOCTL_MAXNR 50
+#define ME4000_MAGIC 'y'
+#define ME4000_GET_USER_INFO          _IOR (ME4000_MAGIC, 0, me4000_user_info_t)
+
+#define ME4000_AO_START               _IOW (ME4000_MAGIC, 1, unsigned long)
+#define ME4000_AO_STOP                _IO  (ME4000_MAGIC, 2)
+#define ME4000_AO_IMMEDIATE_STOP      _IO  (ME4000_MAGIC, 3)
+#define ME4000_AO_RESET               _IO  (ME4000_MAGIC, 4)
+#define ME4000_AO_PRELOAD             _IO  (ME4000_MAGIC, 5)
+#define ME4000_AO_PRELOAD_UPDATE      _IO  (ME4000_MAGIC, 6)
+#define ME4000_AO_EX_TRIG_ENABLE      _IO  (ME4000_MAGIC, 7)
+#define ME4000_AO_EX_TRIG_DISABLE     _IO  (ME4000_MAGIC, 8)
+#define ME4000_AO_EX_TRIG_SETUP       _IOW (ME4000_MAGIC, 9, int)
+#define ME4000_AO_TIMER_SET_DIVISOR   _IOW (ME4000_MAGIC, 10, unsigned long)
+#define ME4000_AO_ENABLE_DO           _IO  (ME4000_MAGIC, 11)
+#define ME4000_AO_DISABLE_DO          _IO  (ME4000_MAGIC, 12)
+#define ME4000_AO_FSM_STATE           _IOR (ME4000_MAGIC, 13, int)
+
+#define ME4000_AI_SINGLE              _IOR (ME4000_MAGIC, 14, me4000_ai_single_t)
+#define ME4000_AI_START               _IOW (ME4000_MAGIC, 15, unsigned long)
+#define ME4000_AI_STOP                _IO  (ME4000_MAGIC, 16)
+#define ME4000_AI_IMMEDIATE_STOP      _IO  (ME4000_MAGIC, 17)
+#define ME4000_AI_EX_TRIG_ENABLE      _IO  (ME4000_MAGIC, 18)
+#define ME4000_AI_EX_TRIG_DISABLE     _IO  (ME4000_MAGIC, 19)
+#define ME4000_AI_EX_TRIG_SETUP       _IOW (ME4000_MAGIC, 20, me4000_ai_trigger_t)
+#define ME4000_AI_CONFIG              _IOW (ME4000_MAGIC, 21, me4000_ai_config_t)
+#define ME4000_AI_SC_SETUP            _IOW (ME4000_MAGIC, 22, me4000_ai_sc_t)
+#define ME4000_AI_FSM_STATE           _IOR (ME4000_MAGIC, 23, int)
+
+#define ME4000_DIO_CONFIG             _IOW (ME4000_MAGIC, 24, me4000_dio_config_t)
+#define ME4000_DIO_GET_BYTE           _IOR (ME4000_MAGIC, 25, me4000_dio_byte_t)
+#define ME4000_DIO_SET_BYTE           _IOW (ME4000_MAGIC, 26, me4000_dio_byte_t)
+#define ME4000_DIO_RESET              _IO  (ME4000_MAGIC, 27)
+
+#define ME4000_CNT_READ               _IOR (ME4000_MAGIC, 28, me4000_cnt_t)
+#define ME4000_CNT_WRITE              _IOW (ME4000_MAGIC, 29, me4000_cnt_t)
+#define ME4000_CNT_CONFIG             _IOW (ME4000_MAGIC, 30, me4000_cnt_config_t)
+#define ME4000_CNT_RESET              _IO  (ME4000_MAGIC, 31)
+
+#define ME4000_EXT_INT_DISABLE        _IO  (ME4000_MAGIC, 32)
+#define ME4000_EXT_INT_ENABLE         _IO  (ME4000_MAGIC, 33)
+#define ME4000_EXT_INT_COUNT          _IOR (ME4000_MAGIC, 34, int)
+
+#define ME4000_AI_OFFSET_ENABLE       _IO  (ME4000_MAGIC, 35)
+#define ME4000_AI_OFFSET_DISABLE      _IO  (ME4000_MAGIC, 36)
+#define ME4000_AI_FULLSCALE_ENABLE    _IO  (ME4000_MAGIC, 37)
+#define ME4000_AI_FULLSCALE_DISABLE   _IO  (ME4000_MAGIC, 38)
+
+#define ME4000_AI_EEPROM_READ         _IOR (ME4000_MAGIC, 39, me4000_eeprom_t)
+#define ME4000_AI_EEPROM_WRITE        _IOW (ME4000_MAGIC, 40, me4000_eeprom_t)
+
+#define ME4000_AO_SIMULTANEOUS_EX_TRIG _IO  (ME4000_MAGIC, 41)
+#define ME4000_AO_SIMULTANEOUS_SW      _IO  (ME4000_MAGIC, 42)
+#define ME4000_AO_SIMULTANEOUS_DISABLE _IO  (ME4000_MAGIC, 43)
+#define ME4000_AO_SIMULTANEOUS_UPDATE  _IOW (ME4000_MAGIC, 44, me4000_ao_channel_list_t)
+
+#define ME4000_AO_SYNCHRONOUS_EX_TRIG  _IO  (ME4000_MAGIC, 45)
+#define ME4000_AO_SYNCHRONOUS_SW       _IO  (ME4000_MAGIC, 46)
+#define ME4000_AO_SYNCHRONOUS_DISABLE  _IO  (ME4000_MAGIC, 47)
+
+#define ME4000_AO_EX_TRIG_TIMEOUT      _IOW (ME4000_MAGIC, 48, unsigned long)
+#define ME4000_AO_GET_FREE_BUFFER      _IOR (ME4000_MAGIC, 49, unsigned long)
+
+#define ME4000_AI_GET_COUNT_BUFFER     _IOR (ME4000_MAGIC, 50, unsigned long)
+
+#endif
-- 
1.6.0.2


^ permalink raw reply	[flat|nested] 37+ messages in thread

* [PATCH 10/23] Staging: add the go7007 video driver
  2008-10-10 22:41 [GIT PATCH] STAGING patches for 2.6.28 Greg KH
                   ` (5 preceding siblings ...)
  2008-10-10 22:42 ` [PATCH 09/23] Staging: add me4000 pci data collection driver Greg KH
@ 2008-10-10 22:42 ` Greg KH
  2008-10-10 22:42 ` [PATCH 11/23] Staging: USB/IP: add common functions needed Greg KH
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 37+ messages in thread
From: Greg KH @ 2008-10-10 22:42 UTC (permalink / raw)
  To: linux-kernel; +Cc: Greg Kroah-Hartman, Ross Cohen

From: Greg Kroah-Hartman <gregkh@suse.de>

Todo:
	- checkpatch.pl cleanups
	- sparse cleanups
	- lots of little modules, should be merged together
	  and added to the build.
	- testing?
	- handle churn in v4l layer.

Many thanks to Ross Cohen <rcohen@snurgle.org> for cleanup patches on
this driver.

Cc: Ross Cohen <rcohen@snurgle.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/staging/Kconfig                 |    2 +
 drivers/staging/Makefile                |    1 +
 drivers/staging/go7007/Kconfig          |   25 +
 drivers/staging/go7007/Makefile         |   18 +
 drivers/staging/go7007/README           |   11 +
 drivers/staging/go7007/go7007-driver.c  |  688 +++++++++++++
 drivers/staging/go7007/go7007-fw.c      | 1639 +++++++++++++++++++++++++++++++
 drivers/staging/go7007/go7007-i2c.c     |  309 ++++++
 drivers/staging/go7007/go7007-priv.h    |  279 ++++++
 drivers/staging/go7007/go7007-usb.c     | 1229 +++++++++++++++++++++++
 drivers/staging/go7007/go7007-v4l2.c    | 1503 ++++++++++++++++++++++++++++
 drivers/staging/go7007/go7007.h         |  114 +++
 drivers/staging/go7007/saa7134-go7007.c |  484 +++++++++
 drivers/staging/go7007/snd-go7007.c     |  305 ++++++
 drivers/staging/go7007/wis-i2c.h        |   55 +
 drivers/staging/go7007/wis-ov7640.c     |  131 +++
 drivers/staging/go7007/wis-saa7113.c    |  363 +++++++
 drivers/staging/go7007/wis-saa7115.c    |  492 +++++++++
 drivers/staging/go7007/wis-sony-tuner.c |  741 ++++++++++++++
 drivers/staging/go7007/wis-tw2804.c     |  381 +++++++
 drivers/staging/go7007/wis-tw9903.c     |  363 +++++++
 drivers/staging/go7007/wis-uda1342.c    |  136 +++
 22 files changed, 9269 insertions(+), 0 deletions(-)
 create mode 100644 drivers/staging/go7007/Kconfig
 create mode 100644 drivers/staging/go7007/Makefile
 create mode 100644 drivers/staging/go7007/README
 create mode 100644 drivers/staging/go7007/go7007-driver.c
 create mode 100644 drivers/staging/go7007/go7007-fw.c
 create mode 100644 drivers/staging/go7007/go7007-i2c.c
 create mode 100644 drivers/staging/go7007/go7007-priv.h
 create mode 100644 drivers/staging/go7007/go7007-usb.c
 create mode 100644 drivers/staging/go7007/go7007-v4l2.c
 create mode 100644 drivers/staging/go7007/go7007.h
 create mode 100644 drivers/staging/go7007/saa7134-go7007.c
 create mode 100644 drivers/staging/go7007/snd-go7007.c
 create mode 100644 drivers/staging/go7007/wis-i2c.h
 create mode 100644 drivers/staging/go7007/wis-ov7640.c
 create mode 100644 drivers/staging/go7007/wis-saa7113.c
 create mode 100644 drivers/staging/go7007/wis-saa7115.c
 create mode 100644 drivers/staging/go7007/wis-sony-tuner.c
 create mode 100644 drivers/staging/go7007/wis-tw2804.c
 create mode 100644 drivers/staging/go7007/wis-tw9903.c
 create mode 100644 drivers/staging/go7007/wis-uda1342.c

diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 56c73bc..f16bc9c 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -31,4 +31,6 @@ source "drivers/staging/sxg/Kconfig"
 
 source "drivers/staging/me4000/Kconfig"
 
+source "drivers/staging/go7007/Kconfig"
+
 endif # STAGING
diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
index 97df19b..aa61662 100644
--- a/drivers/staging/Makefile
+++ b/drivers/staging/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_ET131X)		+= et131x/
 obj-$(CONFIG_SLICOSS)		+= slicoss/
 obj-$(CONFIG_SXG)		+= sxg/
 obj-$(CONFIG_ME4000)		+= me4000/
+obj-$(CONFIG_VIDEO_GO7007)	+= go7007/
diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/go7007/Kconfig
new file mode 100644
index 0000000..57a121c
--- /dev/null
+++ b/drivers/staging/go7007/Kconfig
@@ -0,0 +1,25 @@
+config VIDEO_GO7007
+	tristate "Go 7007 support"
+	depends on VIDEO_DEV && PCI && I2C && INPUT
+	select VIDEOBUF_DMA_SG
+	select VIDEO_IR
+	select VIDEO_TUNER
+	select VIDEO_TVEEPROM
+	select CRC32
+	default N
+	---help---
+	  This is a video4linux driver for some wierd device...
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called go7007
+
+config VIDEO_GO7007_USB
+	tristate "Go 7007 USB support"
+	depends on VIDEO_GO7007 && USB
+	default N
+	---help---
+	  This is a video4linux driver for some wierd device...
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called go7007-usb
+
diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile
new file mode 100644
index 0000000..9b9310c
--- /dev/null
+++ b/drivers/staging/go7007/Makefile
@@ -0,0 +1,18 @@
+#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \
+		wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \
+		wis-tw2804.o
+
+
+obj-$(CONFIG_VIDEO_GO7007) += go7007.o
+obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o
+
+go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o snd-go7007.o
+
+
+#ifneq ($(SAA7134_BUILD),)
+#obj-m += saa7134-go7007.o
+#endif
+
+EXTRA_CFLAGS += -Idrivers/staging/saa7134
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
diff --git a/drivers/staging/go7007/README b/drivers/staging/go7007/README
new file mode 100644
index 0000000..48f4476
--- /dev/null
+++ b/drivers/staging/go7007/README
@@ -0,0 +1,11 @@
+Todo:
+	- checkpatch.pl cleanups
+	- sparse cleanups
+	- lots of little modules, should be merged together
+	  and added to the build.
+	- testing?
+	- handle churn in v4l layer.
+
+Please send patchs to Greg Kroah-Hartman <greg@kroah.com> and Cc: Ross
+Cohen <rcohen@snurgle.org> as well.
+
diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c
new file mode 100644
index 0000000..5a336ff
--- /dev/null
+++ b/drivers/staging/go7007/go7007-driver.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+#include <linux/videodev.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+/*
+ * Wait for an interrupt to be delivered from the GO7007SB and return
+ * the associated value and data.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data)
+{
+	go->interrupt_available = 0;
+	go->hpi_ops->read_interrupt(go);
+	if (wait_event_timeout(go->interrupt_waitq,
+				go->interrupt_available, 5*HZ) < 0) {
+		printk(KERN_ERR "go7007: timeout waiting for read interrupt\n");
+		return -1;
+	}
+	if (!go->interrupt_available)
+		return -1;
+	go->interrupt_available = 0;
+	*value = go->interrupt_value & 0xfffe;
+	*data = go->interrupt_data;
+	return 0;
+}
+EXPORT_SYMBOL(go7007_read_interrupt);
+
+/*
+ * Read a register/address on the GO7007SB.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data)
+{
+	int count = 100;
+	u16 value;
+
+	if (go7007_write_interrupt(go, 0x0010, addr) < 0)
+		return -EIO;
+	while (count-- > 0) {
+		if (go7007_read_interrupt(go, &value, data) == 0 &&
+				value == 0xa000)
+			return 0;
+	}
+	return -EIO;
+}
+EXPORT_SYMBOL(go7007_read_addr);
+
+/*
+ * Send the boot firmware to the encoder, which just wakes it up and lets
+ * us talk to the GPIO pins and on-board I2C adapter.
+ *
+ * Must be called with the hw_lock held.
+ */
+static int go7007_load_encoder(struct go7007 *go)
+{
+	const struct firmware *fw_entry;
+	char fw_name[] = "go7007fw.bin";
+	void *bounce;
+	int fw_len, rv = 0;
+	u16 intr_val, intr_data;
+
+	if (request_firmware(&fw_entry, fw_name, go->dev)) {
+		printk(KERN_ERR
+			"go7007: unable to load firmware from file \"%s\"\n",
+			fw_name);
+		return -1;
+	}
+	if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) {
+		printk(KERN_ERR "go7007: file \"%s\" does not appear to be "
+				"go7007 firmware\n", fw_name);
+		release_firmware(fw_entry);
+		return -1;
+	}
+	fw_len = fw_entry->size - 16;
+	bounce = kmalloc(fw_len, GFP_KERNEL);
+	if (bounce == NULL) {
+		printk(KERN_ERR "go7007: unable to allocate %d bytes for "
+				"firmware transfer\n", fw_len);
+		release_firmware(fw_entry);
+		return -1;
+	}
+	memcpy(bounce, fw_entry->data + 16, fw_len);
+	release_firmware(fw_entry);
+	if (go7007_interface_reset(go) < 0 ||
+			go7007_send_firmware(go, bounce, fw_len) < 0 ||
+			go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+			(intr_val & ~0x1) != 0x5a5a) {
+		printk(KERN_ERR "go7007: error transferring firmware\n");
+		rv = -1;
+	}
+	kfree(bounce);
+	return rv;
+}
+
+/*
+ * Boot the encoder and register the I2C adapter if requested.  Do the
+ * minimum initialization necessary, since the board-specific code may
+ * still need to probe the board ID.
+ *
+ * Must NOT be called with the hw_lock held.
+ */
+int go7007_boot_encoder(struct go7007 *go, int init_i2c)
+{
+	int ret;
+
+	down(&go->hw_lock);
+	ret = go7007_load_encoder(go);
+	up(&go->hw_lock);
+	if (ret < 0)
+		return -1;
+	if (!init_i2c)
+		return 0;
+	if (go7007_i2c_init(go) < 0)
+		return -1;
+	go->i2c_adapter_online = 1;
+	return 0;
+}
+EXPORT_SYMBOL(go7007_boot_encoder);
+
+/*
+ * Configure any hardware-related registers in the GO7007, such as GPIO
+ * pins and bus parameters, which are board-specific.  This assumes
+ * the boot firmware has already been downloaded.
+ *
+ * Must be called with the hw_lock held.
+ */
+static int go7007_init_encoder(struct go7007 *go)
+{
+	if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) {
+		go7007_write_addr(go, 0x1000, 0x0811);
+		go7007_write_addr(go, 0x1000, 0x0c11);
+	}
+	if (go->board_id == GO7007_BOARDID_MATRIX_REV) {
+		/* Set GPIO pin 0 to be an output (audio clock control) */
+		go7007_write_addr(go, 0x3c82, 0x0001);
+		go7007_write_addr(go, 0x3c80, 0x00fe);
+	}
+	return 0;
+}
+
+/*
+ * Send the boot firmware to the GO7007 and configure the registers.  This
+ * is the only way to stop the encoder once it has started streaming video.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_reset_encoder(struct go7007 *go)
+{
+	if (go7007_load_encoder(go) < 0)
+		return -1;
+	return go7007_init_encoder(go);
+}
+
+/*
+ * Attempt to instantiate an I2C client by ID, probably loading a module.
+ */
+static int init_i2c_module(struct i2c_adapter *adapter, int id, int addr)
+{
+	char *modname;
+
+	switch (id) {
+	case I2C_DRIVERID_WIS_SAA7115:
+		modname = "wis-saa7115";
+		break;
+	case I2C_DRIVERID_WIS_SAA7113:
+		modname = "wis-saa7113";
+		break;
+	case I2C_DRIVERID_WIS_UDA1342:
+		modname = "wis-uda1342";
+		break;
+	case I2C_DRIVERID_WIS_SONY_TUNER:
+		modname = "wis-sony-tuner";
+		break;
+	case I2C_DRIVERID_WIS_TW9903:
+		modname = "wis-tw9903";
+		break;
+	case I2C_DRIVERID_WIS_TW2804:
+		modname = "wis-tw2804";
+		break;
+	case I2C_DRIVERID_WIS_OV7640:
+		modname = "wis-ov7640";
+		break;
+	default:
+		modname = NULL;
+		break;
+	}
+	if (modname != NULL)
+		request_module(modname);
+	if (wis_i2c_probe_device(adapter, id, addr) == 1)
+		return 0;
+	if (modname != NULL)
+		printk(KERN_INFO
+			"go7007: probing for module %s failed", modname);
+	else
+		printk(KERN_INFO
+			"go7007: sensor %u seems to be unsupported!\n", id);
+	return -1;
+}
+
+/*
+ * Finalize the GO7007 hardware setup, register the on-board I2C adapter
+ * (if used on this board), load the I2C client driver for the sensor
+ * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2
+ * interfaces.
+ *
+ * Must NOT be called with the hw_lock held.
+ */
+int go7007_register_encoder(struct go7007 *go)
+{
+	int i, ret;
+
+	printk(KERN_INFO "go7007: registering new %s\n", go->name);
+
+	down(&go->hw_lock);
+	ret = go7007_init_encoder(go);
+	up(&go->hw_lock);
+	if (ret < 0)
+		return -1;
+
+	if (!go->i2c_adapter_online &&
+			go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) {
+		if (go7007_i2c_init(go) < 0)
+			return -1;
+		go->i2c_adapter_online = 1;
+	}
+	if (go->i2c_adapter_online) {
+		for (i = 0; i < go->board_info->num_i2c_devs; ++i)
+			init_i2c_module(&go->i2c_adapter,
+					go->board_info->i2c_devs[i].id,
+					go->board_info->i2c_devs[i].addr);
+#ifdef TUNER_SET_TYPE_ADDR
+		if (go->tuner_type >= 0) {
+			struct tuner_setup tun_setup = {
+				.mode_mask	= T_ANALOG_TV,
+				.addr		= ADDR_UNSET,
+				.type		= go->tuner_type
+			};
+			i2c_clients_command(&go->i2c_adapter,
+				TUNER_SET_TYPE_ADDR, &tun_setup);
+		}
+#else
+		if (go->tuner_type >= 0)
+			i2c_clients_command(&go->i2c_adapter,
+				TUNER_SET_TYPE, &go->tuner_type);
+#endif
+		if (go->board_id == GO7007_BOARDID_ADLINK_MPG24)
+			i2c_clients_command(&go->i2c_adapter,
+				DECODER_SET_CHANNEL, &go->channel_number);
+	}
+	if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) {
+		go->audio_enabled = 1;
+		go7007_snd_init(go);
+	}
+	return go7007_v4l2_init(go);
+}
+EXPORT_SYMBOL(go7007_register_encoder);
+
+/*
+ * Send the encode firmware to the encoder, which will cause it
+ * to immediately start delivering the video and audio streams.
+ *
+ * Must be called with the hw_lock held.
+ */
+int go7007_start_encoder(struct go7007 *go)
+{
+	u8 *fw;
+	int fw_len, rv = 0, i;
+	u16 intr_val, intr_data;
+
+	go->modet_enable = 0;
+	if (!go->dvd_mode)
+		for (i = 0; i < 4; ++i) {
+			if (go->modet[i].enable) {
+				go->modet_enable = 1;
+				continue;
+			}
+			go->modet[i].pixel_threshold = 32767;
+			go->modet[i].motion_threshold = 32767;
+			go->modet[i].mb_threshold = 32767;
+		}
+
+	if (go7007_construct_fw_image(go, &fw, &fw_len) < 0)
+		return -1;
+
+	if (go7007_send_firmware(go, fw, fw_len) < 0 ||
+			go7007_read_interrupt(go, &intr_val, &intr_data) < 0) {
+		printk(KERN_ERR "go7007: error transferring firmware\n");
+		rv = -1;
+		goto start_error;
+	}
+
+	go->state = STATE_DATA;
+	go->parse_length = 0;
+	go->seen_frame = 0;
+	if (go7007_stream_start(go) < 0) {
+		printk(KERN_ERR "go7007: error starting stream transfer\n");
+		rv = -1;
+		goto start_error;
+	}
+
+start_error:
+	kfree(fw);
+	return rv;
+}
+
+/*
+ * Store a byte in the current video buffer, if there is one.
+ */
+static inline void store_byte(struct go7007_buffer *gobuf, u8 byte)
+{
+	if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) {
+		unsigned int pgidx = gobuf->offset >> PAGE_SHIFT;
+		unsigned int pgoff = gobuf->offset & ~PAGE_MASK;
+
+		*((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte;
+		++gobuf->offset;
+		++gobuf->bytesused;
+	}
+}
+
+/*
+ * Deliver the last video buffer and get a new one to start writing to.
+ */
+static void frame_boundary(struct go7007 *go)
+{
+	struct go7007_buffer *gobuf;
+	int i;
+
+	if (go->active_buf) {
+		if (go->active_buf->modet_active) {
+			if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) {
+				for (i = 0; i < 216; ++i)
+					store_byte(go->active_buf,
+							go->active_map[i]);
+				go->active_buf->bytesused -= 216;
+			} else
+				go->active_buf->modet_active = 0;
+		}
+		go->active_buf->state = BUF_STATE_DONE;
+		wake_up_interruptible(&go->frame_waitq);
+		go->active_buf = NULL;
+	}
+	list_for_each_entry(gobuf, &go->stream, stream)
+		if (gobuf->state == BUF_STATE_QUEUED) {
+			gobuf->seq = go->next_seq;
+			do_gettimeofday(&gobuf->timestamp);
+			go->active_buf = gobuf;
+			break;
+		}
+	++go->next_seq;
+}
+
+static void write_bitmap_word(struct go7007 *go)
+{
+	int x, y, i, stride = ((go->width >> 4) + 7) >> 3;
+
+	for (i = 0; i < 16; ++i) {
+		y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4);
+		x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4);
+		go->active_map[stride * y + (x >> 3)] |=
+					(go->modet_word & 1) << (x & 0x7);
+		go->modet_word >>= 1;
+	}
+}
+
+/*
+ * Parse a chunk of the video stream into frames.  The frames are not
+ * delimited by the hardware, so we have to parse the frame boundaries
+ * based on the type of video stream we're receiving.
+ */
+void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length)
+{
+	int i, seq_start_code = -1, frame_start_code = -1;
+
+	spin_lock(&go->spinlock);
+
+	switch (go->format) {
+	case GO7007_FORMAT_MPEG4:
+		seq_start_code = 0xB0;
+		frame_start_code = 0xB6;
+		break;
+	case GO7007_FORMAT_MPEG1:
+	case GO7007_FORMAT_MPEG2:
+		seq_start_code = 0xB3;
+		frame_start_code = 0x00;
+		break;
+	}
+
+	for (i = 0; i < length; ++i) {
+		if (go->active_buf != NULL &&
+			    go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) {
+			printk(KERN_DEBUG "go7007: dropping oversized frame\n");
+			go->active_buf->offset -= go->active_buf->bytesused;
+			go->active_buf->bytesused = 0;
+			go->active_buf->modet_active = 0;
+			go->active_buf = NULL;
+		}
+
+		switch (go->state) {
+		case STATE_DATA:
+			switch (buf[i]) {
+			case 0x00:
+				go->state = STATE_00;
+				break;
+			case 0xFF:
+				go->state = STATE_FF;
+				break;
+			default:
+				store_byte(go->active_buf, buf[i]);
+				break;
+			}
+			break;
+		case STATE_00:
+			switch (buf[i]) {
+			case 0x00:
+				go->state = STATE_00_00;
+				break;
+			case 0xFF:
+				store_byte(go->active_buf, 0x00);
+				go->state = STATE_FF;
+				break;
+			default:
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, buf[i]);
+				go->state = STATE_DATA;
+				break;
+			}
+			break;
+		case STATE_00_00:
+			switch (buf[i]) {
+			case 0x00:
+				store_byte(go->active_buf, 0x00);
+				/* go->state remains STATE_00_00 */
+				break;
+			case 0x01:
+				go->state = STATE_00_00_01;
+				break;
+			case 0xFF:
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x00);
+				go->state = STATE_FF;
+				break;
+			default:
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, buf[i]);
+				go->state = STATE_DATA;
+				break;
+			}
+			break;
+		case STATE_00_00_01:
+			/* If this is the start of a new MPEG frame,
+			 * get a new buffer */
+			if ((go->format == GO7007_FORMAT_MPEG1 ||
+					go->format == GO7007_FORMAT_MPEG2 ||
+					go->format == GO7007_FORMAT_MPEG4) &&
+					(buf[i] == seq_start_code ||
+						buf[i] == 0xB8 || /* GOP code */
+						buf[i] == frame_start_code)) {
+				if (go->active_buf == NULL || go->seen_frame)
+					frame_boundary(go);
+				if (buf[i] == frame_start_code) {
+					if (go->active_buf != NULL)
+						go->active_buf->frame_offset =
+							go->active_buf->offset;
+					go->seen_frame = 1;
+				} else {
+					go->seen_frame = 0;
+				}
+			}
+			/* Handle any special chunk types, or just write the
+			 * start code to the (potentially new) buffer */
+			switch (buf[i]) {
+			case 0xF5: /* timestamp */
+				go->parse_length = 12;
+				go->state = STATE_UNPARSED;
+				break;
+			case 0xF6: /* vbi */
+				go->state = STATE_VBI_LEN_A;
+				break;
+			case 0xF8: /* MD map */
+				go->parse_length = 0;
+				memset(go->active_map, 0,
+						sizeof(go->active_map));
+				go->state = STATE_MODET_MAP;
+				break;
+			case 0xFF: /* Potential JPEG start code */
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x01);
+				go->state = STATE_FF;
+				break;
+			default:
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x00);
+				store_byte(go->active_buf, 0x01);
+				store_byte(go->active_buf, buf[i]);
+				go->state = STATE_DATA;
+				break;
+			}
+			break;
+		case STATE_FF:
+			switch (buf[i]) {
+			case 0x00:
+				store_byte(go->active_buf, 0xFF);
+				go->state = STATE_00;
+				break;
+			case 0xFF:
+				store_byte(go->active_buf, 0xFF);
+				/* go->state remains STATE_FF */
+				break;
+			case 0xD8:
+				if (go->format == GO7007_FORMAT_MJPEG)
+					frame_boundary(go);
+				/* fall through */
+			default:
+				store_byte(go->active_buf, 0xFF);
+				store_byte(go->active_buf, buf[i]);
+				go->state = STATE_DATA;
+				break;
+			}
+			break;
+		case STATE_VBI_LEN_A:
+			go->parse_length = buf[i] << 8;
+			go->state = STATE_VBI_LEN_B;
+			break;
+		case STATE_VBI_LEN_B:
+			go->parse_length |= buf[i];
+			if (go->parse_length > 0)
+				go->state = STATE_UNPARSED;
+			else
+				go->state = STATE_DATA;
+			break;
+		case STATE_MODET_MAP:
+			if (go->parse_length < 204) {
+				if (go->parse_length & 1) {
+					go->modet_word |= buf[i];
+					write_bitmap_word(go);
+				} else
+					go->modet_word = buf[i] << 8;
+			} else if (go->parse_length == 207 && go->active_buf) {
+				go->active_buf->modet_active = buf[i];
+			}
+			if (++go->parse_length == 208)
+				go->state = STATE_DATA;
+			break;
+		case STATE_UNPARSED:
+			if (--go->parse_length == 0)
+				go->state = STATE_DATA;
+			break;
+		}
+	}
+
+	spin_unlock(&go->spinlock);
+}
+EXPORT_SYMBOL(go7007_parse_video_stream);
+
+/*
+ * Allocate a new go7007 struct.  Used by the hardware-specific probe.
+ */
+struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev)
+{
+	struct go7007 *go;
+	int i;
+
+	go = kmalloc(sizeof(struct go7007), GFP_KERNEL);
+	if (go == NULL)
+		return NULL;
+	go->dev = dev;
+	go->board_info = board;
+	go->board_id = 0;
+	go->tuner_type = -1;
+	go->channel_number = 0;
+	go->name[0] = 0;
+	init_MUTEX(&go->hw_lock);
+	init_waitqueue_head(&go->frame_waitq);
+	spin_lock_init(&go->spinlock);
+	go->video_dev = NULL;
+	go->ref_count = 0;
+	go->status = STATUS_INIT;
+	memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter));
+	go->i2c_adapter_online = 0;
+	go->interrupt_available = 0;
+	init_waitqueue_head(&go->interrupt_waitq);
+	go->in_use = 0;
+	go->input = 0;
+	if (board->sensor_flags & GO7007_SENSOR_TV) {
+		go->standard = GO7007_STD_NTSC;
+		go->width = 720;
+		go->height = 480;
+		go->sensor_framerate = 30000;
+	} else {
+		go->standard = GO7007_STD_OTHER;
+		go->width = board->sensor_width;
+		go->height = board->sensor_height;
+		go->sensor_framerate = board->sensor_framerate;
+	}
+	go->encoder_v_offset = board->sensor_v_offset;
+	go->encoder_h_offset = board->sensor_h_offset;
+	go->encoder_h_halve = 0;
+	go->encoder_v_halve = 0;
+	go->encoder_subsample = 0;
+	go->streaming = 0;
+	go->format = GO7007_FORMAT_MJPEG;
+	go->bitrate = 1500000;
+	go->fps_scale = 1;
+	go->pali = 0;
+	go->aspect_ratio = GO7007_RATIO_1_1;
+	go->gop_size = 0;
+	go->ipb = 0;
+	go->closed_gop = 0;
+	go->repeat_seqhead = 0;
+	go->seq_header_enable = 0;
+	go->gop_header_enable = 0;
+	go->dvd_mode = 0;
+	go->interlace_coding = 0;
+	for (i = 0; i < 4; ++i)
+		go->modet[i].enable = 0;;
+	for (i = 0; i < 1624; ++i)
+		go->modet_map[i] = 0;
+	go->audio_deliver = NULL;
+	go->audio_enabled = 0;
+	INIT_LIST_HEAD(&go->stream);
+
+	return go;
+}
+EXPORT_SYMBOL(go7007_alloc);
+
+/*
+ * Detach and unregister the encoder.  The go7007 struct won't be freed
+ * until v4l2 finishes releasing its resources and all associated fds are
+ * closed by applications.
+ */
+void go7007_remove(struct go7007 *go)
+{
+	if (go->i2c_adapter_online) {
+		if (i2c_del_adapter(&go->i2c_adapter) == 0)
+			go->i2c_adapter_online = 0;
+		else
+			printk(KERN_ERR
+				"go7007: error removing I2C adapter!\n");
+	}
+
+	if (go->audio_enabled)
+		go7007_snd_remove(go);
+	go7007_v4l2_remove(go);
+}
+EXPORT_SYMBOL(go7007_remove);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/go7007-fw.c b/drivers/staging/go7007/go7007-fw.c
new file mode 100644
index 0000000..c2aea10
--- /dev/null
+++ b/drivers/staging/go7007/go7007-fw.c
@@ -0,0 +1,1639 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+/*
+ * This file contains code to generate a firmware image for the GO7007SB
+ * encoder.  Much of the firmware is read verbatim from a file, but some of
+ * it concerning bitrate control and other things that can be configured at
+ * run-time are generated dynamically.  Note that the format headers
+ * generated here do not affect the functioning of the encoder; they are
+ * merely parroted back to the host at the start of each frame.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <asm/byteorder.h>
+
+#include "go7007-priv.h"
+
+/* Constants used in the source firmware image to describe code segments */
+
+#define	FLAG_MODE_MJPEG		(1)
+#define	FLAG_MODE_MPEG1		(1<<1)
+#define	FLAG_MODE_MPEG2		(1<<2)
+#define	FLAG_MODE_MPEG4		(1<<3)
+#define	FLAG_MODE_H263		(1<<4)
+#define FLAG_MODE_ALL		(FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \
+					FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \
+					FLAG_MODE_H263)
+#define FLAG_SPECIAL		(1<<8)
+
+#define SPECIAL_FRM_HEAD	0
+#define SPECIAL_BRC_CTRL	1
+#define SPECIAL_CONFIG		2
+#define SPECIAL_SEQHEAD		3
+#define SPECIAL_AV_SYNC		4
+#define SPECIAL_FINAL		5
+#define SPECIAL_AUDIO		6
+#define SPECIAL_MODET		7
+
+/* Little data class for creating MPEG headers bit-by-bit */
+
+struct code_gen {
+	unsigned char *p; /* destination */
+	u32 a; /* collects bits at the top of the variable */
+	int b; /* bit position of most recently-written bit */
+	int len; /* written out so far */
+};
+
+#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 }
+
+#define CODE_ADD(name, val, length) do { \
+	name.b -= (length); \
+	name.a |= (val) << name.b; \
+	while (name.b <= 24) { \
+		*name.p = name.a >> 24; \
+		++name.p; \
+		name.a <<= 8; \
+		name.b += 8; \
+		name.len += 8; \
+	} \
+} while (0)
+
+#define CODE_LENGTH(name) (name.len + (32 - name.b))
+
+/* Tables for creating the bitrate control data */
+
+static const s16 converge_speed_ip[101] = {
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+	1, 1, 1, 1, 1, 1, 1, 1, 1, 2,
+	2, 2, 2, 2, 2, 2, 2, 2, 2, 3,
+	3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+	5, 5, 5, 6, 6, 6, 7, 7, 8, 8,
+	9, 10, 10, 11, 12, 13, 14, 15, 16, 17,
+	19, 20, 22, 23, 25, 27, 30, 32, 35, 38,
+	41, 45, 49, 53, 58, 63, 69, 76, 83, 91,
+	100
+};
+
+static const s16 converge_speed_ipb[101] = {
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+	3, 3, 3, 3, 3, 4, 4, 4, 4, 4,
+	4, 4, 4, 4, 5, 5, 5, 5, 5, 6,
+	6, 6, 6, 7, 7, 7, 7, 8, 8, 9,
+	9, 9, 10, 10, 11, 12, 12, 13, 14, 14,
+	15, 16, 17, 18, 19, 20, 22, 23, 25, 26,
+	28, 30, 32, 34, 37, 40, 42, 46, 49, 53,
+	57, 61, 66, 71, 77, 83, 90, 97, 106, 115,
+	125, 135, 147, 161, 175, 191, 209, 228, 249, 273,
+	300
+};
+
+static const s16 LAMBDA_table[4][101] = {
+	{	16, 16, 16, 16, 17, 17, 17, 18, 18, 18,
+		19, 19, 19, 20, 20, 20, 21, 21, 22, 22,
+		22, 23, 23, 24, 24, 25, 25, 25, 26, 26,
+		27, 27, 28, 28, 29, 29, 30, 31, 31, 32,
+		32, 33, 33, 34, 35, 35, 36, 37, 37, 38,
+		39, 39, 40, 41, 42, 42, 43, 44, 45, 46,
+		46, 47, 48, 49, 50, 51, 52, 53, 54, 55,
+		56, 57, 58, 59, 60, 61, 62, 63, 64, 65,
+		67, 68, 69, 70, 72, 73, 74, 76, 77, 78,
+		80, 81, 83, 84, 86, 87, 89, 90, 92, 94,
+		96
+	},
+	{
+		20, 20, 20, 21, 21, 21, 22, 22, 23, 23,
+		23, 24, 24, 25, 25, 26, 26, 27, 27, 28,
+		28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
+		34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
+		40, 41, 42, 43, 43, 44, 45, 46, 47, 48,
+		48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
+		58, 59, 60, 61, 62, 64, 65, 66, 67, 68,
+		70, 71, 72, 73, 75, 76, 78, 79, 80, 82,
+		83, 85, 86, 88, 90, 91, 93, 95, 96, 98,
+		100, 102, 103, 105, 107, 109, 111, 113, 115, 117,
+		120
+	},
+	{
+		24, 24, 24, 25, 25, 26, 26, 27, 27, 28,
+		28, 29, 29, 30, 30, 31, 31, 32, 33, 33,
+		34, 34, 35, 36, 36, 37, 38, 38, 39, 40,
+		41, 41, 42, 43, 44, 44, 45, 46, 47, 48,
+		49, 50, 50, 51, 52, 53, 54, 55, 56, 57,
+		58, 59, 60, 62, 63, 64, 65, 66, 67, 69,
+		70, 71, 72, 74, 75, 76, 78, 79, 81, 82,
+		84, 85, 87, 88, 90, 92, 93, 95, 97, 98,
+		100, 102, 104, 106, 108, 110, 112, 114, 116, 118,
+		120, 122, 124, 127, 129, 131, 134, 136, 138, 141,
+		144
+	},
+	{
+		32, 32, 33, 33, 34, 34, 35, 36, 36, 37,
+		38, 38, 39, 40, 41, 41, 42, 43, 44, 44,
+		45, 46, 47, 48, 49, 50, 50, 51, 52, 53,
+		54, 55, 56, 57, 58, 59, 60, 62, 63, 64,
+		65, 66, 67, 69, 70, 71, 72, 74, 75, 76,
+		78, 79, 81, 82, 84, 85, 87, 88, 90, 92,
+		93, 95, 97, 98, 100, 102, 104, 106, 108, 110,
+		112, 114, 116, 118, 120, 122, 124, 127, 129, 131,
+		134, 136, 139, 141, 144, 146, 149, 152, 154, 157,
+		160, 163, 166, 169, 172, 175, 178, 181, 185, 188,
+		192
+	}
+};
+
+/* MPEG blank frame generation tables */
+
+enum mpeg_frame_type {
+	PFRAME,
+	BFRAME_PRE,
+	BFRAME_POST,
+	BFRAME_BIDIR,
+	BFRAME_EMPTY
+};
+
+static const u32 addrinctab[33][2] = {
+	{ 0x01, 1 },	{ 0x03, 3 },	{ 0x02, 3 },	{ 0x03, 4 },
+	{ 0x02, 4 },	{ 0x03, 5 },	{ 0x02, 5 },	{ 0x07, 7 },
+	{ 0x06, 7 },	{ 0x0b, 8 },	{ 0x0a, 8 },	{ 0x09, 8 },
+	{ 0x08, 8 },	{ 0x07, 8 },	{ 0x06, 8 },	{ 0x17, 10 },
+	{ 0x16, 10 },	{ 0x15, 10 },	{ 0x14, 10 },	{ 0x13, 10 },
+	{ 0x12, 10 },	{ 0x23, 11 },	{ 0x22, 11 },	{ 0x21, 11 },
+	{ 0x20, 11 },	{ 0x1f, 11 },	{ 0x1e, 11 },	{ 0x1d, 11 },
+	{ 0x1c, 11 },	{ 0x1b, 11 },	{ 0x1a, 11 },	{ 0x19, 11 },
+	{ 0x18, 11 }
+};
+
+/* Standard JPEG tables */
+
+static const u8 default_intra_quant_table[] = {
+	 8, 16, 19, 22, 26, 27, 29, 34,
+	16, 16, 22, 24, 27, 29, 34, 37,
+	19, 22, 26, 27, 29, 34, 34, 38,
+	22, 22, 26, 27, 29, 34, 37, 40,
+	22, 26, 27, 29, 32, 35, 40, 48,
+	26, 27, 29, 32, 35, 40, 48, 58,
+	26, 27, 29, 34, 38, 46, 56, 69,
+	27, 29, 35, 38, 46, 56, 69, 83
+};
+
+static const u8 bits_dc_luminance[] = {
+	0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0
+};
+
+static const u8 val_dc_luminance[] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+};
+
+static const u8 bits_dc_chrominance[] = {
+	0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0
+};
+
+static const u8 val_dc_chrominance[] = {
+	0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11
+};
+
+static const u8 bits_ac_luminance[] = {
+	0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d
+};
+
+static const u8 val_ac_luminance[] = {
+	0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+	0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+	0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+	0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+	0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+	0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+	0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+	0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+	0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+	0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+	0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+	0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+	0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+	0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+	0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+	0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+	0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+	0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+	0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+	0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+	0xf9, 0xfa
+};
+
+static const u8 bits_ac_chrominance[] = {
+	0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77
+};
+
+static const u8 val_ac_chrominance[] = {
+	0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
+	0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
+	0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
+	0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
+	0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
+	0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
+	0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
+	0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
+	0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
+	0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
+	0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
+	0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+	0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
+	0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
+	0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
+	0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
+	0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
+	0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
+	0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+	0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+	0xf9, 0xfa
+};
+
+/* Zig-zag mapping for quant table
+ *
+ * OK, let's do this mapping on the actual table above so it doesn't have
+ * to be done on the fly.
+ */
+static const int zz[64] = {
+	0,   1,  8, 16,  9,  2,  3, 10, 17, 24, 32, 25, 18, 11,  4,  5,
+	12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13,  6,  7, 14, 21, 28,
+	35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
+	58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
+};
+
+static int copy_packages(u16 *dest, u16 *src, int pkg_cnt, int space)
+{
+	int i, cnt = pkg_cnt * 32;
+
+	if (space < cnt)
+		return -1;
+
+	for (i = 0; i < cnt; ++i)
+		dest[i] = __cpu_to_le16(src[i]);
+
+	return cnt;
+}
+
+static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q)
+{
+	int i, p = 0;
+
+	buf[p++] = 0xff;
+	buf[p++] = 0xd8;
+	buf[p++] = 0xff;
+	buf[p++] = 0xdb;
+	buf[p++] = 0;
+	buf[p++] = 2 + 65;
+	buf[p++] = 0;
+	buf[p++] = default_intra_quant_table[0];
+	for (i = 1; i < 64; ++i)
+		/* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */
+		buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3;
+	buf[p++] = 0xff;
+	buf[p++] = 0xc0;
+	buf[p++] = 0;
+	buf[p++] = 17;
+	buf[p++] = 8;
+	buf[p++] = go->height >> 8;
+	buf[p++] = go->height & 0xff;
+	buf[p++] = go->width >> 8;
+	buf[p++] = go->width & 0xff;
+	buf[p++] = 3;
+	buf[p++] = 1;
+	buf[p++] = 0x22;
+	buf[p++] = 0;
+	buf[p++] = 2;
+	buf[p++] = 0x11;
+	buf[p++] = 0;
+	buf[p++] = 3;
+	buf[p++] = 0x11;
+	buf[p++] = 0;
+	buf[p++] = 0xff;
+	buf[p++] = 0xc4;
+	buf[p++] = 418 >> 8;
+	buf[p++] = 418 & 0xff;
+	buf[p++] = 0x00;
+	memcpy(buf + p, bits_dc_luminance + 1, 16);
+	p += 16;
+	memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance));
+	p += sizeof(val_dc_luminance);
+	buf[p++] = 0x01;
+	memcpy(buf + p, bits_dc_chrominance + 1, 16);
+	p += 16;
+	memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance));
+	p += sizeof(val_dc_chrominance);
+	buf[p++] = 0x10;
+	memcpy(buf + p, bits_ac_luminance + 1, 16);
+	p += 16;
+	memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance));
+	p += sizeof(val_ac_luminance);
+	buf[p++] = 0x11;
+	memcpy(buf + p, bits_ac_chrominance + 1, 16);
+	p += 16;
+	memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance));
+	p += sizeof(val_ac_chrominance);
+	buf[p++] = 0xff;
+	buf[p++] = 0xda;
+	buf[p++] = 0;
+	buf[p++] = 12;
+	buf[p++] = 3;
+	buf[p++] = 1;
+	buf[p++] = 0x00;
+	buf[p++] = 2;
+	buf[p++] = 0x11;
+	buf[p++] = 3;
+	buf[p++] = 0x11;
+	buf[p++] = 0;
+	buf[p++] = 63;
+	buf[p++] = 0;
+	return p;
+}
+
+static int gen_mjpeghdr_to_package(struct go7007 *go, u16 *code, int space)
+{
+	u8 *buf;
+	u16 mem = 0x3e00;
+	unsigned int addr = 0x19;
+	int size = 0, i, off = 0, chunk;
+
+	buf = kmalloc(4096, GFP_KERNEL);
+	if (buf == NULL) {
+		printk(KERN_ERR "go7007: unable to allocate 4096 bytes for "
+				"firmware construction\n");
+		return -1;
+	}
+	memset(buf, 0, 4096);
+
+	for (i = 1; i < 32; ++i) {
+		mjpeg_frame_header(go, buf + size, i);
+		size += 80;
+	}
+	chunk = mjpeg_frame_header(go, buf + size, 1);
+	memmove(buf + size, buf + size + 80, chunk - 80);
+	size += chunk - 80;
+
+	for (i = 0; i < size; i += chunk * 2) {
+		if (space - off < 32) {
+			off = -1;
+			goto done;
+		}
+
+		code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+		chunk = 28;
+		if (mem + chunk > 0x4000)
+			chunk = 0x4000 - mem;
+		if (i + 2 * chunk > size)
+			chunk = (size - i) / 2;
+
+		if (chunk < 28) {
+			code[off] = __cpu_to_le16(0x4000 | chunk);
+			code[off + 31] = __cpu_to_le16(addr++);
+			mem = 0x3e00;
+		} else {
+			code[off] = __cpu_to_le16(0x1000 | 28);
+			code[off + 31] = 0;
+			mem += 28;
+		}
+
+		memcpy(&code[off + 2], buf + i, chunk * 2);
+		off += 32;
+	}
+done:
+	kfree(buf);
+	return off;
+}
+
+static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf,
+		int modulo, int pict_struct, enum mpeg_frame_type frame)
+{
+	int i, j, mb_code, mb_len;
+	int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
+	CODE_GEN(c, buf + 6);
+
+	switch (frame) {
+	case PFRAME:
+		mb_code = 0x1;
+		mb_len = 3;
+		break;
+	case BFRAME_PRE:
+		mb_code = 0x2;
+		mb_len = 4;
+		break;
+	case BFRAME_POST:
+		mb_code = 0x2;
+		mb_len = 3;
+		break;
+	case BFRAME_BIDIR:
+		mb_code = 0x2;
+		mb_len = 2;
+		break;
+	default: /* keep the compiler happy */
+		mb_code = mb_len = 0;
+		break;
+	}
+
+	CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13);
+	CODE_ADD(c, 0xffff, 16);
+	CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
+	if (frame != PFRAME)
+		CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4);
+	else
+		CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */
+	CODE_ADD(c, 0, 3); /* What is this?? */
+	/* Byte-align with zeros */
+	j = 8 - (CODE_LENGTH(c) % 8);
+	if (j != 8)
+		CODE_ADD(c, 0, j);
+
+	if (go->format == GO7007_FORMAT_MPEG2) {
+		CODE_ADD(c, 0x1, 24);
+		CODE_ADD(c, 0xb5, 8);
+		CODE_ADD(c, 0x844, 12);
+		CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8);
+		if (go->interlace_coding) {
+			CODE_ADD(c, pict_struct, 4);
+			if (go->dvd_mode)
+				CODE_ADD(c, 0x000, 11);
+			else
+				CODE_ADD(c, 0x200, 11);
+		} else {
+			CODE_ADD(c, 0x3, 4);
+			CODE_ADD(c, 0x20c, 11);
+		}
+		/* Byte-align with zeros */
+		j = 8 - (CODE_LENGTH(c) % 8);
+		if (j != 8)
+			CODE_ADD(c, 0, j);
+	}
+
+	for (i = 0; i < rows; ++i) {
+		CODE_ADD(c, 1, 24);
+		CODE_ADD(c, i + 1, 8);
+		CODE_ADD(c, 0x2, 6);
+		CODE_ADD(c, 0x1, 1);
+		CODE_ADD(c, mb_code, mb_len);
+		if (go->interlace_coding) {
+			CODE_ADD(c, 0x1, 2);
+			CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+		}
+		if (frame == BFRAME_BIDIR) {
+			CODE_ADD(c, 0x3, 2);
+			if (go->interlace_coding)
+				CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+		}
+		CODE_ADD(c, 0x3, 2);
+		for (j = (go->width >> 4) - 2; j >= 33; j -= 33)
+			CODE_ADD(c, 0x8, 11);
+		CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]);
+		CODE_ADD(c, mb_code, mb_len);
+		if (go->interlace_coding) {
+			CODE_ADD(c, 0x1, 2);
+			CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+		}
+		if (frame == BFRAME_BIDIR) {
+			CODE_ADD(c, 0x3, 2);
+			if (go->interlace_coding)
+				CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1);
+		}
+		CODE_ADD(c, 0x3, 2);
+
+		/* Byte-align with zeros */
+		j = 8 - (CODE_LENGTH(c) % 8);
+		if (j != 8)
+			CODE_ADD(c, 0, j);
+	}
+
+	i = CODE_LENGTH(c) + 4 * 8;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x01;
+	buf[5] = 0x00;
+	return i;
+}
+
+static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
+{
+	int i, aspect_ratio, picture_rate;
+	CODE_GEN(c, buf + 6);
+
+	if (go->format == GO7007_FORMAT_MPEG1) {
+		switch (go->aspect_ratio) {
+		case GO7007_RATIO_4_3:
+			aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
+			break;
+		case GO7007_RATIO_16_9:
+			aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
+			break;
+		default:
+			aspect_ratio = 1;
+			break;
+		}
+	} else {
+		switch (go->aspect_ratio) {
+		case GO7007_RATIO_4_3:
+			aspect_ratio = 2;
+			break;
+		case GO7007_RATIO_16_9:
+			aspect_ratio = 3;
+			break;
+		default:
+			aspect_ratio = 1;
+			break;
+		}
+	}
+	switch (go->sensor_framerate) {
+	case 24000:
+		picture_rate = 1;
+		break;
+	case 24024:
+		picture_rate = 2;
+		break;
+	case 25025:
+		picture_rate = go->interlace_coding ? 6 : 3;
+		break;
+	case 30000:
+		picture_rate = go->interlace_coding ? 7 : 4;
+		break;
+	case 30030:
+		picture_rate = go->interlace_coding ? 8 : 5;
+		break;
+	default:
+		picture_rate = 5; /* 30 fps seems like a reasonable default */
+		break;
+	}
+
+	CODE_ADD(c, go->width, 12);
+	CODE_ADD(c, go->height, 12);
+	CODE_ADD(c, aspect_ratio, 4);
+	CODE_ADD(c, picture_rate, 4);
+	CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18);
+	CODE_ADD(c, 1, 1);
+	CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10);
+	CODE_ADD(c, 0, 3);
+
+	/* Byte-align with zeros */
+	i = 8 - (CODE_LENGTH(c) % 8);
+	if (i != 8)
+		CODE_ADD(c, 0, i);
+
+	if (go->format == GO7007_FORMAT_MPEG2) {
+		CODE_ADD(c, 0x1, 24);
+		CODE_ADD(c, 0xb5, 8);
+		CODE_ADD(c, 0x148, 12);
+		if (go->interlace_coding)
+			CODE_ADD(c, 0x20001, 20);
+		else
+			CODE_ADD(c, 0xa0001, 20);
+		CODE_ADD(c, 0, 16);
+
+		/* Byte-align with zeros */
+		i = 8 - (CODE_LENGTH(c) % 8);
+		if (i != 8)
+			CODE_ADD(c, 0, i);
+
+		if (ext) {
+			CODE_ADD(c, 0x1, 24);
+			CODE_ADD(c, 0xb52, 12);
+			CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3);
+			CODE_ADD(c, 0x105, 9);
+			CODE_ADD(c, 0x505, 16);
+			CODE_ADD(c, go->width, 14);
+			CODE_ADD(c, 1, 1);
+			CODE_ADD(c, go->height, 14);
+
+			/* Byte-align with zeros */
+			i = 8 - (CODE_LENGTH(c) % 8);
+			if (i != 8)
+				CODE_ADD(c, 0, i);
+		}
+	}
+
+	i = CODE_LENGTH(c) + 4 * 8;
+	buf[0] = i & 0xff;
+	buf[1] = i >> 8;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x01;
+	buf[5] = 0xb3;
+	return i;
+}
+
+static int gen_mpeg1hdr_to_package(struct go7007 *go,
+					u16 *code, int space, int *framelen)
+{
+	u8 *buf;
+	u16 mem = 0x3e00;
+	unsigned int addr = 0x19;
+	int i, off = 0, chunk;
+
+	buf = kmalloc(5120, GFP_KERNEL);
+	if (buf == NULL) {
+		printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
+				"firmware construction\n");
+		return -1;
+	}
+	memset(buf, 0, 5120);
+	framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME);
+	if (go->interlace_coding)
+		framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8,
+							0, 2, PFRAME);
+	buf[0] = framelen[0] & 0xff;
+	buf[1] = framelen[0] >> 8;
+	i = 368;
+	framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE);
+	if (go->interlace_coding)
+		framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8,
+							0, 2, BFRAME_PRE);
+	buf[i] = framelen[1] & 0xff;
+	buf[i + 1] = framelen[1] >> 8;
+	i += 1632;
+	framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST);
+	if (go->interlace_coding)
+		framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8,
+							0, 2, BFRAME_POST);
+	buf[i] = framelen[2] & 0xff;
+	buf[i + 1] = framelen[2] >> 8;
+	i += 1432;
+	framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR);
+	if (go->interlace_coding)
+		framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8,
+							0, 2, BFRAME_BIDIR);
+	buf[i] = framelen[3] & 0xff;
+	buf[i + 1] = framelen[3] >> 8;
+	i += 1632 + 16;
+	mpeg1_sequence_header(go, buf + i, 0);
+	i += 40;
+	for (i = 0; i < 5120; i += chunk * 2) {
+		if (space - off < 32) {
+			off = -1;
+			goto done;
+		}
+
+		code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+		chunk = 28;
+		if (mem + chunk > 0x4000)
+			chunk = 0x4000 - mem;
+		if (i + 2 * chunk > 5120)
+			chunk = (5120 - i) / 2;
+
+		if (chunk < 28) {
+			code[off] = __cpu_to_le16(0x4000 | chunk);
+			code[off + 31] = __cpu_to_le16(addr);
+			if (mem + chunk == 0x4000) {
+				mem = 0x3e00;
+				++addr;
+			}
+		} else {
+			code[off] = __cpu_to_le16(0x1000 | 28);
+			code[off + 31] = 0;
+			mem += 28;
+		}
+
+		memcpy(&code[off + 2], buf + i, chunk * 2);
+		off += 32;
+	}
+done:
+	kfree(buf);
+	return off;
+}
+
+static int vti_bitlen(struct go7007 *go)
+{
+	unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale;
+
+	for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i);
+	return i + 1;
+}
+
+static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf,
+		int modulo, enum mpeg_frame_type frame)
+{
+	int i;
+	CODE_GEN(c, buf + 6);
+	int mb_count = (go->width >> 4) * (go->height >> 4);
+
+	CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2);
+	if (modulo)
+		CODE_ADD(c, 0x1, 1);
+	CODE_ADD(c, 0x1, 2);
+	CODE_ADD(c, 0, vti_bitlen(go));
+	CODE_ADD(c, 0x3, 2);
+	if (frame == PFRAME)
+		CODE_ADD(c, 0, 1);
+	CODE_ADD(c, 0xc, 11);
+	if (frame != PFRAME)
+		CODE_ADD(c, 0x4, 3);
+	if (frame != BFRAME_EMPTY) {
+		for (i = 0; i < mb_count; ++i) {
+			switch (frame) {
+			case PFRAME:
+				CODE_ADD(c, 0x1, 1);
+				break;
+			case BFRAME_PRE:
+				CODE_ADD(c, 0x47, 8);
+				break;
+			case BFRAME_POST:
+				CODE_ADD(c, 0x27, 7);
+				break;
+			case BFRAME_BIDIR:
+				CODE_ADD(c, 0x5f, 8);
+				break;
+			case BFRAME_EMPTY: /* keep compiler quiet */
+				break;
+			}
+		}
+	}
+
+	/* Byte-align with a zero followed by ones */
+	i = 8 - (CODE_LENGTH(c) % 8);
+	CODE_ADD(c, 0, 1);
+	CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
+
+	i = CODE_LENGTH(c) + 4 * 8;
+	buf[0] = i & 0xff;
+	buf[1] = i >> 8;
+	buf[2] = 0x00;
+	buf[3] = 0x00;
+	buf[4] = 0x01;
+	buf[5] = 0xb6;
+	return i;
+}
+
+static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext)
+{
+	const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali,
+		0x00, 0x00, 0x01, 0xb5, 0x09,
+		0x00, 0x00, 0x01, 0x00,
+		0x00, 0x00, 0x01, 0x20, };
+	int i, aspect_ratio;
+	int fps = go->sensor_framerate / go->fps_scale;
+	CODE_GEN(c, buf + 2 + sizeof(head));
+
+	switch (go->aspect_ratio) {
+	case GO7007_RATIO_4_3:
+		aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2;
+		break;
+	case GO7007_RATIO_16_9:
+		aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4;
+		break;
+	default:
+		aspect_ratio = 1;
+		break;
+	}
+
+	memcpy(buf + 2, head, sizeof(head));
+	CODE_ADD(c, 0x191, 17);
+	CODE_ADD(c, aspect_ratio, 4);
+	CODE_ADD(c, 0x1, 4);
+	CODE_ADD(c, fps, 16);
+	CODE_ADD(c, 0x3, 2);
+	CODE_ADD(c, 1001, vti_bitlen(go));
+	CODE_ADD(c, 1, 1);
+	CODE_ADD(c, go->width, 13);
+	CODE_ADD(c, 1, 1);
+	CODE_ADD(c, go->height, 13);
+	CODE_ADD(c, 0x2830, 14);
+
+	/* Byte-align */
+	i = 8 - (CODE_LENGTH(c) % 8);
+	CODE_ADD(c, 0, 1);
+	CODE_ADD(c, (1 << (i - 1)) - 1, i - 1);
+
+	i = CODE_LENGTH(c) + sizeof(head) * 8;
+	buf[0] = i & 0xff;
+	buf[1] = i >> 8;
+	return i;
+}
+
+static int gen_mpeg4hdr_to_package(struct go7007 *go,
+					u16 *code, int space, int *framelen)
+{
+	u8 *buf;
+	u16 mem = 0x3e00;
+	unsigned int addr = 0x19;
+	int i, off = 0, chunk;
+
+	buf = kmalloc(5120, GFP_KERNEL);
+	if (buf == NULL) {
+		printk(KERN_ERR "go7007: unable to allocate 5120 bytes for "
+				"firmware construction\n");
+		return -1;
+	}
+	memset(buf, 0, 5120);
+	framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME);
+	i = 368;
+	framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE);
+	i += 1632;
+	framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST);
+	i += 1432;
+	framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR);
+	i += 1632;
+	mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY);
+	i += 16;
+	mpeg4_sequence_header(go, buf + i, 0);
+	i += 40;
+	for (i = 0; i < 5120; i += chunk * 2) {
+		if (space - off < 32) {
+			off = -1;
+			goto done;
+		}
+
+		code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+		chunk = 28;
+		if (mem + chunk > 0x4000)
+			chunk = 0x4000 - mem;
+		if (i + 2 * chunk > 5120)
+			chunk = (5120 - i) / 2;
+
+		if (chunk < 28) {
+			code[off] = __cpu_to_le16(0x4000 | chunk);
+			code[off + 31] = __cpu_to_le16(addr);
+			if (mem + chunk == 0x4000) {
+				mem = 0x3e00;
+				++addr;
+			}
+		} else {
+			code[off] = __cpu_to_le16(0x1000 | 28);
+			code[off + 31] = 0;
+			mem += 28;
+		}
+
+		memcpy(&code[off + 2], buf + i, chunk * 2);
+		off += 32;
+	}
+	mem = 0x3e00;
+	addr = go->ipb ? 0x14f9 : 0x0af9;
+	memset(buf, 0, 5120);
+	framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME);
+	i = 368;
+	framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE);
+	i += 1632;
+	framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST);
+	i += 1432;
+	framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR);
+	i += 1632;
+	mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY);
+	i += 16;
+	for (i = 0; i < 5120; i += chunk * 2) {
+		if (space - off < 32) {
+			off = -1;
+			goto done;
+		}
+
+		code[off + 1] = __cpu_to_le16(0x8000 | mem);
+
+		chunk = 28;
+		if (mem + chunk > 0x4000)
+			chunk = 0x4000 - mem;
+		if (i + 2 * chunk > 5120)
+			chunk = (5120 - i) / 2;
+
+		if (chunk < 28) {
+			code[off] = __cpu_to_le16(0x4000 | chunk);
+			code[off + 31] = __cpu_to_le16(addr);
+			if (mem + chunk == 0x4000) {
+				mem = 0x3e00;
+				++addr;
+			}
+		} else {
+			code[off] = __cpu_to_le16(0x1000 | 28);
+			code[off + 31] = 0;
+			mem += 28;
+		}
+
+		memcpy(&code[off + 2], buf + i, chunk * 2);
+		off += 32;
+	}
+done:
+	kfree(buf);
+	return off;
+}
+
+static int brctrl_to_package(struct go7007 *go,
+					u16 *code, int space, int *framelen)
+{
+	int converge_speed = 0;
+	int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ?
+				100 : 0;
+	int peak_rate = 6 * go->bitrate / 5;
+	int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ?
+				go->bitrate :
+				(go->dvd_mode ? 900000 : peak_rate);
+	int fps = go->sensor_framerate / go->fps_scale;
+	int q = 0;
+	/* Bizarre math below depends on rounding errors in division */
+	u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps;
+	u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps;
+	u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000);
+	u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32);
+	u32 cplx[] = {
+		q > 0 ? sgop_expt_addr * q :
+			2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+		q > 0 ? sgop_expt_addr * q :
+			2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+		q > 0 ? sgop_expt_addr * q :
+			2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+		q > 0 ? sgop_expt_addr * q :
+			2 * go->width * go->height * (go->ipb ? 6 : 4) / 32,
+	};
+	u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr;
+	u16 pack[] = {
+		0x200e,		0x0000,
+		0xBF20,		go->ipb ? converge_speed_ipb[converge_speed]
+					: converge_speed_ip[converge_speed],
+		0xBF21,		go->ipb ? 2 : 0,
+		0xBF22,		go->ipb ? LAMBDA_table[0][lambda / 2 + 50]
+					: 32767,
+		0xBF23,		go->ipb ? LAMBDA_table[1][lambda] : 32767,
+		0xBF24,		32767,
+		0xBF25,		lambda > 99 ? 32767 : LAMBDA_table[3][lambda],
+		0xBF26,		sgop_expt_addr & 0x0000FFFF,
+		0xBF27,		sgop_expt_addr >> 16,
+		0xBF28,		sgop_peak_addr & 0x0000FFFF,
+		0xBF29,		sgop_peak_addr >> 16,
+		0xBF2A,		vbv_alert_addr & 0x0000FFFF,
+		0xBF2B,		vbv_alert_addr >> 16,
+		0xBF2C,		0,
+		0xBF2D,		0,
+		0,		0,
+
+		0x200e,		0x0000,
+		0xBF2E,		vbv_alert_addr & 0x0000FFFF,
+		0xBF2F,		vbv_alert_addr >> 16,
+		0xBF30,		cplx[0] & 0x0000FFFF,
+		0xBF31,		cplx[0] >> 16,
+		0xBF32,		cplx[1] & 0x0000FFFF,
+		0xBF33,		cplx[1] >> 16,
+		0xBF34,		cplx[2] & 0x0000FFFF,
+		0xBF35,		cplx[2] >> 16,
+		0xBF36,		cplx[3] & 0x0000FFFF,
+		0xBF37,		cplx[3] >> 16,
+		0xBF38,		0,
+		0xBF39,		0,
+		0xBF3A,		total_expt_addr & 0x0000FFFF,
+		0xBF3B,		total_expt_addr >> 16,
+		0,		0,
+
+		0x200e,		0x0000,
+		0xBF3C,		total_expt_addr & 0x0000FFFF,
+		0xBF3D,		total_expt_addr >> 16,
+		0xBF3E,		0,
+		0xBF3F,		0,
+		0xBF48,		0,
+		0xBF49,		0,
+		0xBF4A,		calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q),
+		0xBF4B,		4,
+		0xBF4C,		0,
+		0xBF4D,		0,
+		0xBF4E,		0,
+		0xBF4F,		0,
+		0xBF50,		0,
+		0xBF51,		0,
+		0,		0,
+
+		0x200e,		0x0000,
+		0xBF40,		sgop_expt_addr & 0x0000FFFF,
+		0xBF41,		sgop_expt_addr >> 16,
+		0xBF42,		0,
+		0xBF43,		0,
+		0xBF44,		0,
+		0xBF45,		0,
+		0xBF46,		(go->width >> 4) * (go->height >> 4),
+		0xBF47,		0,
+		0xBF64,		0,
+		0xBF65,		0,
+		0xBF18,		framelen[4],
+		0xBF19,		framelen[5],
+		0xBF1A,		framelen[6],
+		0xBF1B,		framelen[7],
+		0,		0,
+
+#if 0 /* Remove once we don't care about matching */
+		0x200e,		0x0000,
+		0xBF56,		4,
+		0xBF57,		0,
+		0xBF58,		5,
+		0xBF59,		0,
+		0xBF5A,		6,
+		0xBF5B,		0,
+		0xBF5C,		8,
+		0xBF5D,		0,
+		0xBF5E,		1,
+		0xBF5F,		0,
+		0xBF60,		1,
+		0xBF61,		0,
+		0xBF62,		0,
+		0xBF63,		0,
+		0,		0,
+#else
+		0x2008,		0x0000,
+		0xBF56,		4,
+		0xBF57,		0,
+		0xBF58,		5,
+		0xBF59,		0,
+		0xBF5A,		6,
+		0xBF5B,		0,
+		0xBF5C,		8,
+		0xBF5D,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+#endif
+
+		0x200e,		0x0000,
+		0xBF10,		0,
+		0xBF11,		0,
+		0xBF12,		0,
+		0xBF13,		0,
+		0xBF14,		0,
+		0xBF15,		0,
+		0xBF16,		0,
+		0xBF17,		0,
+		0xBF7E,		0,
+		0xBF7F,		1,
+		0xBF52,		framelen[0],
+		0xBF53,		framelen[1],
+		0xBF54,		framelen[2],
+		0xBF55,		framelen[3],
+		0,		0,
+	};
+
+	return copy_packages(code, pack, 6, space);
+}
+
+static int config_package(struct go7007 *go, u16 *code, int space)
+{
+	int fps = go->sensor_framerate / go->fps_scale / 1000;
+	int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
+	int brc_window_size = fps;
+	int q_min = 2, q_max = 31;
+	int THACCoeffSet0 = 0;
+	u16 pack[] = {
+		0x200e,		0x0000,
+		0xc002,		0x14b4,
+		0xc003,		0x28b4,
+		0xc004,		0x3c5a,
+		0xdc05,		0x2a77,
+		0xc6c3,		go->format == GO7007_FORMAT_MPEG4 ? 0 :
+				(go->format == GO7007_FORMAT_H263 ? 0 : 1),
+		0xc680,		go->format == GO7007_FORMAT_MPEG4 ? 0xf1 :
+				(go->format == GO7007_FORMAT_H263 ? 0x61 :
+									0xd3),
+		0xc780,		0x0140,
+		0xe009,		0x0001,
+		0xc60f,		0x0008,
+		0xd4ff,		0x0002,
+		0xe403,		2340,
+		0xe406,		75,
+		0xd411,		0x0001,
+		0xd410,		0xa1d6,
+		0x0001,		0x2801,
+
+		0x200d,		0x0000,
+		0xe402,		0x018b,
+		0xe401,		0x8b01,
+		0xd472,		(go->board_info->sensor_flags &
+							GO7007_SENSOR_TV) &&
+						(!go->interlace_coding) ?
+					0x01b0 : 0x0170,
+		0xd475,		(go->board_info->sensor_flags &
+							GO7007_SENSOR_TV) &&
+						(!go->interlace_coding) ?
+					0x0008 : 0x0009,
+		0xc404,		go->interlace_coding ? 0x44 :
+				(go->format == GO7007_FORMAT_MPEG4 ? 0x11 :
+				(go->format == GO7007_FORMAT_MPEG1 ? 0x02 :
+				(go->format == GO7007_FORMAT_MPEG2 ? 0x04 :
+				(go->format == GO7007_FORMAT_H263  ? 0x08 :
+								     0x20)))),
+		0xbf0a,		(go->format == GO7007_FORMAT_MPEG4 ? 8 :
+				(go->format == GO7007_FORMAT_MPEG1 ? 1 :
+				(go->format == GO7007_FORMAT_MPEG2 ? 2 :
+				(go->format == GO7007_FORMAT_H263 ? 4 : 16)))) |
+				((go->repeat_seqhead ? 1 : 0) << 6) |
+				((go->dvd_mode ? 1 : 0) << 9) |
+				((go->gop_header_enable ? 1 : 0) << 10),
+		0xbf0b,		0,
+		0xdd5a,		go->ipb ? 0x14 : 0x0a,
+		0xbf0c,		0,
+		0xbf0d,		0,
+		0xc683,		THACCoeffSet0,
+		0xc40a,		(go->width << 4) | rows,
+		0xe01a,		go->board_info->hpi_buffer_cap,
+		0,		0,
+		0,		0,
+
+		0x2008,		0,
+		0xe402,		0x88,
+		0xe401,		0x8f01,
+		0xbf6a,		0,
+		0xbf6b,		0,
+		0xbf6c,		0,
+		0xbf6d,		0,
+		0xbf6e,		0,
+		0xbf6f,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+
+		0x200e,		0,
+		0xbf66,		brc_window_size,
+		0xbf67,		0,
+		0xbf68,		q_min,
+		0xbf69,		q_max,
+		0xbfe0,		0,
+		0xbfe1,		0,
+		0xbfe2,		0,
+		0xbfe3,		go->ipb ? 3 : 1,
+		0xc031,		go->board_info->sensor_flags &
+					GO7007_SENSOR_VBI ? 1 : 0,
+		0xc01c,		0x1f,
+		0xdd8c,		0x15,
+		0xdd94,		0x15,
+		0xdd88,		go->ipb ? 0x1401 : 0x0a01,
+		0xdd90,		go->ipb ? 0x1401 : 0x0a01,
+		0,		0,
+
+		0x200e,		0,
+		0xbfe4,		0,
+		0xbfe5,		0,
+		0xbfe6,		0,
+		0xbfe7,		fps << 8,
+		0xbfe8,		0x3a00,
+		0xbfe9,		0,
+		0xbfea,		0,
+		0xbfeb,		0,
+		0xbfec,		(go->interlace_coding ? 1 << 15 : 0) |
+					(go->modet_enable ? 0xa : 0) |
+					(go->board_info->sensor_flags &
+						GO7007_SENSOR_VBI ? 1 : 0),
+		0xbfed,		0,
+		0xbfee,		0,
+		0xbfef,		0,
+		0xbff0,		go->board_info->sensor_flags &
+					GO7007_SENSOR_TV ? 0xf060 : 0xb060,
+		0xbff1,		0,
+		0,		0,
+	};
+
+	return copy_packages(code, pack, 5, space);
+}
+
+static int seqhead_to_package(struct go7007 *go, u16 *code, int space,
+	int (*sequence_header_func)(struct go7007 *go,
+		unsigned char *buf, int ext))
+{
+	int vop_time_increment_bitlength = vti_bitlen(go);
+	int fps = go->sensor_framerate / go->fps_scale *
+					(go->interlace_coding ? 2 : 1);
+	unsigned char buf[40] = { };
+	int len = sequence_header_func(go, buf, 1);
+	u16 pack[] = {
+		0x2006,		0,
+		0xbf08,		fps,
+		0xbf09,		0,
+		0xbff2,		vop_time_increment_bitlength,
+		0xbff3,		(1 << vop_time_increment_bitlength) - 1,
+		0xbfe6,		0,
+		0xbfe7,		(fps / 1000) << 8,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+
+		0x2007,		0,
+		0xc800,		buf[2] << 8 | buf[3],
+		0xc801,		buf[4] << 8 | buf[5],
+		0xc802,		buf[6] << 8 | buf[7],
+		0xc803,		buf[8] << 8 | buf[9],
+		0xc406,		64,
+		0xc407,		len - 64,
+		0xc61b,		1,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+
+		0x200e,		0,
+		0xc808,		buf[10] << 8 | buf[11],
+		0xc809,		buf[12] << 8 | buf[13],
+		0xc80a,		buf[14] << 8 | buf[15],
+		0xc80b,		buf[16] << 8 | buf[17],
+		0xc80c,		buf[18] << 8 | buf[19],
+		0xc80d,		buf[20] << 8 | buf[21],
+		0xc80e,		buf[22] << 8 | buf[23],
+		0xc80f,		buf[24] << 8 | buf[25],
+		0xc810,		buf[26] << 8 | buf[27],
+		0xc811,		buf[28] << 8 | buf[29],
+		0xc812,		buf[30] << 8 | buf[31],
+		0xc813,		buf[32] << 8 | buf[33],
+		0xc814,		buf[34] << 8 | buf[35],
+		0xc815,		buf[36] << 8 | buf[37],
+		0,		0,
+		0,		0,
+		0,		0,
+	};
+
+	return copy_packages(code, pack, 3, space);
+}
+
+static int relative_prime(int big, int little)
+{
+	int remainder;
+
+	while (little != 0) {
+		remainder = big % little;
+		big = little;
+		little = remainder;
+	}
+	return big;
+}
+
+static int avsync_to_package(struct go7007 *go, u16 *code, int space)
+{
+	int arate = go->board_info->audio_rate * 1001 * go->fps_scale;
+	int ratio = arate / go->sensor_framerate;
+	int adjratio = ratio * 215 / 100;
+	int rprime = relative_prime(go->sensor_framerate,
+					arate % go->sensor_framerate);
+	int f1 = (arate % go->sensor_framerate) / rprime;
+	int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime;
+	u16 pack[] = {
+		0x200e,		0,
+		0xbf98,		(u16)((-adjratio) & 0xffff),
+		0xbf99,		(u16)((-adjratio) >> 16),
+		0xbf92,		0,
+		0xbf93,		0,
+		0xbff4,		f1 > f2 ? f1 : f2,
+		0xbff5,		f1 < f2 ? f1 : f2,
+		0xbff6,		f1 < f2 ? ratio : ratio + 1,
+		0xbff7,		f1 > f2 ? ratio : ratio + 1,
+		0xbff8,		0,
+		0xbff9,		0,
+		0xbffa,		adjratio & 0xffff,
+		0xbffb,		adjratio >> 16,
+		0xbf94,		0,
+		0xbf95,		0,
+		0,		0,
+	};
+
+	return copy_packages(code, pack, 1, space);
+}
+
+static int final_package(struct go7007 *go, u16 *code, int space)
+{
+	int rows = go->interlace_coding ? go->height / 32 : go->height / 16;
+	u16 pack[] = {
+		0x8000,
+		0,
+		0,
+		0,
+		0,
+		0,
+		0,
+		2,
+		((go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
+						(!go->interlace_coding) ?
+					(1 << 14) | (1 << 9) : 0) |
+			((go->encoder_subsample ? 1 : 0) << 8) |
+			(go->board_info->sensor_flags &
+				GO7007_SENSOR_CONFIG_MASK),
+		((go->encoder_v_halve ? 1 : 0) << 14) |
+			(go->encoder_v_halve ? rows << 9 : rows << 8) |
+			(go->encoder_h_halve ? 1 << 6 : 0) |
+			(go->encoder_h_halve ? go->width >> 3 : go->width >> 4),
+		(1 << 15) | (go->encoder_v_offset << 6) |
+			(1 << 7) | (go->encoder_h_offset >> 2),
+		(1 << 6),
+		0,
+		0,
+		((go->fps_scale - 1) << 8) |
+			(go->board_info->sensor_flags & GO7007_SENSOR_TV ?
+						(1 << 7) : 0) |
+			0x41,
+		go->ipb ? 0xd4c : 0x36b,
+		(rows << 8) | (go->width >> 4),
+		go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0,
+		(1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) |
+			((go->closed_gop ? 1 : 0) << 12) |
+			((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) |
+		/*	(1 << 9) |   */
+			((go->ipb ? 3 : 0) << 7) |
+			((go->modet_enable ? 1 : 0) << 2) |
+			((go->dvd_mode ? 1 : 0) << 1) | 1,
+		(go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 :
+			(go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 :
+			(go->format == GO7007_FORMAT_MJPEG ? 0x89a0 :
+			(go->format == GO7007_FORMAT_MPEG4 ? 0x8920 :
+			(go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))),
+		go->ipb ? 0x1f15 : 0x1f0b,
+		go->ipb ? 0x0015 : 0x000b,
+		go->ipb ? 0xa800 : 0x5800,
+		0xffff,
+		0x0020 + 0x034b * 0,
+		0x0020 + 0x034b * 1,
+		0x0020 + 0x034b * 2,
+		0x0020 + 0x034b * 3,
+		0x0020 + 0x034b * 4,
+		0x0020 + 0x034b * 5,
+		go->ipb ? (go->gop_size / 3) : go->gop_size,
+		(go->height >> 4) * (go->width >> 4) * 110 / 100,
+	};
+
+	return copy_packages(code, pack, 1, space);
+}
+
+static int audio_to_package(struct go7007 *go, u16 *code, int space)
+{
+	int clock_config = ((go->board_info->audio_flags &
+				GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) |
+			((go->board_info->audio_flags &
+				GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) |
+			(((go->board_info->audio_bclk_div / 4) - 1) << 4) |
+			(go->board_info->audio_main_div - 1);
+	u16 pack[] = {
+		0x200d,		0,
+		0x9002,		0,
+		0x9002,		0,
+		0x9031,		0,
+		0x9032,		0,
+		0x9033,		0,
+		0x9034,		0,
+		0x9035,		0,
+		0x9036,		0,
+		0x9037,		0,
+		0x9040,		0,
+		0x9000,		clock_config,
+		0x9001,		(go->board_info->audio_flags & 0xffff) |
+					(1 << 9),
+		0x9000,		((go->board_info->audio_flags &
+						GO7007_AUDIO_I2S_MASTER ?
+						1 : 0) << 10) |
+					clock_config,
+		0,		0,
+		0,		0,
+		0x2005,		0,
+		0x9041,		0,
+		0x9042,		256,
+		0x9043,		0,
+		0x9044,		16,
+		0x9045,		16,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+		0,		0,
+	};
+
+	return copy_packages(code, pack, 2, space);
+}
+
+static int modet_to_package(struct go7007 *go, u16 *code, int space)
+{
+	int ret, mb, i, addr, cnt = 0;
+	u16 pack[32];
+	u16 thresholds[] = {
+		0x200e,		0,
+		0xbf82,		go->modet[0].pixel_threshold,
+		0xbf83,		go->modet[1].pixel_threshold,
+		0xbf84,		go->modet[2].pixel_threshold,
+		0xbf85,		go->modet[3].pixel_threshold,
+		0xbf86,		go->modet[0].motion_threshold,
+		0xbf87,		go->modet[1].motion_threshold,
+		0xbf88,		go->modet[2].motion_threshold,
+		0xbf89,		go->modet[3].motion_threshold,
+		0xbf8a,		go->modet[0].mb_threshold,
+		0xbf8b,		go->modet[1].mb_threshold,
+		0xbf8c,		go->modet[2].mb_threshold,
+		0xbf8d,		go->modet[3].mb_threshold,
+		0xbf8e,		0,
+		0xbf8f,		0,
+		0,		0,
+	};
+
+	ret = copy_packages(code, thresholds, 1, space);
+	if (ret < 0)
+		return -1;
+	cnt += ret;
+
+	addr = 0xbac0;
+	memset(pack, 0, 64);
+	i = 0;
+	for (mb = 0; mb < 1624; ++mb) {
+		pack[i * 2 + 3] <<= 2;
+		pack[i * 2 + 3] |= go->modet_map[mb];
+		if (mb % 8 != 7)
+			continue;
+		pack[i * 2 + 2] = addr++;
+		++i;
+		if (i == 10 || mb == 1623) {
+			pack[0] = 0x2000 | i;
+			ret = copy_packages(code + cnt, pack, 1, space - cnt);
+			if (ret < 0)
+				return -1;
+			cnt += ret;
+			i = 0;
+			memset(pack, 0, 64);
+		}
+		pack[i * 2 + 3] = 0;
+	}
+
+	memset(pack, 0, 64);
+	i = 0;
+	for (addr = 0xbb90; addr < 0xbbfa; ++addr) {
+		pack[i * 2 + 2] = addr;
+		pack[i * 2 + 3] = 0;
+		++i;
+		if (i == 10 || addr == 0xbbf9) {
+			pack[0] = 0x2000 | i;
+			ret = copy_packages(code + cnt, pack, 1, space - cnt);
+			if (ret < 0)
+				return -1;
+			cnt += ret;
+			i = 0;
+			memset(pack, 0, 64);
+		}
+	}
+	return cnt;
+}
+
+static int do_special(struct go7007 *go, u16 type, u16 *code, int space,
+			int *framelen)
+{
+	switch (type) {
+	case SPECIAL_FRM_HEAD:
+		switch (go->format) {
+		case GO7007_FORMAT_MJPEG:
+			return gen_mjpeghdr_to_package(go, code, space);
+		case GO7007_FORMAT_MPEG1:
+		case GO7007_FORMAT_MPEG2:
+			return gen_mpeg1hdr_to_package(go, code, space,
+								framelen);
+		case GO7007_FORMAT_MPEG4:
+			return gen_mpeg4hdr_to_package(go, code, space,
+								framelen);
+		}
+	case SPECIAL_BRC_CTRL:
+		return brctrl_to_package(go, code, space, framelen);
+	case SPECIAL_CONFIG:
+		return config_package(go, code, space);
+	case SPECIAL_SEQHEAD:
+		switch (go->format) {
+		case GO7007_FORMAT_MPEG1:
+		case GO7007_FORMAT_MPEG2:
+			return seqhead_to_package(go, code, space,
+					mpeg1_sequence_header);
+		case GO7007_FORMAT_MPEG4:
+			return seqhead_to_package(go, code, space,
+					mpeg4_sequence_header);
+		default:
+			return 0;
+		}
+	case SPECIAL_AV_SYNC:
+		return avsync_to_package(go, code, space);
+	case SPECIAL_FINAL:
+		return final_package(go, code, space);
+	case SPECIAL_AUDIO:
+		return audio_to_package(go, code, space);
+	case SPECIAL_MODET:
+		return modet_to_package(go, code, space);
+	}
+	printk(KERN_ERR
+		"go7007: firmware file contains unsupported feature %04x\n",
+		type);
+	return -1;
+}
+
+int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen)
+{
+	const struct firmware *fw_entry;
+	u16 *code, *src;
+	int framelen[8] = { }; /* holds the lengths of empty frame templates */
+	int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags;
+	int mode_flag;
+	int ret;
+
+	switch (go->format) {
+	case GO7007_FORMAT_MJPEG:
+		mode_flag = FLAG_MODE_MJPEG;
+		break;
+	case GO7007_FORMAT_MPEG1:
+		mode_flag = FLAG_MODE_MPEG1;
+		break;
+	case GO7007_FORMAT_MPEG2:
+		mode_flag = FLAG_MODE_MPEG2;
+		break;
+	case GO7007_FORMAT_MPEG4:
+		mode_flag = FLAG_MODE_MPEG4;
+		break;
+	default:
+		return -1;
+	}
+	if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) {
+		printk(KERN_ERR
+			"go7007: unable to load firmware from file \"%s\"\n",
+			go->board_info->firmware);
+		return -1;
+	}
+	code = kmalloc(codespace * 2, GFP_KERNEL);
+	if (code == NULL) {
+		printk(KERN_ERR "go7007: unable to allocate %d bytes for "
+				"firmware construction\n", codespace * 2);
+		goto fw_failed;
+	}
+	memset(code, 0, codespace * 2);
+	src = (u16 *)fw_entry->data;
+	srclen = fw_entry->size / 2;
+	while (srclen >= 2) {
+		chunk_flags = __le16_to_cpu(src[0]);
+		chunk_len = __le16_to_cpu(src[1]);
+		if (chunk_len + 2 > srclen) {
+			printk(KERN_ERR "go7007: firmware file \"%s\" "
+					"appears to be corrupted\n",
+					go->board_info->firmware);
+			goto fw_failed;
+		}
+		if (chunk_flags & mode_flag) {
+			if (chunk_flags & FLAG_SPECIAL) {
+				ret = do_special(go, __le16_to_cpu(src[2]),
+					&code[i], codespace - i, framelen);
+				if (ret < 0) {
+					printk(KERN_ERR "go7007: insufficient "
+							"memory for firmware "
+							"construction\n");
+					goto fw_failed;
+				}
+				i += ret;
+			} else {
+				if (codespace - i < chunk_len) {
+					printk(KERN_ERR "go7007: insufficient "
+							"memory for firmware "
+							"construction\n");
+					goto fw_failed;
+				}
+				memcpy(&code[i], &src[2], chunk_len * 2);
+				i += chunk_len;
+			}
+		}
+		srclen -= chunk_len + 2;
+		src += chunk_len + 2;
+	}
+	release_firmware(fw_entry);
+	*fw = (u8 *)code;
+	*fwlen = i * 2;
+	return 0;
+
+fw_failed:
+	kfree(code);
+	release_firmware(fw_entry);
+	return -1;
+}
diff --git a/drivers/staging/go7007/go7007-i2c.c b/drivers/staging/go7007/go7007-i2c.c
new file mode 100644
index 0000000..10baae3
--- /dev/null
+++ b/drivers/staging/go7007/go7007-i2c.c
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/version.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+/************** Registration interface for I2C client drivers **************/
+
+/* Since there's no way to auto-probe the I2C devices connected to the I2C
+ * bus on the go7007, we have this silly little registration system that
+ * client drivers can use to register their I2C driver ID and their
+ * detect_client function (the one that's normally passed to i2c_probe).
+ *
+ * When a new go7007 device is connected, we can look up in a board info
+ * table by the USB or PCI vendor/product/revision ID to determine
+ * which I2C client module to load.  The client driver module will register
+ * itself here, and then we can call the registered detect_client function
+ * to force-load a new client at the address listed in the board info table.
+ *
+ * Really the I2C subsystem should have a way to force-load I2C client
+ * drivers when we have a priori knowledge of what's on the bus, especially
+ * since the existing I2C auto-probe mechanism is so hokey, but we'll use
+ * our own mechanism for the time being. */
+
+struct wis_i2c_client_driver {
+	unsigned int id;
+	found_proc found_proc;
+	struct list_head list;
+};
+
+static LIST_HEAD(i2c_client_drivers);
+static DECLARE_MUTEX(i2c_client_driver_list_lock);
+
+/* Client drivers register here by their I2C driver ID */
+int wis_i2c_add_driver(unsigned int id, found_proc found_proc)
+{
+	struct wis_i2c_client_driver *driver;
+
+	driver = kmalloc(sizeof(struct wis_i2c_client_driver), GFP_KERNEL);
+	if (driver == NULL)
+		return -ENOMEM;
+	driver->id = id;
+	driver->found_proc = found_proc;
+
+	down(&i2c_client_driver_list_lock);
+	list_add_tail(&driver->list, &i2c_client_drivers);
+	up(&i2c_client_driver_list_lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(wis_i2c_add_driver);
+
+void wis_i2c_del_driver(found_proc found_proc)
+{
+	struct wis_i2c_client_driver *driver, *next;
+
+	down(&i2c_client_driver_list_lock);
+	list_for_each_entry_safe(driver, next, &i2c_client_drivers, list)
+		if (driver->found_proc == found_proc) {
+			list_del(&driver->list);
+			kfree(driver);
+		}
+	up(&i2c_client_driver_list_lock);
+}
+EXPORT_SYMBOL(wis_i2c_del_driver);
+
+/* The main go7007 driver calls this to instantiate a client by driver
+ * ID and bus address, which are both stored in the board info table */
+int wis_i2c_probe_device(struct i2c_adapter *adapter,
+				unsigned int id, int addr)
+{
+	struct wis_i2c_client_driver *driver;
+	int found = 0;
+
+	if (addr < 0 || addr > 0x7f)
+		return -1;
+	down(&i2c_client_driver_list_lock);
+	list_for_each_entry(driver, &i2c_client_drivers, list)
+		if (driver->id == id) {
+			if (driver->found_proc(adapter, addr, 0) == 0)
+				found = 1;
+			break;
+		}
+	up(&i2c_client_driver_list_lock);
+	return found;
+}
+
+/********************* Driver for on-board I2C adapter *********************/
+
+/* #define GO7007_I2C_DEBUG */
+
+#define SPI_I2C_ADDR_BASE		0x1400
+#define STATUS_REG_ADDR			(SPI_I2C_ADDR_BASE + 0x2)
+#define I2C_CTRL_REG_ADDR		(SPI_I2C_ADDR_BASE + 0x6)
+#define I2C_DEV_UP_ADDR_REG_ADDR	(SPI_I2C_ADDR_BASE + 0x7)
+#define I2C_LO_ADDR_REG_ADDR		(SPI_I2C_ADDR_BASE + 0x8)
+#define I2C_DATA_REG_ADDR		(SPI_I2C_ADDR_BASE + 0x9)
+#define I2C_CLKFREQ_REG_ADDR		(SPI_I2C_ADDR_BASE + 0xa)
+
+#define I2C_STATE_MASK			0x0007
+#define I2C_READ_READY_MASK		0x0008
+
+/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs
+ * on the Adlink PCI-MPG24, so access is shared between all of them. */
+static DECLARE_MUTEX(adlink_mpg24_i2c_lock);
+
+static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read,
+		u16 command, int flags, u8 *data)
+{
+	int i, ret = -1;
+	u16 val;
+
+	if (go->status == STATUS_SHUTDOWN)
+		return -1;
+
+#ifdef GO7007_I2C_DEBUG
+	if (read)
+		printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n",
+			command, addr);
+	else
+		printk(KERN_DEBUG
+			"go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n",
+			*data, command, addr);
+#endif
+
+	down(&go->hw_lock);
+
+	if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
+		/* Bridge the I2C port on this GO7007 to the shared bus */
+		down(&adlink_mpg24_i2c_lock);
+		go7007_write_addr(go, 0x3c82, 0x0020);
+	}
+
+	/* Wait for I2C adapter to be ready */
+	for (i = 0; i < 10; ++i) {
+		if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
+			goto i2c_done;
+		if (!(val & I2C_STATE_MASK))
+			break;
+		msleep(100);
+	}
+	if (i == 10) {
+		printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
+		goto i2c_done;
+	}
+
+	/* Set target register (command) */
+	go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags);
+	go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command);
+
+	/* If we're writing, send the data and target address and we're done */
+	if (!read) {
+		go7007_write_addr(go, I2C_DATA_REG_ADDR, *data);
+		go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
+					(addr << 9) | (command >> 8));
+		ret = 0;
+		goto i2c_done;
+	}
+
+	/* Otherwise, we're reading.  First clear i2c_rx_data_rdy. */
+	if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
+		goto i2c_done;
+
+	/* Send the target address plus read flag */
+	go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR,
+			(addr << 9) | 0x0100 | (command >> 8));
+
+	/* Wait for i2c_rx_data_rdy */
+	for (i = 0; i < 10; ++i) {
+		if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0)
+			goto i2c_done;
+		if (val & I2C_READ_READY_MASK)
+			break;
+		msleep(100);
+	}
+	if (i == 10) {
+		printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n");
+		goto i2c_done;
+	}
+
+	/* Retrieve the read byte */
+	if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0)
+		goto i2c_done;
+	*data = val;
+	ret = 0;
+
+i2c_done:
+	if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) {
+		/* Isolate the I2C port on this GO7007 from the shared bus */
+		go7007_write_addr(go, 0x3c82, 0x0000);
+		up(&adlink_mpg24_i2c_lock);
+	}
+	up(&go->hw_lock);
+	return ret;
+}
+
+static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+		unsigned short flags, char read_write,
+		u8 command, int size, union i2c_smbus_data *data)
+{
+	struct go7007 *go = i2c_get_adapdata(adapter);
+
+	if (size != I2C_SMBUS_BYTE_DATA)
+		return -1;
+	return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command,
+			flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte);
+}
+
+/* VERY LIMITED I2C master xfer function -- only needed because the
+ * SMBus functions only support 8-bit commands and the SAA7135 uses
+ * 16-bit commands.  The I2C interface on the GO7007, as limited as
+ * it is, does support this mode. */
+
+static int go7007_i2c_master_xfer(struct i2c_adapter *adapter,
+					struct i2c_msg msgs[], int num)
+{
+	struct go7007 *go = i2c_get_adapdata(adapter);
+	int i;
+
+	for (i = 0; i < num; ++i) {
+		/* We can only do two things here -- write three bytes, or
+		 * write two bytes and read one byte. */
+		if (msgs[i].len == 2) {
+			if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr ||
+					(msgs[i].flags & I2C_M_RD) ||
+					!(msgs[i + 1].flags & I2C_M_RD) ||
+					msgs[i + 1].len != 1)
+				return -1;
+			if (go7007_i2c_xfer(go, msgs[i].addr, 1,
+					(msgs[i].buf[0] << 8) | msgs[i].buf[1],
+					0x01, &msgs[i + 1].buf[0]) < 0)
+				return -1;
+			++i;
+		} else if (msgs[i].len == 3) {
+			if (msgs[i].flags & I2C_M_RD)
+				return -1;
+			if (msgs[i].len != 3)
+				return -1;
+			if (go7007_i2c_xfer(go, msgs[i].addr, 0,
+					(msgs[i].buf[0] << 8) | msgs[i].buf[1],
+					0x01, &msgs[i].buf[2]) < 0)
+				return -1;
+		} else
+			return -1;
+	}
+
+	return 0;
+}
+
+static u32 go7007_functionality(struct i2c_adapter *adapter)
+{
+	return I2C_FUNC_SMBUS_BYTE_DATA;
+}
+
+static struct i2c_algorithm go7007_algo = {
+	.smbus_xfer	= go7007_smbus_xfer,
+	.master_xfer	= go7007_i2c_master_xfer,
+	.functionality	= go7007_functionality,
+};
+
+static struct i2c_adapter go7007_adap_templ = {
+	.owner			= THIS_MODULE,
+	.class			= I2C_CLASS_TV_ANALOG,
+	.name			= "WIS GO7007SB",
+	.id			= I2C_ALGO_GO7007,
+	.algo			= &go7007_algo,
+};
+
+int go7007_i2c_init(struct go7007 *go)
+{
+	memcpy(&go->i2c_adapter, &go7007_adap_templ,
+			sizeof(go7007_adap_templ));
+	go->i2c_adapter.dev.parent = go->dev;
+	i2c_set_adapdata(&go->i2c_adapter, go);
+	if (i2c_add_adapter(&go->i2c_adapter) < 0) {
+		printk(KERN_ERR
+			"go7007-i2c: error: i2c_add_adapter failed\n");
+		return -1;
+	}
+	return 0;
+}
diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/go7007/go7007-priv.h
new file mode 100644
index 0000000..005542d
--- /dev/null
+++ b/drivers/staging/go7007/go7007-priv.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+/*
+ * This is the private include file for the go7007 driver.  It should not
+ * be included by anybody but the driver itself, and especially not by
+ * user-space applications.
+ */
+
+struct go7007;
+
+/* IDs to activate board-specific support code */
+#define GO7007_BOARDID_MATRIX_II	0
+#define GO7007_BOARDID_MATRIX_RELOAD	1
+#define GO7007_BOARDID_STAR_TREK	2
+#define GO7007_BOARDID_PCI_VOYAGER	3
+#define GO7007_BOARDID_XMEN		4
+#define GO7007_BOARDID_XMEN_II		5
+#define GO7007_BOARDID_XMEN_III		6
+#define GO7007_BOARDID_MATRIX_REV	7
+#define GO7007_BOARDID_PX_M402U		16
+#define GO7007_BOARDID_PX_TV402U_ANY	17 /* need to check tuner model */
+#define GO7007_BOARDID_PX_TV402U_NA	18 /* detected NTSC tuner */
+#define GO7007_BOARDID_PX_TV402U_EU	19 /* detected PAL tuner */
+#define GO7007_BOARDID_PX_TV402U_JP	20 /* detected NTSC-J tuner */
+#define GO7007_BOARDID_LIFEVIEW_LR192	21 /* TV Walker Ultra */
+#define GO7007_BOARDID_ENDURA		22
+#define GO7007_BOARDID_ADLINK_MPG24	23
+
+/* Various characteristics of each board */
+#define GO7007_BOARD_HAS_AUDIO		(1<<0)
+#define GO7007_BOARD_USE_ONBOARD_I2C	(1<<1)
+#define GO7007_BOARD_HAS_TUNER		(1<<2)
+
+/* Characteristics of sensor devices */
+#define GO7007_SENSOR_VALID_POLAR	(1<<0)
+#define GO7007_SENSOR_HREF_POLAR	(1<<1)
+#define GO7007_SENSOR_VREF_POLAR	(1<<2)
+#define GO7007_SENSOR_FIELD_ID_POLAR	(1<<3)
+#define GO7007_SENSOR_BIT_WIDTH		(1<<4)
+#define GO7007_SENSOR_VALID_ENABLE	(1<<5)
+#define GO7007_SENSOR_656		(1<<6)
+#define GO7007_SENSOR_CONFIG_MASK	0x7f
+#define GO7007_SENSOR_TV		(1<<7)
+#define GO7007_SENSOR_VBI		(1<<8)
+#define GO7007_SENSOR_SCALING		(1<<9)
+
+/* Characteristics of audio sensor devices */
+#define GO7007_AUDIO_I2S_MODE_1		(1)
+#define GO7007_AUDIO_I2S_MODE_2		(2)
+#define GO7007_AUDIO_I2S_MODE_3		(3)
+#define GO7007_AUDIO_BCLK_POLAR		(1<<2)
+#define GO7007_AUDIO_WORD_14		(14<<4)
+#define GO7007_AUDIO_WORD_16		(16<<4)
+#define GO7007_AUDIO_ONE_CHANNEL	(1<<11)
+#define GO7007_AUDIO_I2S_MASTER		(1<<16)
+#define GO7007_AUDIO_OKI_MODE		(1<<17)
+
+struct go7007_board_info {
+	char *firmware;
+	unsigned int flags;
+	int hpi_buffer_cap;
+	unsigned int sensor_flags;
+	int sensor_width;
+	int sensor_height;
+	int sensor_framerate;
+	int sensor_h_offset;
+	int sensor_v_offset;
+	unsigned int audio_flags;
+	int audio_rate;
+	int audio_bclk_div;
+	int audio_main_div;
+	int num_i2c_devs;
+	struct {
+		int id;
+		int addr;
+	} i2c_devs[4];
+	int num_inputs;
+	struct {
+		int video_input;
+		int audio_input;
+		char *name;
+	} inputs[4];
+};
+
+struct go7007_hpi_ops {
+	int (*interface_reset)(struct go7007 *go);
+	int (*write_interrupt)(struct go7007 *go, int addr, int data);
+	int (*read_interrupt)(struct go7007 *go);
+	int (*stream_start)(struct go7007 *go);
+	int (*stream_stop)(struct go7007 *go);
+	int (*send_firmware)(struct go7007 *go, u8 *data, int len);
+};
+
+/* The video buffer size must be a multiple of PAGE_SIZE */
+#define	GO7007_BUF_PAGES	(128 * 1024 / PAGE_SIZE)
+#define	GO7007_BUF_SIZE		(GO7007_BUF_PAGES << PAGE_SHIFT)
+
+struct go7007_buffer {
+	struct go7007 *go; /* Reverse reference for VMA ops */
+	int index; /* Reverse reference for DQBUF */
+	enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state;
+	u32 seq;
+	struct timeval timestamp;
+	struct list_head stream;
+	struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */
+	unsigned long user_addr;
+	unsigned int page_count;
+	unsigned int offset;
+	unsigned int bytesused;
+	unsigned int frame_offset;
+	u32 modet_active;
+	int mapped;
+};
+
+struct go7007_file {
+	struct go7007 *go;
+	struct semaphore lock;
+	int buf_count;
+	struct go7007_buffer *bufs;
+};
+
+#define	GO7007_FORMAT_MJPEG	0
+#define GO7007_FORMAT_MPEG4	1
+#define GO7007_FORMAT_MPEG1	2
+#define GO7007_FORMAT_MPEG2	3
+#define GO7007_FORMAT_H263	4
+
+#define GO7007_RATIO_1_1	0
+#define GO7007_RATIO_4_3	1
+#define GO7007_RATIO_16_9	2
+
+enum go7007_parser_state {
+	STATE_DATA,
+	STATE_00,
+	STATE_00_00,
+	STATE_00_00_01,
+	STATE_FF,
+	STATE_VBI_LEN_A,
+	STATE_VBI_LEN_B,
+	STATE_MODET_MAP,
+	STATE_UNPARSED,
+};
+
+struct go7007 {
+	struct device *dev;
+	struct go7007_board_info *board_info;
+	unsigned int board_id;
+	int tuner_type;
+	int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */
+	char name[64];
+	struct video_device *video_dev;
+	int ref_count;
+	enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status;
+	spinlock_t spinlock;
+	struct semaphore hw_lock;
+	int streaming;
+	int in_use;
+	int audio_enabled;
+
+	/* Video input */
+	int input;
+	enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard;
+	int sensor_framerate;
+	int width;
+	int height;
+	int encoder_h_offset;
+	int encoder_v_offset;
+	unsigned int encoder_h_halve:1;
+	unsigned int encoder_v_halve:1;
+	unsigned int encoder_subsample:1;
+
+	/* Encoder config */
+	int format;
+	int bitrate;
+	int fps_scale;
+	int pali;
+	int aspect_ratio;
+	int gop_size;
+	unsigned int ipb:1;
+	unsigned int closed_gop:1;
+	unsigned int repeat_seqhead:1;
+	unsigned int seq_header_enable:1;
+	unsigned int gop_header_enable:1;
+	unsigned int dvd_mode:1;
+	unsigned int interlace_coding:1;
+
+	/* Motion detection */
+	unsigned int modet_enable:1;
+	struct {
+		unsigned int enable:1;
+		int pixel_threshold;
+		int motion_threshold;
+		int mb_threshold;
+	} modet[4];
+	unsigned char modet_map[1624];
+	unsigned char active_map[216];
+
+	/* Video streaming */
+	struct go7007_buffer *active_buf;
+	enum go7007_parser_state state;
+	int parse_length;
+	u16 modet_word;
+	int seen_frame;
+	u32 next_seq;
+	struct list_head stream;
+	wait_queue_head_t frame_waitq;
+
+	/* Audio streaming */
+	void (*audio_deliver)(struct go7007 *go, u8 *buf, int length);
+	void *snd_context;
+
+	/* I2C */
+	int i2c_adapter_online;
+	struct i2c_adapter i2c_adapter;
+
+	/* HPI driver */
+	struct go7007_hpi_ops *hpi_ops;
+	void *hpi_context;
+	int interrupt_available;
+	wait_queue_head_t interrupt_waitq;
+	unsigned short interrupt_value;
+	unsigned short interrupt_data;
+};
+
+/* All of these must be called with the hpi_lock semaphore held! */
+#define go7007_interface_reset(go) \
+			((go)->hpi_ops->interface_reset(go))
+#define	go7007_write_interrupt(go, x, y) \
+			((go)->hpi_ops->write_interrupt)((go), (x), (y))
+#define go7007_stream_start(go) \
+			((go)->hpi_ops->stream_start(go))
+#define go7007_stream_stop(go) \
+			((go)->hpi_ops->stream_stop(go))
+#define	go7007_send_firmware(go, x, y) \
+			((go)->hpi_ops->send_firmware)((go), (x), (y))
+#define go7007_write_addr(go, x, y) \
+			((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y))
+
+/* go7007-driver.c */
+int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data);
+int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data);
+int go7007_boot_encoder(struct go7007 *go, int init_i2c);
+int go7007_reset_encoder(struct go7007 *go);
+int go7007_register_encoder(struct go7007 *go);
+int go7007_start_encoder(struct go7007 *go);
+void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length);
+struct go7007 *go7007_alloc(struct go7007_board_info *board,
+					struct device *dev);
+void go7007_remove(struct go7007 *go);
+
+/* go7007-fw.c */
+int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen);
+
+/* go7007-i2c.c */
+int go7007_i2c_init(struct go7007 *go);
+int go7007_i2c_remove(struct go7007 *go);
+
+/* go7007-v4l2.c */
+int go7007_v4l2_init(struct go7007 *go);
+void go7007_v4l2_remove(struct go7007 *go);
+
+/* snd-go7007.c */
+int go7007_snd_init(struct go7007 *go);
+int go7007_snd_remove(struct go7007 *go);
diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c
new file mode 100644
index 0000000..d4ed6d2
--- /dev/null
+++ b/drivers/staging/go7007/go7007-usb.c
@@ -0,0 +1,1229 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <asm/byteorder.h>
+#include <media/tvaudio.h>
+
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+static unsigned int assume_endura;
+module_param(assume_endura, int, 0644);
+MODULE_PARM_DESC(assume_endura, "when probing fails, hardware is a Pelco Endura");
+
+/* #define GO7007_USB_DEBUG */
+/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */
+
+#define	HPI_STATUS_ADDR	0xFFF4
+#define	INT_PARAM_ADDR	0xFFF6
+#define	INT_INDEX_ADDR	0xFFF8
+
+/*
+ * Pipes on EZ-USB interface:
+ * 	0 snd - Control
+ * 	0 rcv - Control
+ * 	2 snd - Download firmware (control)
+ * 	4 rcv - Read Interrupt (interrupt)
+ * 	6 rcv - Read Video (bulk)
+ * 	8 rcv - Read Audio (bulk)
+ */
+
+#define GO7007_USB_EZUSB		(1<<0)
+#define GO7007_USB_EZUSB_I2C		(1<<1)
+
+struct go7007_usb_board {
+	unsigned int flags;
+	struct go7007_board_info main_info;
+};
+
+struct go7007_usb {
+	struct go7007_usb_board *board;
+	struct semaphore i2c_lock;
+	struct usb_device *usbdev;
+	struct urb *video_urbs[8];
+	struct urb *audio_urbs[8];
+	struct urb *intr_urb;
+};
+
+/*********************** Product specification data ***********************/
+
+static struct go7007_usb_board board_matrix_ii = {
+	.flags		= GO7007_USB_EZUSB,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_HAS_AUDIO |
+					GO7007_BOARD_USE_ONBOARD_I2C,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_WORD_16,
+		.audio_rate	 = 48000,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 7,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_VALID_ENABLE |
+					GO7007_SENSOR_TV |
+					GO7007_SENSOR_VBI |
+					GO7007_SENSOR_SCALING,
+		.num_i2c_devs	 = 1,
+		.i2c_devs	 = {
+			{
+				.id	= I2C_DRIVERID_WIS_SAA7115,
+				.addr	= 0x20,
+			},
+		},
+		.num_inputs	 = 2,
+		.inputs 	 = {
+			{
+				.video_input	= 0,
+				.name		= "Composite",
+			},
+			{
+				.video_input	= 9,
+				.name		= "S-Video",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_matrix_reload = {
+	.flags		= GO7007_USB_EZUSB,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_HAS_AUDIO |
+					GO7007_BOARD_USE_ONBOARD_I2C,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_I2S_MASTER |
+					GO7007_AUDIO_WORD_16,
+		.audio_rate	 = 48000,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 7,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_TV,
+		.num_i2c_devs	 = 1,
+		.i2c_devs	 = {
+			{
+				.id	= I2C_DRIVERID_WIS_SAA7113,
+				.addr	= 0x25,
+			},
+		},
+		.num_inputs	 = 2,
+		.inputs 	 = {
+			{
+				.video_input	= 0,
+				.name		= "Composite",
+			},
+			{
+				.video_input	= 9,
+				.name		= "S-Video",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_star_trek = {
+	.flags		= GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_HAS_AUDIO, /* |
+					GO7007_BOARD_HAS_TUNER, */
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_VALID_ENABLE |
+					GO7007_SENSOR_TV |
+					GO7007_SENSOR_VBI |
+					GO7007_SENSOR_SCALING,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_WORD_16,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 7,
+		.num_i2c_devs	 = 1,
+		.i2c_devs	 = {
+			{
+				.id	= I2C_DRIVERID_WIS_SAA7115,
+				.addr	= 0x20,
+			},
+		},
+		.num_inputs	 = 2,
+		.inputs 	 = {
+			{
+				.video_input	= 1,
+			/*	.audio_input	= AUDIO_EXTERN, */
+				.name		= "Composite",
+			},
+			{
+				.video_input	= 8,
+			/*	.audio_input	= AUDIO_EXTERN, */
+				.name		= "S-Video",
+			},
+		/*	{
+		 *		.video_input	= 3,
+		 *		.audio_input	= AUDIO_TUNER,
+		 *		.name		= "Tuner",
+		 *	},
+		 */
+		},
+	},
+};
+
+static struct go7007_usb_board board_px_tv402u = {
+	.flags		= GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_HAS_AUDIO |
+					GO7007_BOARD_HAS_TUNER,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_VALID_ENABLE |
+					GO7007_SENSOR_TV |
+					GO7007_SENSOR_VBI |
+					GO7007_SENSOR_SCALING,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_WORD_16,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 7,
+		.num_i2c_devs	 = 3,
+		.i2c_devs	 = {
+			{
+				.id	= I2C_DRIVERID_WIS_SAA7115,
+				.addr	= 0x20,
+			},
+			{
+				.id	= I2C_DRIVERID_WIS_UDA1342,
+				.addr	= 0x1a,
+			},
+			{
+				.id	= I2C_DRIVERID_WIS_SONY_TUNER,
+				.addr	= 0x60,
+			},
+		},
+		.num_inputs	 = 3,
+		.inputs 	 = {
+			{
+				.video_input	= 1,
+		.audio_input	 = TVAUDIO_INPUT_EXTERN,
+				.name		= "Composite",
+			},
+			{
+				.video_input	= 8,
+				.audio_input	= TVAUDIO_INPUT_EXTERN,
+				.name		= "S-Video",
+			},
+			{
+				.video_input	= 3,
+				.audio_input	= TVAUDIO_INPUT_TUNER,
+				.name		= "Tuner",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_xmen = {
+	.flags		= 0,
+	.main_info	= {
+		.firmware	  = "go7007tv.bin",
+		.flags		  = GO7007_BOARD_USE_ONBOARD_I2C,
+		.hpi_buffer_cap   = 0,
+		.sensor_flags	  = GO7007_SENSOR_VREF_POLAR,
+		.sensor_width	  = 320,
+		.sensor_height	  = 240,
+		.sensor_framerate = 30030,
+		.audio_flags	  = GO7007_AUDIO_ONE_CHANNEL |
+					GO7007_AUDIO_I2S_MODE_3 |
+					GO7007_AUDIO_WORD_14 |
+					GO7007_AUDIO_I2S_MASTER |
+					GO7007_AUDIO_BCLK_POLAR |
+					GO7007_AUDIO_OKI_MODE,
+		.audio_rate	  = 8000,
+		.audio_bclk_div	  = 48,
+		.audio_main_div	  = 1,
+		.num_i2c_devs	  = 1,
+		.i2c_devs	  = {
+			{
+				.id	= I2C_DRIVERID_WIS_OV7640,
+				.addr	= 0x21,
+			},
+		},
+		.num_inputs	  = 1,
+		.inputs 	  = {
+			{
+				.name		= "Camera",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_matrix_revolution = {
+	.flags		= GO7007_USB_EZUSB,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_HAS_AUDIO |
+					GO7007_BOARD_USE_ONBOARD_I2C,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_I2S_MASTER |
+					GO7007_AUDIO_WORD_16,
+		.audio_rate	 = 48000,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 7,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_TV |
+					GO7007_SENSOR_VBI,
+		.num_i2c_devs	 = 1,
+		.i2c_devs	 = {
+			{
+				.id	= I2C_DRIVERID_WIS_TW9903,
+				.addr	= 0x44,
+			},
+		},
+		.num_inputs	 = 2,
+		.inputs 	 = {
+			{
+				.video_input	= 2,
+				.name		= "Composite",
+			},
+			{
+				.video_input	= 8,
+				.name		= "S-Video",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_lifeview_lr192 = {
+	.flags		= GO7007_USB_EZUSB,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_HAS_AUDIO |
+					GO7007_BOARD_USE_ONBOARD_I2C,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_WORD_16,
+		.audio_rate	 = 48000,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 7,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_VALID_ENABLE |
+					GO7007_SENSOR_TV |
+					GO7007_SENSOR_VBI |
+					GO7007_SENSOR_SCALING,
+		.num_i2c_devs	 = 0,
+		.num_inputs	 = 1,
+		.inputs 	 = {
+			{
+				.video_input	= 0,
+				.name		= "Composite",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_endura = {
+	.flags		= 0,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = 0,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_I2S_MASTER |
+					GO7007_AUDIO_WORD_16,
+		.audio_rate	 = 8000,
+		.audio_bclk_div	 = 48,
+		.audio_main_div	 = 8,
+		.hpi_buffer_cap  = 0,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_TV,
+		.sensor_h_offset = 8,
+		.num_i2c_devs	 = 0,
+		.num_inputs	 = 1,
+		.inputs 	 = {
+			{
+				.name		= "Camera",
+			},
+		},
+	},
+};
+
+static struct go7007_usb_board board_adlink_mpg24 = {
+	.flags		= 0,
+	.main_info	= {
+		.firmware	 = "go7007tv.bin",
+		.flags		 = GO7007_BOARD_USE_ONBOARD_I2C,
+		.audio_flags	 = GO7007_AUDIO_I2S_MODE_1 |
+					GO7007_AUDIO_I2S_MASTER |
+					GO7007_AUDIO_WORD_16,
+		.audio_rate	 = 48000,
+		.audio_bclk_div	 = 8,
+		.audio_main_div	 = 2,
+		.hpi_buffer_cap  = 0,
+		.sensor_flags	 = GO7007_SENSOR_656 |
+					GO7007_SENSOR_TV |
+					GO7007_SENSOR_VBI,
+		.num_i2c_devs	 = 1,
+		.i2c_devs	 = {
+			{
+				.id	= I2C_DRIVERID_WIS_TW2804,
+				.addr	= 0x00, /* yes, really */
+			},
+		},
+		.num_inputs	 = 1,
+		.inputs 	 = {
+			{
+				.name		= "Composite",
+			},
+		},
+	},
+};
+
+static struct usb_device_id go7007_usb_id_table[] = {
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+					USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x200,   /* Revision number of XMen */
+		.bcdDevice_hi	= 0x200,
+		.bInterfaceClass	= 255,
+		.bInterfaceSubClass	= 0,
+		.bInterfaceProtocol	= 255,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_XMEN,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x202,   /* Revision number of Matrix II */
+		.bcdDevice_hi	= 0x202,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_MATRIX_II,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x204,   /* Revision number of Matrix */
+		.bcdDevice_hi	= 0x204,   /*     Reloaded */
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+					USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x205,   /* Revision number of XMen-II */
+		.bcdDevice_hi	= 0x205,
+		.bInterfaceClass	= 255,
+		.bInterfaceSubClass	= 0,
+		.bInterfaceProtocol	= 255,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_XMEN_II,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x208,   /* Revision number of Star Trek */
+		.bcdDevice_hi	= 0x208,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_STAR_TREK,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION |
+					USB_DEVICE_ID_MATCH_INT_INFO,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x209,   /* Revision number of XMen-III */
+		.bcdDevice_hi	= 0x209,
+		.bInterfaceClass	= 255,
+		.bInterfaceSubClass	= 0,
+		.bInterfaceProtocol	= 255,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_XMEN_III,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x0eb1,  /* Vendor ID of WIS Technologies */
+		.idProduct	= 0x7007,  /* Product ID of GO7007SB chip */
+		.bcdDevice_lo	= 0x210,   /* Revision number of Matrix */
+		.bcdDevice_hi	= 0x210,   /*     Revolution */
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x093b,  /* Vendor ID of Plextor */
+		.idProduct	= 0xa102,  /* Product ID of M402U */
+		.bcdDevice_lo	= 0x1,	   /* revision number of Blueberry */
+		.bcdDevice_hi	= 0x1,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_PX_M402U,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x093b,  /* Vendor ID of Plextor */
+		.idProduct	= 0xa104,  /* Product ID of TV402U */
+		.bcdDevice_lo	= 0x1,
+		.bcdDevice_hi	= 0x1,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY,
+	},
+	{
+		.match_flags	= USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION,
+		.idVendor	= 0x10fd,  /* Vendor ID of Anubis Electronics */
+		.idProduct	= 0xde00,  /* Product ID of Lifeview LR192 */
+		.bcdDevice_lo	= 0x1,
+		.bcdDevice_hi	= 0x1,
+		.driver_info	= (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192,
+	},
+	{ }					/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, go7007_usb_id_table);
+
+/********************* Driver for EZ-USB HPI interface *********************/
+
+static int go7007_usb_vendor_request(struct go7007 *go, int request,
+		int value, int index, void *transfer_buffer, int length, int in)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	int timeout = 5000;
+
+	if (in) {
+		return usb_control_msg(usb->usbdev,
+				usb_rcvctrlpipe(usb->usbdev, 0), request,
+				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+				value, index, transfer_buffer, length, timeout);
+	} else {
+		return usb_control_msg(usb->usbdev,
+				usb_sndctrlpipe(usb->usbdev, 0), request,
+				USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+				value, index, transfer_buffer, length, timeout);
+	}
+}
+
+static int go7007_usb_interface_reset(struct go7007 *go)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	u16 intr_val, intr_data;
+
+	/* Reset encoder */
+	if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
+		return -1;
+	msleep(100);
+
+	if (usb->board->flags & GO7007_USB_EZUSB) {
+		/* Reset buffer in EZ-USB */
+#ifdef GO7007_USB_DEBUG
+		printk(KERN_DEBUG "go7007-usb: resetting EZ-USB buffers\n");
+#endif
+		if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 ||
+		    go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0)
+			return -1;
+
+		/* Reset encoder again */
+		if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0)
+			return -1;
+		msleep(100);
+	}
+
+	/* Wait for an interrupt to indicate successful hardware reset */
+	if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+			(intr_val & ~0x1) != 0x55aa) {
+		printk(KERN_ERR
+			"go7007-usb: unable to reset the USB interface\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int go7007_usb_ezusb_write_interrupt(struct go7007 *go,
+						int addr, int data)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	int i, r;
+	u16 status_reg;
+	int timeout = 500;
+
+#ifdef GO7007_USB_DEBUG
+	printk(KERN_DEBUG
+		"go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+	for (i = 0; i < 100; ++i) {
+		r = usb_control_msg(usb->usbdev,
+				usb_rcvctrlpipe(usb->usbdev, 0), 0x14,
+				USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+				0, HPI_STATUS_ADDR, &status_reg,
+				sizeof(status_reg), timeout);
+		if (r < 0)
+			goto write_int_error;
+		__le16_to_cpus(&status_reg);
+		if (!(status_reg & 0x0010))
+			break;
+		msleep(10);
+	}
+	if (i == 100) {
+		printk(KERN_ERR
+			"go7007-usb: device is hung, status reg = 0x%04x\n",
+			status_reg);
+		return -1;
+	}
+	r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12,
+			USB_TYPE_VENDOR | USB_RECIP_DEVICE, data,
+			INT_PARAM_ADDR, NULL, 0, timeout);
+	if (r < 0)
+		goto write_int_error;
+	r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0),
+			0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr,
+			INT_INDEX_ADDR, NULL, 0, timeout);
+	if (r < 0)
+		goto write_int_error;
+	return 0;
+
+write_int_error:
+	printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
+	return r;
+}
+
+static int go7007_usb_onboard_write_interrupt(struct go7007 *go,
+						int addr, int data)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	u8 *tbuf;
+	int r;
+	int timeout = 500;
+
+#ifdef GO7007_USB_DEBUG
+	printk(KERN_DEBUG
+		"go7007-usb: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+	tbuf = kmalloc(8, GFP_KERNEL);
+	if (tbuf == NULL)
+		return -ENOMEM;
+	memset(tbuf, 0, 8);
+	tbuf[0] = data & 0xff;
+	tbuf[1] = data >> 8;
+	tbuf[2] = addr & 0xff;
+	tbuf[3] = addr >> 8;
+	r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00,
+			USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa,
+			0xf0f0, tbuf, 8, timeout);
+	kfree(tbuf);
+	if (r < 0) {
+		printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void go7007_usb_readinterrupt_complete(struct urb *urb)
+{
+	struct go7007 *go = (struct go7007 *)urb->context;
+	u16 *regs = (u16 *)urb->transfer_buffer;
+
+	if (urb->status != 0) {
+		if (urb->status != -ESHUTDOWN &&
+				go->status != STATUS_SHUTDOWN) {
+			printk(KERN_ERR
+				"go7007-usb: error in read interrupt: %d\n",
+				urb->status);
+		} else {
+			wake_up(&go->interrupt_waitq);
+			return;
+		}
+	} else if (urb->actual_length != urb->transfer_buffer_length) {
+		printk(KERN_ERR "go7007-usb: short read in interrupt pipe!\n");
+	} else {
+		go->interrupt_available = 1;
+		go->interrupt_data = __le16_to_cpu(regs[0]);
+		go->interrupt_value = __le16_to_cpu(regs[1]);
+#ifdef GO7007_USB_DEBUG
+		printk(KERN_DEBUG "go7007-usb: ReadInterrupt: %04x %04x\n",
+				go->interrupt_value, go->interrupt_data);
+#endif
+	}
+
+	wake_up(&go->interrupt_waitq);
+}
+
+static int go7007_usb_read_interrupt(struct go7007 *go)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	int r;
+
+	r = usb_submit_urb(usb->intr_urb, GFP_KERNEL);
+	if (r < 0) {
+		printk(KERN_ERR
+			"go7007-usb: unable to submit interrupt urb: %d\n", r);
+		return r;
+	}
+	return 0;
+}
+
+static void go7007_usb_read_video_pipe_complete(struct urb *urb)
+{
+	struct go7007 *go = (struct go7007 *)urb->context;
+	int r;
+
+	if (!go->streaming) {
+		wake_up_interruptible(&go->frame_waitq);
+		return;
+	}
+	if (urb->status != 0) {
+		printk(KERN_ERR "go7007-usb: error in video pipe: %d\n",
+				urb->status);
+		return;
+	}
+	if (urb->actual_length != urb->transfer_buffer_length) {
+		printk(KERN_ERR "go7007-usb: short read in video pipe!\n");
+		return;
+	}
+	go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length);
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r < 0)
+		printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", r);
+}
+
+static void go7007_usb_read_audio_pipe_complete(struct urb *urb)
+{
+	struct go7007 *go = (struct go7007 *)urb->context;
+	int r;
+
+	if (!go->streaming)
+		return;
+	if (urb->status != 0) {
+		printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n",
+				urb->status);
+		return;
+	}
+	if (urb->actual_length != urb->transfer_buffer_length) {
+		printk(KERN_ERR "go7007-usb: short read in audio pipe!\n");
+		return;
+	}
+	if (go->audio_deliver != NULL)
+		go->audio_deliver(go, urb->transfer_buffer, urb->actual_length);
+	r = usb_submit_urb(urb, GFP_ATOMIC);
+	if (r < 0)
+		printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", r);
+}
+
+static int go7007_usb_stream_start(struct go7007 *go)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	int i, r;
+
+	for (i = 0; i < 8; ++i) {
+		r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL);
+		if (r < 0) {
+			printk(KERN_ERR "go7007-usb: error submitting video "
+					"urb %d: %d\n", i, r);
+			goto video_submit_failed;
+		}
+	}
+	if (!go->audio_enabled)
+		return 0;
+
+	for (i = 0; i < 8; ++i) {
+		r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL);
+		if (r < 0) {
+			printk(KERN_ERR "go7007-usb: error submitting audio "
+					"urb %d: %d\n", i, r);
+			goto audio_submit_failed;
+		}
+	}
+	return 0;
+
+audio_submit_failed:
+	for (i = 0; i < 8; ++i)
+		usb_kill_urb(usb->audio_urbs[i]);
+video_submit_failed:
+	for (i = 0; i < 8; ++i)
+		usb_kill_urb(usb->video_urbs[i]);
+	return -1;
+}
+
+static int go7007_usb_stream_stop(struct go7007 *go)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	int i;
+
+	if (go->status == STATUS_SHUTDOWN)
+		return 0;
+	for (i = 0; i < 8; ++i)
+		usb_kill_urb(usb->video_urbs[i]);
+	if (go->audio_enabled)
+		for (i = 0; i < 8; ++i)
+			usb_kill_urb(usb->audio_urbs[i]);
+	return 0;
+}
+
+static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len)
+{
+	struct go7007_usb *usb = go->hpi_context;
+	int transferred, pipe;
+	int timeout = 500;
+
+#ifdef GO7007_USB_DEBUG
+	printk(KERN_DEBUG "go7007-usb: DownloadBuffer sending %d bytes\n", len);
+#endif
+
+	if (usb->board->flags & GO7007_USB_EZUSB)
+		pipe = usb_sndbulkpipe(usb->usbdev, 2);
+	else
+		pipe = usb_sndbulkpipe(usb->usbdev, 3);
+
+	return usb_bulk_msg(usb->usbdev, pipe, data, len,
+					&transferred, timeout);
+}
+
+static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = {
+	.interface_reset	= go7007_usb_interface_reset,
+	.write_interrupt	= go7007_usb_ezusb_write_interrupt,
+	.read_interrupt		= go7007_usb_read_interrupt,
+	.stream_start		= go7007_usb_stream_start,
+	.stream_stop		= go7007_usb_stream_stop,
+	.send_firmware		= go7007_usb_send_firmware,
+};
+
+static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = {
+	.interface_reset	= go7007_usb_interface_reset,
+	.write_interrupt	= go7007_usb_onboard_write_interrupt,
+	.read_interrupt		= go7007_usb_read_interrupt,
+	.stream_start		= go7007_usb_stream_start,
+	.stream_stop		= go7007_usb_stream_stop,
+	.send_firmware		= go7007_usb_send_firmware,
+};
+
+/********************* Driver for EZ-USB I2C adapter *********************/
+
+static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter,
+					struct i2c_msg msgs[], int num)
+{
+	struct go7007 *go = i2c_get_adapdata(adapter);
+	struct go7007_usb *usb = go->hpi_context;
+	u8 buf[16];
+	int buf_len, i;
+	int ret = -1;
+
+	if (go->status == STATUS_SHUTDOWN)
+		return -1;
+
+	down(&usb->i2c_lock);
+
+	for (i = 0; i < num; ++i) {
+		/* The hardware command is "write some bytes then read some
+		 * bytes", so we try to coalesce a write followed by a read
+		 * into a single USB transaction */
+		if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr &&
+				!(msgs[i].flags & I2C_M_RD) &&
+				(msgs[i + 1].flags & I2C_M_RD)) {
+#ifdef GO7007_I2C_DEBUG
+			printk(KERN_DEBUG "go7007-usb: i2c write/read %d/%d "
+					"bytes on %02x\n", msgs[i].len,
+					msgs[i + 1].len, msgs[i].addr);
+#endif
+			buf[0] = 0x01;
+			buf[1] = msgs[i].len + 1;
+			buf[2] = msgs[i].addr << 1;
+			memcpy(&buf[3], msgs[i].buf, msgs[i].len);
+			buf_len = msgs[i].len + 3;
+			buf[buf_len++] = msgs[++i].len;
+		} else if (msgs[i].flags & I2C_M_RD) {
+#ifdef GO7007_I2C_DEBUG
+			printk(KERN_DEBUG "go7007-usb: i2c read %d "
+					"bytes on %02x\n", msgs[i].len,
+					msgs[i].addr);
+#endif
+			buf[0] = 0x01;
+			buf[1] = 1;
+			buf[2] = msgs[i].addr << 1;
+			buf[3] = msgs[i].len;
+			buf_len = 4;
+		} else {
+#ifdef GO7007_I2C_DEBUG
+			printk(KERN_DEBUG "go7007-usb: i2c write %d "
+					"bytes on %02x\n", msgs[i].len,
+					msgs[i].addr);
+#endif
+			buf[0] = 0x00;
+			buf[1] = msgs[i].len + 1;
+			buf[2] = msgs[i].addr << 1;
+			memcpy(&buf[3], msgs[i].buf, msgs[i].len);
+			buf_len = msgs[i].len + 3;
+			buf[buf_len++] = 0;
+		}
+		if (go7007_usb_vendor_request(go, 0x24, 0, 0,
+						buf, buf_len, 0) < 0)
+			goto i2c_done;
+		if (msgs[i].flags & I2C_M_RD) {
+			memset(buf, 0, sizeof(buf));
+			if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf,
+						msgs[i].len + 1, 1) < 0)
+				goto i2c_done;
+			memcpy(msgs[i].buf, buf + 1, msgs[i].len);
+		}
+	}
+	ret = 0;
+
+i2c_done:
+	up(&usb->i2c_lock);
+	return ret;
+}
+
+static u32 go7007_usb_functionality(struct i2c_adapter *adapter)
+{
+	/* No errors are reported by the hardware, so we don't bother
+	 * supporting quick writes to avoid confusing probing */
+	return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK;
+}
+
+static struct i2c_algorithm go7007_usb_algo = {
+	.master_xfer	= go7007_usb_i2c_master_xfer,
+	.functionality	= go7007_usb_functionality,
+};
+
+static struct i2c_adapter go7007_usb_adap_templ = {
+	.owner			= THIS_MODULE,
+	.class			= I2C_CLASS_TV_ANALOG,
+	.name			= "WIS GO7007SB EZ-USB",
+	.id			= I2C_ALGO_GO7007_USB,
+	.algo			= &go7007_usb_algo,
+};
+
+/********************* USB add/remove functions *********************/
+
+static int go7007_usb_probe(struct usb_interface *intf,
+		const struct usb_device_id *id)
+{
+	struct go7007 *go;
+	struct go7007_usb *usb;
+	struct go7007_usb_board *board;
+	struct usb_device *usbdev = interface_to_usbdev(intf);
+	char *name;
+	int video_pipe, i, v_urb_len;
+
+	printk(KERN_DEBUG "go7007-usb: probing new GO7007 USB board\n");
+
+	switch (id->driver_info) {
+	case GO7007_BOARDID_MATRIX_II:
+		name = "WIS Matrix II or compatible";
+		board = &board_matrix_ii;
+		break;
+	case GO7007_BOARDID_MATRIX_RELOAD:
+		name = "WIS Matrix Reloaded or compatible";
+		board = &board_matrix_reload;
+		break;
+	case GO7007_BOARDID_MATRIX_REV:
+		name = "WIS Matrix Revolution or compatible";
+		board = &board_matrix_revolution;
+		break;
+	case GO7007_BOARDID_STAR_TREK:
+		name = "WIS Star Trek or compatible";
+		board = &board_star_trek;
+		break;
+	case GO7007_BOARDID_XMEN:
+		name = "WIS XMen or compatible";
+		board = &board_xmen;
+		break;
+	case GO7007_BOARDID_XMEN_II:
+		name = "WIS XMen II or compatible";
+		board = &board_xmen;
+		break;
+	case GO7007_BOARDID_XMEN_III:
+		name = "WIS XMen III or compatible";
+		board = &board_xmen;
+		break;
+	case GO7007_BOARDID_PX_M402U:
+		name = "Plextor PX-M402U";
+		board = &board_matrix_ii;
+		break;
+	case GO7007_BOARDID_PX_TV402U_ANY:
+		name = "Plextor PX-TV402U (unknown tuner)";
+		board = &board_px_tv402u;
+		break;
+	case GO7007_BOARDID_LIFEVIEW_LR192:
+		printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra "
+				"is not supported.  Sorry!\n");
+		return 0;
+		name = "Lifeview TV Walker Ultra";
+		board = &board_lifeview_lr192;
+		break;
+	default:
+		printk(KERN_ERR "go7007-usb: unknown board ID %d!\n",
+				(unsigned int)id->driver_info);
+		return 0;
+	}
+
+	usb = kmalloc(sizeof(struct go7007_usb), GFP_KERNEL);
+	if (usb == NULL)
+		return -ENOMEM;
+	memset(usb, 0, sizeof(struct go7007_usb));
+
+	/* Allocate the URB and buffer for receiving incoming interrupts */
+	usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL);
+	if (usb->intr_urb == NULL)
+		goto allocfail;
+	usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL);
+	if (usb->intr_urb->transfer_buffer == NULL)
+		goto allocfail;
+
+	go = go7007_alloc(&board->main_info, &intf->dev);
+	if (go == NULL)
+		goto allocfail;
+	usb->board = board;
+	usb->usbdev = usbdev;
+	go->board_id = id->driver_info;
+	strncpy(go->name, name, sizeof(go->name));
+	if (board->flags & GO7007_USB_EZUSB)
+		go->hpi_ops = &go7007_usb_ezusb_hpi_ops;
+	else
+		go->hpi_ops = &go7007_usb_onboard_hpi_ops;
+	go->hpi_context = usb;
+	usb_fill_int_urb(usb->intr_urb, usb->usbdev,
+			usb_rcvintpipe(usb->usbdev, 4),
+			usb->intr_urb->transfer_buffer, 2*sizeof(u16),
+			go7007_usb_readinterrupt_complete, go, 8);
+	usb_set_intfdata(intf, go);
+
+	/* Boot the GO7007 */
+	if (go7007_boot_encoder(go, go->board_info->flags &
+					GO7007_BOARD_USE_ONBOARD_I2C) < 0)
+		goto initfail;
+
+	/* Register the EZ-USB I2C adapter, if we're using it */
+	if (board->flags & GO7007_USB_EZUSB_I2C) {
+		memcpy(&go->i2c_adapter, &go7007_usb_adap_templ,
+				sizeof(go7007_usb_adap_templ));
+		init_MUTEX(&usb->i2c_lock);
+		go->i2c_adapter.dev.parent = go->dev;
+		i2c_set_adapdata(&go->i2c_adapter, go);
+		if (i2c_add_adapter(&go->i2c_adapter) < 0) {
+			printk(KERN_ERR
+				"go7007-usb: error: i2c_add_adapter failed\n");
+			goto initfail;
+		}
+		go->i2c_adapter_online = 1;
+	}
+
+	/* Pelco and Adlink reused the XMen and XMen-III vendor and product
+	 * IDs for their own incompatible designs.  We can detect XMen boards
+	 * by probing the sensor, but there is no way to probe the sensors on
+	 * the Pelco and Adlink designs so we default to the Adlink.  If it
+	 * is actually a Pelco, the user must set the assume_endura module
+	 * parameter. */
+	if ((go->board_id == GO7007_BOARDID_XMEN ||
+				go->board_id == GO7007_BOARDID_XMEN_III) &&
+			go->i2c_adapter_online) {
+		union i2c_smbus_data data;
+
+		/* Check to see if register 0x0A is 0x76 */
+		i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB,
+			I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data);
+		if (data.byte != 0x76) {
+			if (assume_endura) {
+				go->board_id = GO7007_BOARDID_ENDURA;
+				usb->board = board = &board_endura;
+				go->board_info = &board->main_info;
+				strncpy(go->name, "Pelco Endura",
+						sizeof(go->name));
+			} else {
+				u16 channel;
+
+				/* set GPIO5 to be an output, currently low */
+				go7007_write_addr(go, 0x3c82, 0x0000);
+				go7007_write_addr(go, 0x3c80, 0x00df);
+				/* read channel number from GPIO[1:0] */
+				go7007_read_addr(go, 0x3c81, &channel);
+				channel &= 0x3;
+				go->board_id = GO7007_BOARDID_ADLINK_MPG24;
+				usb->board = board = &board_adlink_mpg24;
+				go->board_info = &board->main_info;
+				go->channel_number = channel;
+				snprintf(go->name, sizeof(go->name),
+					"Adlink PCI-MPG24, channel #%d",
+					channel);
+			}
+		}
+	}
+
+	/* Probe the tuner model on the TV402U */
+	if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) {
+		u8 data[3];
+
+		/* Board strapping indicates tuner model */
+		if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) {
+			printk(KERN_ERR "go7007-usb: GPIO read failed!\n");
+			goto initfail;
+		}
+		switch (data[0] >> 6) {
+		case 1:
+			go->board_id = GO7007_BOARDID_PX_TV402U_EU;
+			go->tuner_type = TUNER_SONY_BTF_PG472Z;
+			strncpy(go->name, "Plextor PX-TV402U-EU",
+					sizeof(go->name));
+			break;
+		case 2:
+			go->board_id = GO7007_BOARDID_PX_TV402U_JP;
+			go->tuner_type = TUNER_SONY_BTF_PK467Z;
+			strncpy(go->name, "Plextor PX-TV402U-JP",
+					sizeof(go->name));
+			break;
+		case 3:
+			go->board_id = GO7007_BOARDID_PX_TV402U_NA;
+			go->tuner_type = TUNER_SONY_BTF_PB463Z;
+			strncpy(go->name, "Plextor PX-TV402U-NA",
+					sizeof(go->name));
+			break;
+		default:
+			printk(KERN_DEBUG "go7007-usb: unable to detect "
+						"tuner type!\n");
+			break;
+		}
+		/* Configure tuner mode selection inputs connected
+		 * to the EZ-USB GPIO output pins */
+		if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0,
+					NULL, 0, 0) < 0) {
+			printk(KERN_ERR
+				"go7007-usb: GPIO write failed!\n");
+			goto initfail;
+		}
+	}
+
+	/* Print a nasty message if the user attempts to use a USB2.0 device in
+	 * a USB1.1 port.  There will be silent corruption of the stream. */
+	if ((board->flags & GO7007_USB_EZUSB) &&
+			usbdev->speed != USB_SPEED_HIGH)
+		printk(KERN_ERR "go7007-usb: *** WARNING ***  This device "
+				"must be connected to a USB 2.0 port!  "
+				"Attempting to capture video through a USB 1.1 "
+				"port will result in stream corruption, even "
+				"at low bitrates!\n");
+
+	/* Do any final GO7007 initialization, then register the
+	 * V4L2 and ALSA interfaces */
+	if (go7007_register_encoder(go) < 0)
+		goto initfail;
+
+	/* Allocate the URBs and buffers for receiving the video stream */
+	if (board->flags & GO7007_USB_EZUSB) {
+		v_urb_len = 1024;
+		video_pipe = usb_rcvbulkpipe(usb->usbdev, 6);
+	} else {
+		v_urb_len = 512;
+		video_pipe = usb_rcvbulkpipe(usb->usbdev, 1);
+	}
+	for (i = 0; i < 8; ++i) {
+		usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+		if (usb->video_urbs[i] == NULL)
+			goto initfail;
+		usb->video_urbs[i]->transfer_buffer =
+						kmalloc(v_urb_len, GFP_KERNEL);
+		if (usb->video_urbs[i]->transfer_buffer == NULL)
+			goto initfail;
+		usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe,
+				usb->video_urbs[i]->transfer_buffer, v_urb_len,
+				go7007_usb_read_video_pipe_complete, go);
+	}
+
+	/* Allocate the URBs and buffers for receiving the audio stream */
+	if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled)
+		for (i = 0; i < 8; ++i) {
+			usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL);
+			if (usb->audio_urbs[i] == NULL)
+				goto initfail;
+			usb->audio_urbs[i]->transfer_buffer = kmalloc(4096,
+								GFP_KERNEL);
+			if (usb->audio_urbs[i]->transfer_buffer == NULL)
+				goto initfail;
+			usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev,
+				usb_rcvbulkpipe(usb->usbdev, 8),
+				usb->audio_urbs[i]->transfer_buffer, 4096,
+				go7007_usb_read_audio_pipe_complete, go);
+		}
+
+
+	go->status = STATUS_ONLINE;
+	return 0;
+
+initfail:
+	go->status = STATUS_SHUTDOWN;
+	return 0;
+
+allocfail:
+	if (usb->intr_urb) {
+		kfree(usb->intr_urb->transfer_buffer);
+		usb_free_urb(usb->intr_urb);
+	}
+	kfree(usb);
+	return -ENOMEM;
+}
+
+static void go7007_usb_disconnect(struct usb_interface *intf)
+{
+	struct go7007 *go = usb_get_intfdata(intf);
+	struct go7007_usb *usb = go->hpi_context;
+	int i;
+
+	go->status = STATUS_SHUTDOWN;
+	usb_kill_urb(usb->intr_urb);
+
+	/* Free USB-related structs */
+	for (i = 0; i < 8; ++i) {
+		if (usb->video_urbs[i] != NULL) {
+			if (usb->video_urbs[i]->transfer_buffer != NULL)
+				kfree(usb->video_urbs[i]->transfer_buffer);
+			usb_free_urb(usb->video_urbs[i]);
+		}
+		if (usb->audio_urbs[i] != NULL) {
+			if (usb->audio_urbs[i]->transfer_buffer != NULL)
+				kfree(usb->audio_urbs[i]->transfer_buffer);
+			usb_free_urb(usb->audio_urbs[i]);
+		}
+	}
+	kfree(usb->intr_urb->transfer_buffer);
+	usb_free_urb(usb->intr_urb);
+
+	kfree(go->hpi_context);
+
+	go7007_remove(go);
+}
+
+static struct usb_driver go7007_usb_driver = {
+	.name		= "go7007",
+	.probe		= go7007_usb_probe,
+	.disconnect	= go7007_usb_disconnect,
+	.id_table	= go7007_usb_id_table,
+};
+
+static int __init go7007_usb_init(void)
+{
+	return usb_register(&go7007_usb_driver);
+}
+
+static void __exit go7007_usb_cleanup(void)
+{
+	usb_deregister(&go7007_usb_driver);
+}
+
+module_init(go7007_usb_init);
+module_exit(go7007_usb_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c
new file mode 100644
index 0000000..d54d019
--- /dev/null
+++ b/drivers/staging/go7007/go7007-v4l2.c
@@ -0,0 +1,1503 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/fs.h>
+#include <linux/unistd.h>
+#include <linux/time.h>
+#include <linux/vmalloc.h>
+#include <linux/pagemap.h>
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include <linux/i2c.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+
+#include "go7007.h"
+#include "go7007-priv.h"
+#include "wis-i2c.h"
+
+static void deactivate_buffer(struct go7007_buffer *gobuf)
+{
+	int i;
+
+	if (gobuf->state != BUF_STATE_IDLE) {
+		list_del(&gobuf->stream);
+		gobuf->state = BUF_STATE_IDLE;
+	}
+	if (gobuf->page_count > 0) {
+		for (i = 0; i < gobuf->page_count; ++i)
+			page_cache_release(gobuf->pages[i]);
+		gobuf->page_count = 0;
+	}
+}
+
+static void abort_queued(struct go7007 *go)
+{
+	struct go7007_buffer *gobuf, *next;
+
+	list_for_each_entry_safe(gobuf, next, &go->stream, stream) {
+		deactivate_buffer(gobuf);
+	}
+}
+
+static int go7007_streamoff(struct go7007 *go)
+{
+	int retval = -EINVAL;
+	unsigned long flags;
+
+	down(&go->hw_lock);
+	if (go->streaming) {
+		go->streaming = 0;
+		go7007_stream_stop(go);
+		spin_lock_irqsave(&go->spinlock, flags);
+		abort_queued(go);
+		spin_unlock_irqrestore(&go->spinlock, flags);
+		go7007_reset_encoder(go);
+		retval = 0;
+	}
+	up(&go->hw_lock);
+	return 0;
+}
+
+static int go7007_open(struct inode *inode, struct file *file)
+{
+	struct go7007 *go = video_get_drvdata(video_devdata(file));
+	struct go7007_file *gofh;
+
+	if (go->status != STATUS_ONLINE)
+		return -EBUSY;
+	gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL);
+	if (gofh == NULL)
+		return -ENOMEM;
+	++go->ref_count;
+	gofh->go = go;
+	init_MUTEX(&gofh->lock);
+	gofh->buf_count = 0;
+	file->private_data = gofh;
+	return 0;
+}
+
+static int go7007_release(struct inode *inode, struct file *file)
+{
+	struct go7007_file *gofh = file->private_data;
+	struct go7007 *go = gofh->go;
+
+	if (gofh->buf_count > 0) {
+		go7007_streamoff(go);
+		go->in_use = 0;
+		kfree(gofh->bufs);
+		gofh->buf_count = 0;
+	}
+	kfree(gofh);
+	if (--go->ref_count == 0)
+		kfree(go);
+	file->private_data = NULL;
+	return 0;
+}
+
+static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format)
+{
+	u8 *f = page_address(gobuf->pages[0]);
+
+	switch (format) {
+	case GO7007_FORMAT_MJPEG:
+		return V4L2_BUF_FLAG_KEYFRAME;
+	case GO7007_FORMAT_MPEG4:
+		switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) {
+		case 0:
+			return V4L2_BUF_FLAG_KEYFRAME;
+		case 1:
+			return V4L2_BUF_FLAG_PFRAME;
+		case 2:
+			return V4L2_BUF_FLAG_BFRAME;
+		default:
+			return 0;
+		}
+	case GO7007_FORMAT_MPEG1:
+	case GO7007_FORMAT_MPEG2:
+		switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) {
+		case 1:
+			return V4L2_BUF_FLAG_KEYFRAME;
+		case 2:
+			return V4L2_BUF_FLAG_PFRAME;
+		case 3:
+			return V4L2_BUF_FLAG_BFRAME;
+		default:
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try)
+{
+	int sensor_height = 0, sensor_width = 0;
+	int width, height, i;
+
+	if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG &&
+			fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG &&
+			fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4)
+		return -EINVAL;
+
+	switch (go->standard) {
+	case GO7007_STD_NTSC:
+		sensor_width = 720;
+		sensor_height = 480;
+		break;
+	case GO7007_STD_PAL:
+		sensor_width = 720;
+		sensor_height = 576;
+		break;
+	case GO7007_STD_OTHER:
+		sensor_width = go->board_info->sensor_width;
+		sensor_height = go->board_info->sensor_height;
+		break;
+	}
+
+	if (fmt == NULL) {
+		width = sensor_width;
+		height = sensor_height;
+	} else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
+		if (fmt->fmt.pix.width > sensor_width)
+			width = sensor_width;
+		else if (fmt->fmt.pix.width < 144)
+			width = 144;
+		else
+			width = fmt->fmt.pix.width & ~0x0f;
+
+		if (fmt->fmt.pix.height > sensor_height)
+			height = sensor_height;
+		else if (fmt->fmt.pix.height < 96)
+			height = 96;
+		else
+			height = fmt->fmt.pix.height & ~0x0f;
+	} else {
+		int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height;
+		int sensor_size = sensor_width * sensor_height;
+
+		if (64 * requested_size < 9 * sensor_size) {
+			width = sensor_width / 4;
+			height = sensor_height / 4;
+		} else if (64 * requested_size < 36 * sensor_size) {
+			width = sensor_width / 2;
+			height = sensor_height / 2;
+		} else {
+			width = sensor_width;
+			height = sensor_height;
+		}
+		width &= ~0xf;
+		height &= ~0xf;
+	}
+
+	if (fmt != NULL) {
+		u32 pixelformat = fmt->fmt.pix.pixelformat;
+
+		memset(fmt, 0, sizeof(*fmt));
+		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		fmt->fmt.pix.width = width;
+		fmt->fmt.pix.height = height;
+		fmt->fmt.pix.pixelformat = pixelformat;
+		fmt->fmt.pix.field = V4L2_FIELD_NONE;
+		fmt->fmt.pix.bytesperline = 0;
+		fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
+		fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */
+	}
+
+	if (try)
+		return 0;
+
+	go->width = width;
+	go->height = height;
+	go->encoder_h_offset = go->board_info->sensor_h_offset;
+	go->encoder_v_offset = go->board_info->sensor_v_offset;
+	for (i = 0; i < 4; ++i)
+		go->modet[i].enable = 0;
+	for (i = 0; i < 1624; ++i)
+		go->modet_map[i] = 0;
+
+	if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) {
+		struct video_decoder_resolution res;
+
+		res.width = width;
+		if (height > sensor_height / 2) {
+			res.height = height / 2;
+			go->encoder_v_halve = 0;
+		} else {
+			res.height = height;
+			go->encoder_v_halve = 1;
+		}
+		if (go->i2c_adapter_online)
+			i2c_clients_command(&go->i2c_adapter,
+					DECODER_SET_RESOLUTION, &res);
+	} else {
+		if (width <= sensor_width / 4) {
+			go->encoder_h_halve = 1;
+			go->encoder_v_halve = 1;
+			go->encoder_subsample = 1;
+		} else if (width <= sensor_width / 2) {
+			go->encoder_h_halve = 1;
+			go->encoder_v_halve = 1;
+			go->encoder_subsample = 0;
+		} else {
+			go->encoder_h_halve = 0;
+			go->encoder_v_halve = 0;
+			go->encoder_subsample = 0;
+		}
+	}
+
+	if (fmt == NULL)
+		return 0;
+
+	switch (fmt->fmt.pix.pixelformat) {
+	case V4L2_PIX_FMT_MPEG:
+		if (go->format == GO7007_FORMAT_MPEG1 ||
+				go->format == GO7007_FORMAT_MPEG2 ||
+				go->format == GO7007_FORMAT_MPEG4)
+			break;
+		go->format = GO7007_FORMAT_MPEG1;
+		go->pali = 0;
+		go->aspect_ratio = GO7007_RATIO_1_1;
+		go->gop_size = go->sensor_framerate / 1000;
+		go->ipb = 0;
+		go->closed_gop = 1;
+		go->repeat_seqhead = 1;
+		go->seq_header_enable = 1;
+		go->gop_header_enable = 1;
+		go->dvd_mode = 0;
+		break;
+	/* Backwards compatibility only! */
+	case V4L2_PIX_FMT_MPEG4:
+		if (go->format == GO7007_FORMAT_MPEG4)
+			break;
+		go->format = GO7007_FORMAT_MPEG4;
+		go->pali = 0xf5;
+		go->aspect_ratio = GO7007_RATIO_1_1;
+		go->gop_size = go->sensor_framerate / 1000;
+		go->ipb = 0;
+		go->closed_gop = 1;
+		go->repeat_seqhead = 1;
+		go->seq_header_enable = 1;
+		go->gop_header_enable = 1;
+		go->dvd_mode = 0;
+		break;
+	case V4L2_PIX_FMT_MJPEG:
+		go->format = GO7007_FORMAT_MJPEG;
+		go->pali = 0;
+		go->aspect_ratio = GO7007_RATIO_1_1;
+		go->gop_size = 0;
+		go->ipb = 0;
+		go->closed_gop = 0;
+		go->repeat_seqhead = 0;
+		go->seq_header_enable = 0;
+		go->gop_header_enable = 0;
+		go->dvd_mode = 0;
+		break;
+	}
+	return 0;
+}
+
+static int clip_to_modet_map(struct go7007 *go, int region,
+		struct v4l2_clip *clip_list)
+{
+	struct v4l2_clip clip, *clip_ptr;
+	int x, y, mbnum;
+
+	/* Check if coordinates are OK and if any macroblocks are already
+	 * used by other regions (besides 0) */
+	clip_ptr = clip_list;
+	while (clip_ptr) {
+		if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
+			return -EFAULT;
+		if (clip.c.left < 0 || (clip.c.left & 0xF) ||
+				clip.c.width <= 0 || (clip.c.width & 0xF))
+			return -EINVAL;
+		if (clip.c.left + clip.c.width > go->width)
+			return -EINVAL;
+		if (clip.c.top < 0 || (clip.c.top & 0xF) ||
+				clip.c.height <= 0 || (clip.c.height & 0xF))
+			return -EINVAL;
+		if (clip.c.top + clip.c.height > go->height)
+			return -EINVAL;
+		for (y = 0; y < clip.c.height; y += 16)
+			for (x = 0; x < clip.c.width; x += 16) {
+				mbnum = (go->width >> 4) *
+						((clip.c.top + y) >> 4) +
+					((clip.c.left + x) >> 4);
+				if (go->modet_map[mbnum] != 0 &&
+						go->modet_map[mbnum] != region)
+					return -EBUSY;
+			}
+		clip_ptr = clip.next;
+	}
+
+	/* Clear old region macroblocks */
+	for (mbnum = 0; mbnum < 1624; ++mbnum)
+		if (go->modet_map[mbnum] == region)
+			go->modet_map[mbnum] = 0;
+
+	/* Claim macroblocks in this list */
+	clip_ptr = clip_list;
+	while (clip_ptr) {
+		if (copy_from_user(&clip, clip_ptr, sizeof(clip)))
+			return -EFAULT;
+		for (y = 0; y < clip.c.height; y += 16)
+			for (x = 0; x < clip.c.width; x += 16) {
+				mbnum = (go->width >> 4) *
+						((clip.c.top + y) >> 4) +
+					((clip.c.left + x) >> 4);
+				go->modet_map[mbnum] = region;
+			}
+		clip_ptr = clip.next;
+	}
+	return 0;
+}
+
+static int go7007_do_ioctl(struct inode *inode, struct file *file,
+		unsigned int cmd, void *arg)
+{
+	struct go7007_file *gofh = file->private_data;
+	struct go7007 *go = gofh->go;
+	unsigned long flags;
+	int retval = 0;
+
+	switch (cmd) {
+	case VIDIOC_QUERYCAP:
+	{
+		struct v4l2_capability *cap = arg;
+
+		memset(cap, 0, sizeof(*cap));
+		strcpy(cap->driver, "go7007");
+		strncpy(cap->card, go->name, sizeof(cap->card));
+		cap->version = KERNEL_VERSION(0, 9, 8);
+		cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+				V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */
+		if (go->board_info->flags & GO7007_BOARD_HAS_TUNER)
+			cap->capabilities |= V4L2_CAP_TUNER;
+		return 0;
+	}
+	case VIDIOC_ENUM_FMT:
+	{
+		struct v4l2_fmtdesc *fmt = arg;
+		unsigned int index;
+		char *desc;
+		u32 pixelformat;
+
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		switch (fmt->index) {
+		case 0:
+			pixelformat = V4L2_PIX_FMT_MJPEG;
+			desc = "Motion-JPEG";
+			break;
+		case 1:
+			pixelformat = V4L2_PIX_FMT_MPEG;
+			desc = "MPEG1/MPEG2/MPEG4";
+			break;
+		default:
+			return -EINVAL;
+		}
+		index = fmt->index;
+		memset(fmt, 0, sizeof(*fmt));
+		fmt->index = index;
+		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		fmt->flags = V4L2_FMT_FLAG_COMPRESSED;
+		strncpy(fmt->description, desc, sizeof(fmt->description));
+		fmt->pixelformat = pixelformat;
+
+		return 0;
+	}
+	case VIDIOC_TRY_FMT:
+	{
+		struct v4l2_format *fmt = arg;
+
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		return set_capture_size(go, fmt, 1);
+	}
+	case VIDIOC_G_FMT:
+	{
+		struct v4l2_format *fmt = arg;
+
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		memset(fmt, 0, sizeof(*fmt));
+		fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		fmt->fmt.pix.width = go->width;
+		fmt->fmt.pix.height = go->height;
+		fmt->fmt.pix.pixelformat = go->format == GO7007_FORMAT_MJPEG ?
+			V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG;
+		fmt->fmt.pix.field = V4L2_FIELD_NONE;
+		fmt->fmt.pix.bytesperline = 0;
+		fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE;
+		fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */
+		return 0;
+	}
+	case VIDIOC_S_FMT:
+	{
+		struct v4l2_format *fmt = arg;
+
+		if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (go->streaming)
+			return -EBUSY;
+		return set_capture_size(go, fmt, 0);
+	}
+	case VIDIOC_G_FBUF:
+	case VIDIOC_S_FBUF:
+		return -EINVAL;
+	case VIDIOC_REQBUFS:
+	{
+		struct v4l2_requestbuffers *req = arg;
+		unsigned int count, i;
+
+		if (go->streaming)
+			return -EBUSY;
+		if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+				req->memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+
+		down(&gofh->lock);
+		retval = -EBUSY;
+		for (i = 0; i < gofh->buf_count; ++i)
+			if (gofh->bufs[i].mapped > 0)
+				goto unlock_and_return;
+		down(&go->hw_lock);
+		if (go->in_use > 0 && gofh->buf_count == 0) {
+			up(&go->hw_lock);
+			goto unlock_and_return;
+		}
+		if (gofh->buf_count > 0)
+			kfree(gofh->bufs);
+		retval = -ENOMEM;
+		count = req->count;
+		if (count > 0) {
+			if (count < 2)
+				count = 2;
+			if (count > 32)
+				count = 32;
+			gofh->bufs = kmalloc(count *
+						sizeof(struct go7007_buffer),
+					GFP_KERNEL);
+			if (gofh->bufs == NULL) {
+				up(&go->hw_lock);
+				goto unlock_and_return;
+			}
+			memset(gofh->bufs, 0,
+					count * sizeof(struct go7007_buffer));
+			for (i = 0; i < count; ++i) {
+				gofh->bufs[i].go = go;
+				gofh->bufs[i].index = i;
+				gofh->bufs[i].state = BUF_STATE_IDLE;
+				gofh->bufs[i].mapped = 0;
+			}
+			go->in_use = 1;
+		} else {
+			go->in_use = 0;
+		}
+		gofh->buf_count = count;
+		up(&go->hw_lock);
+		up(&gofh->lock);
+		memset(req, 0, sizeof(*req));
+		req->count = count;
+		req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		req->memory = V4L2_MEMORY_MMAP;
+		return 0;
+	}
+	case VIDIOC_QUERYBUF:
+	{
+		struct v4l2_buffer *buf = arg;
+		unsigned int index;
+
+		retval = -EINVAL;
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		index = buf->index;
+		down(&gofh->lock);
+		if (index >= gofh->buf_count)
+			goto unlock_and_return;
+		memset(buf, 0, sizeof(*buf));
+		buf->index = index;
+		buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		switch (gofh->bufs[index].state) {
+		case BUF_STATE_QUEUED:
+			buf->flags = V4L2_BUF_FLAG_QUEUED;
+			break;
+		case BUF_STATE_DONE:
+			buf->flags = V4L2_BUF_FLAG_DONE;
+			break;
+		default:
+			buf->flags = 0;
+		}
+		if (gofh->bufs[index].mapped)
+			buf->flags |= V4L2_BUF_FLAG_MAPPED;
+		buf->memory = V4L2_MEMORY_MMAP;
+		buf->m.offset = index * GO7007_BUF_SIZE;
+		buf->length = GO7007_BUF_SIZE;
+		up(&gofh->lock);
+
+		return 0;
+	}
+	case VIDIOC_QBUF:
+	{
+		struct v4l2_buffer *buf = arg;
+		struct go7007_buffer *gobuf;
+		int ret;
+
+		retval = -EINVAL;
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+				buf->memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+		down(&gofh->lock);
+		if (buf->index < 0 || buf->index >= gofh->buf_count)
+			goto unlock_and_return;
+		gobuf = &gofh->bufs[buf->index];
+		if (gobuf->mapped == 0)
+			goto unlock_and_return;
+		retval = -EBUSY;
+		if (gobuf->state != BUF_STATE_IDLE)
+			goto unlock_and_return;
+		/* offset will be 0 until we really support USERPTR streaming */
+		gobuf->offset = gobuf->user_addr & ~PAGE_MASK;
+		gobuf->bytesused = 0;
+		gobuf->frame_offset = 0;
+		gobuf->modet_active = 0;
+		if (gobuf->offset > 0)
+			gobuf->page_count = GO7007_BUF_PAGES + 1;
+		else
+			gobuf->page_count = GO7007_BUF_PAGES;
+		retval = -ENOMEM;
+		down_read(&current->mm->mmap_sem);
+		ret = get_user_pages(current, current->mm,
+				gobuf->user_addr & PAGE_MASK, gobuf->page_count,
+				1, 1, gobuf->pages, NULL);
+		up_read(&current->mm->mmap_sem);
+		if (ret != gobuf->page_count) {
+			int i;
+			for (i = 0; i < ret; ++i)
+				page_cache_release(gobuf->pages[i]);
+			gobuf->page_count = 0;
+			goto unlock_and_return;
+		}
+		gobuf->state = BUF_STATE_QUEUED;
+		spin_lock_irqsave(&go->spinlock, flags);
+		list_add_tail(&gobuf->stream, &go->stream);
+		spin_unlock_irqrestore(&go->spinlock, flags);
+		up(&gofh->lock);
+		return 0;
+	}
+	case VIDIOC_DQBUF:
+	{
+		struct v4l2_buffer *buf = arg;
+		struct go7007_buffer *gobuf;
+		u32 frame_type_flag;
+		DEFINE_WAIT(wait);
+
+		if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (buf->memory != V4L2_MEMORY_MMAP)
+			return -EINVAL;
+		down(&gofh->lock);
+		retval = -EINVAL;
+		if (list_empty(&go->stream))
+			goto unlock_and_return;
+		gobuf = list_entry(go->stream.next,
+				struct go7007_buffer, stream);
+		retval = -EAGAIN;
+		if (gobuf->state != BUF_STATE_DONE &&
+				!(file->f_flags & O_NONBLOCK)) {
+			for (;;) {
+				prepare_to_wait(&go->frame_waitq, &wait,
+						TASK_INTERRUPTIBLE);
+				if (gobuf->state == BUF_STATE_DONE)
+					break;
+				if (signal_pending(current)) {
+					retval = -ERESTARTSYS;
+					break;
+				}
+				schedule();
+			}
+			finish_wait(&go->frame_waitq, &wait);
+		}
+		if (gobuf->state != BUF_STATE_DONE)
+			goto unlock_and_return;
+		spin_lock_irqsave(&go->spinlock, flags);
+		deactivate_buffer(gobuf);
+		spin_unlock_irqrestore(&go->spinlock, flags);
+		frame_type_flag = get_frame_type_flag(gobuf, go->format);
+		gobuf->state = BUF_STATE_IDLE;
+		memset(buf, 0, sizeof(*buf));
+		buf->index = gobuf->index;
+		buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		buf->bytesused = gobuf->bytesused;
+		buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag;
+		buf->field = V4L2_FIELD_NONE;
+		buf->timestamp = gobuf->timestamp;
+		buf->sequence = gobuf->seq;
+		buf->memory = V4L2_MEMORY_MMAP;
+		buf->m.offset = gobuf->index * GO7007_BUF_SIZE;
+		buf->length = GO7007_BUF_SIZE;
+		buf->reserved = gobuf->modet_active;
+		up(&gofh->lock);
+		return 0;
+	}
+	case VIDIOC_STREAMON:
+	{
+		unsigned int *type = arg;
+
+		if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		down(&gofh->lock);
+		down(&go->hw_lock);
+		if (!go->streaming) {
+			go->streaming = 1;
+			go->next_seq = 0;
+			go->active_buf = NULL;
+			if (go7007_start_encoder(go) < 0)
+				retval = -EIO;
+			else
+				retval = 0;
+		}
+		up(&go->hw_lock);
+		up(&gofh->lock);
+		return retval;
+	}
+	case VIDIOC_STREAMOFF:
+	{
+		unsigned int *type = arg;
+
+		if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		down(&gofh->lock);
+		go7007_streamoff(go);
+		up(&gofh->lock);
+		return 0;
+	}
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *ctrl = arg;
+		u32 id;
+
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		id = ctrl->id;
+		memset(ctrl, 0, sizeof(*ctrl));
+		ctrl->id = id;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, arg);
+		return ctrl->name[0] == 0 ? -EINVAL : 0;
+	}
+	case VIDIOC_G_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+		struct v4l2_queryctrl query;
+
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		memset(&query, 0, sizeof(query));
+		query.id = ctrl->id;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
+		if (query.name[0] == 0)
+			return -EINVAL;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, arg);
+		return 0;
+	}
+	case VIDIOC_S_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+		struct v4l2_queryctrl query;
+
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		memset(&query, 0, sizeof(query));
+		query.id = ctrl->id;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query);
+		if (query.name[0] == 0)
+			return -EINVAL;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, arg);
+		return 0;
+	}
+	case VIDIOC_G_PARM:
+	{
+		struct v4l2_streamparm *parm = arg;
+		struct v4l2_fract timeperframe = {
+			.numerator = 1001 *  go->fps_scale,
+			.denominator = go->sensor_framerate,
+		};
+
+		if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		memset(parm, 0, sizeof(*parm));
+		parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME;
+		parm->parm.capture.timeperframe = timeperframe;
+		return 0;
+	}
+	case VIDIOC_S_PARM:
+	{
+		struct v4l2_streamparm *parm = arg;
+		unsigned int n, d;
+
+		if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		if (parm->parm.capture.capturemode != 0)
+			return -EINVAL;
+		n = go->sensor_framerate *
+			parm->parm.capture.timeperframe.numerator;
+		d = 1001 * parm->parm.capture.timeperframe.denominator;
+		if (n != 0 && d != 0 && n > d)
+			go->fps_scale = (n + d/2) / d;
+		else
+			go->fps_scale = 1;
+		return 0;
+	}
+	case VIDIOC_ENUMSTD:
+	{
+		struct v4l2_standard *std = arg;
+
+		if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+				go->input == go->board_info->num_inputs - 1) {
+			if (!go->i2c_adapter_online)
+				return -EIO;
+			i2c_clients_command(&go->i2c_adapter,
+						VIDIOC_ENUMSTD, arg);
+			if (!std->id) /* hack to indicate EINVAL from tuner */
+				return -EINVAL;
+		} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
+			switch (std->index) {
+			case 0:
+				v4l2_video_std_construct(std,
+						V4L2_STD_NTSC, "NTSC");
+				break;
+			case 1:
+				v4l2_video_std_construct(std,
+						V4L2_STD_PAL | V4L2_STD_SECAM,
+						"PAL/SECAM");
+				break;
+			default:
+				return -EINVAL;
+			}
+		} else {
+			if (std->index != 0)
+				return -EINVAL;
+			memset(std, 0, sizeof(*std));
+			snprintf(std->name, sizeof(std->name), "%dx%d, %dfps",
+				go->board_info->sensor_width,
+				go->board_info->sensor_height,
+				go->board_info->sensor_framerate / 1000);
+			std->frameperiod.numerator = 1001;
+			std->frameperiod.denominator =
+					go->board_info->sensor_framerate;
+		}
+		return 0;
+	}
+	case VIDIOC_G_STD:
+	{
+		v4l2_std_id *std = arg;
+
+		if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+				go->input == go->board_info->num_inputs - 1) {
+			if (!go->i2c_adapter_online)
+				return -EIO;
+			i2c_clients_command(&go->i2c_adapter,
+						VIDIOC_G_STD, arg);
+		} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
+			if (go->standard == GO7007_STD_NTSC)
+				*std = V4L2_STD_NTSC;
+			else
+				*std = V4L2_STD_PAL | V4L2_STD_SECAM;
+		} else
+			*std = 0;
+		return 0;
+	}
+	case VIDIOC_S_STD:
+	{
+		v4l2_std_id *std = arg;
+		int norm;
+
+		if (go->streaming)
+			return -EBUSY;
+		if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) &&
+				*std != 0)
+			return -EINVAL;
+		if (*std == 0)
+			return -EINVAL;
+		if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+				go->input == go->board_info->num_inputs - 1) {
+			if (!go->i2c_adapter_online)
+				return -EIO;
+			i2c_clients_command(&go->i2c_adapter,
+						VIDIOC_S_STD, arg);
+			if (!*std) /* hack to indicate EINVAL from tuner */
+				return -EINVAL;
+		}
+		if (*std & V4L2_STD_NTSC) {
+			go->standard = GO7007_STD_NTSC;
+			go->sensor_framerate = 30000;
+			norm = VIDEO_MODE_NTSC;
+		} else if (*std & V4L2_STD_PAL) {
+			go->standard = GO7007_STD_PAL;
+			go->sensor_framerate = 25025;
+			norm = VIDEO_MODE_PAL;
+		} else if (*std & V4L2_STD_SECAM) {
+			go->standard = GO7007_STD_PAL;
+			go->sensor_framerate = 25025;
+			norm = VIDEO_MODE_SECAM;
+		} else
+			return -EINVAL;
+		if (go->i2c_adapter_online)
+			i2c_clients_command(&go->i2c_adapter,
+						DECODER_SET_NORM, &norm);
+		set_capture_size(go, NULL, 0);
+		return 0;
+	}
+	case VIDIOC_QUERYSTD:
+	{
+		v4l2_std_id *std = arg;
+
+		if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+				go->input == go->board_info->num_inputs - 1) {
+			if (!go->i2c_adapter_online)
+				return -EIO;
+			i2c_clients_command(&go->i2c_adapter,
+						VIDIOC_QUERYSTD, arg);
+		} else if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
+			*std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+		else
+			*std = 0;
+		return 0;
+	}
+	case VIDIOC_ENUMINPUT:
+	{
+		struct v4l2_input *inp = arg;
+		int index;
+
+		if (inp->index >= go->board_info->num_inputs)
+			return -EINVAL;
+		index = inp->index;
+		memset(inp, 0, sizeof(*inp));
+		inp->index = index;
+		strncpy(inp->name, go->board_info->inputs[index].name,
+				sizeof(inp->name));
+		/* If this board has a tuner, it will be the last input */
+		if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) &&
+				index == go->board_info->num_inputs - 1)
+			inp->type = V4L2_INPUT_TYPE_TUNER;
+		else
+			inp->type = V4L2_INPUT_TYPE_CAMERA;
+		inp->audioset = 0;
+		inp->tuner = 0;
+		if (go->board_info->sensor_flags & GO7007_SENSOR_TV)
+			inp->std = V4L2_STD_NTSC | V4L2_STD_PAL |
+							V4L2_STD_SECAM;
+		else
+			inp->std = 0;
+		return 0;
+	}
+	case VIDIOC_G_INPUT:
+	{
+		int *input = arg;
+
+		*input = go->input;
+		return 0;
+	}
+	case VIDIOC_S_INPUT:
+	{
+		int *input = arg;
+
+		if (*input >= go->board_info->num_inputs)
+			return -EINVAL;
+		if (go->streaming)
+			return -EBUSY;
+		go->input = *input;
+		if (go->i2c_adapter_online) {
+			i2c_clients_command(&go->i2c_adapter, DECODER_SET_INPUT,
+				&go->board_info->inputs[*input].video_input);
+			i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO,
+				&go->board_info->inputs[*input].audio_input);
+		}
+		return 0;
+	}
+	case VIDIOC_G_TUNER:
+	{
+		struct v4l2_tuner *t = arg;
+
+		if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+			return -EINVAL;
+		if (t->index != 0)
+			return -EINVAL;
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, arg);
+		t->index = 0;
+		return 0;
+	}
+	case VIDIOC_S_TUNER:
+	{
+		struct v4l2_tuner *t = arg;
+
+		if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+			return -EINVAL;
+		if (t->index != 0)
+			return -EINVAL;
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		switch (go->board_id) {
+		case GO7007_BOARDID_PX_TV402U_NA:
+		case GO7007_BOARDID_PX_TV402U_JP:
+			/* No selectable options currently */
+			if (t->audmode != V4L2_TUNER_MODE_STEREO)
+				return -EINVAL;
+			break;
+		}
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, arg);
+		return 0;
+	}
+	case VIDIOC_G_FREQUENCY:
+	{
+		struct v4l2_frequency *f = arg;
+
+		if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+			return -EINVAL;
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		memset(f, 0, sizeof(*f));
+		f->type = V4L2_TUNER_ANALOG_TV;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, arg);
+		return 0;
+	}
+	case VIDIOC_S_FREQUENCY:
+	{
+		if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER))
+			return -EINVAL;
+		if (!go->i2c_adapter_online)
+			return -EIO;
+		i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, arg);
+		return 0;
+	}
+	case VIDIOC_CROPCAP:
+	{
+		struct v4l2_cropcap *cropcap = arg;
+
+		if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		memset(cropcap, 0, sizeof(*cropcap));
+		cropcap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		/* These specify the raw input of the sensor */
+		switch (go->standard) {
+		case GO7007_STD_NTSC:
+			cropcap->bounds.top = 0;
+			cropcap->bounds.left = 0;
+			cropcap->bounds.width = 720;
+			cropcap->bounds.height = 480;
+			cropcap->defrect.top = 0;
+			cropcap->defrect.left = 0;
+			cropcap->defrect.width = 720;
+			cropcap->defrect.height = 480;
+			break;
+		case GO7007_STD_PAL:
+			cropcap->bounds.top = 0;
+			cropcap->bounds.left = 0;
+			cropcap->bounds.width = 720;
+			cropcap->bounds.height = 576;
+			cropcap->defrect.top = 0;
+			cropcap->defrect.left = 0;
+			cropcap->defrect.width = 720;
+			cropcap->defrect.height = 576;
+			break;
+		case GO7007_STD_OTHER:
+			cropcap->bounds.top = 0;
+			cropcap->bounds.left = 0;
+			cropcap->bounds.width = go->board_info->sensor_width;
+			cropcap->bounds.height = go->board_info->sensor_height;
+			cropcap->defrect.top = 0;
+			cropcap->defrect.left = 0;
+			cropcap->defrect.width = go->board_info->sensor_width;
+			cropcap->defrect.height = go->board_info->sensor_height;
+			break;
+		}
+
+		return 0;
+	}
+	case VIDIOC_G_CROP:
+	{
+		struct v4l2_crop *crop = arg;
+
+		if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		memset(crop, 0, sizeof(*crop));
+		crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+		/* These specify the raw input of the sensor */
+		switch (go->standard) {
+		case GO7007_STD_NTSC:
+			crop->c.top = 0;
+			crop->c.left = 0;
+			crop->c.width = 720;
+			crop->c.height = 480;
+			break;
+		case GO7007_STD_PAL:
+			crop->c.top = 0;
+			crop->c.left = 0;
+			crop->c.width = 720;
+			crop->c.height = 576;
+			break;
+		case GO7007_STD_OTHER:
+			crop->c.top = 0;
+			crop->c.left = 0;
+			crop->c.width = go->board_info->sensor_width;
+			crop->c.height = go->board_info->sensor_height;
+			break;
+		}
+
+		return 0;
+	}
+	case VIDIOC_S_CROP:
+	{
+		struct v4l2_crop *crop = arg;
+
+		if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+			return -EINVAL;
+		return 0;
+	}
+	case VIDIOC_G_JPEGCOMP:
+	{
+		struct v4l2_jpegcompression *params = arg;
+
+		memset(params, 0, sizeof(*params));
+		params->quality = 50; /* ?? */
+		params->jpeg_markers = V4L2_JPEG_MARKER_DHT |
+					V4L2_JPEG_MARKER_DQT;
+
+		return 0;
+	}
+	case VIDIOC_S_JPEGCOMP:
+	{
+		struct v4l2_jpegcompression *params = arg;
+
+		if (params->quality != 50 ||
+				params->jpeg_markers != (V4L2_JPEG_MARKER_DHT |
+							V4L2_JPEG_MARKER_DQT))
+			return -EINVAL;
+		return 0;
+	}
+	/* Temporary ioctls for controlling compression characteristics */
+	case GO7007IOC_S_BITRATE:
+	{
+		int *bitrate = arg;
+
+		if (go->streaming)
+			return -EINVAL;
+		/* Upper bound is kind of arbitrary here */
+		if (*bitrate < 64000 || *bitrate > 10000000)
+			return -EINVAL;
+		go->bitrate = *bitrate;
+		return 0;
+	}
+	case GO7007IOC_G_BITRATE:
+	{
+		int *bitrate = arg;
+
+		*bitrate = go->bitrate;
+		return 0;
+	}
+	case GO7007IOC_S_COMP_PARAMS:
+	{
+		struct go7007_comp_params *comp = arg;
+
+		if (go->format == GO7007_FORMAT_MJPEG)
+			return -EINVAL;
+		if (comp->gop_size > 0)
+			go->gop_size = comp->gop_size;
+		else
+			go->gop_size = go->sensor_framerate / 1000;
+		if (go->gop_size != 15)
+			go->dvd_mode = 0;
+		/*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */
+		if (go->board_info->sensor_flags & GO7007_SENSOR_TV) {
+			switch (comp->aspect_ratio) {
+			case GO7007_ASPECT_RATIO_4_3_NTSC:
+			case GO7007_ASPECT_RATIO_4_3_PAL:
+				go->aspect_ratio = GO7007_RATIO_4_3;
+				break;
+			case GO7007_ASPECT_RATIO_16_9_NTSC:
+			case GO7007_ASPECT_RATIO_16_9_PAL:
+				go->aspect_ratio = GO7007_RATIO_16_9;
+				break;
+			default:
+				go->aspect_ratio = GO7007_RATIO_1_1;
+				break;
+			}
+		}
+		if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) {
+			go->dvd_mode = 0;
+			go->seq_header_enable = 0;
+		} else {
+			go->seq_header_enable = 1;
+		}
+		/* fall-through */
+	}
+	case GO7007IOC_G_COMP_PARAMS:
+	{
+		struct go7007_comp_params *comp = arg;
+
+		if (go->format == GO7007_FORMAT_MJPEG)
+			return -EINVAL;
+		memset(comp, 0, sizeof(*comp));
+		comp->gop_size = go->gop_size;
+		comp->max_b_frames = go->ipb ? 2 : 0;
+		switch (go->aspect_ratio) {
+		case GO7007_RATIO_4_3:
+			if (go->standard == GO7007_STD_NTSC)
+				comp->aspect_ratio =
+					GO7007_ASPECT_RATIO_4_3_NTSC;
+			else
+				comp->aspect_ratio =
+					GO7007_ASPECT_RATIO_4_3_PAL;
+			break;
+		case GO7007_RATIO_16_9:
+			if (go->standard == GO7007_STD_NTSC)
+				comp->aspect_ratio =
+					GO7007_ASPECT_RATIO_16_9_NTSC;
+			else
+				comp->aspect_ratio =
+					GO7007_ASPECT_RATIO_16_9_PAL;
+			break;
+		default:
+			comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1;
+			break;
+		}
+		if (go->closed_gop)
+			comp->flags |= GO7007_COMP_CLOSED_GOP;
+		if (!go->seq_header_enable)
+			comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER;
+		return 0;
+	}
+	case GO7007IOC_S_MPEG_PARAMS:
+	{
+		struct go7007_mpeg_params *mpeg = arg;
+
+		if (go->format != GO7007_FORMAT_MPEG1 &&
+				go->format != GO7007_FORMAT_MPEG2 &&
+				go->format != GO7007_FORMAT_MPEG4)
+			return -EINVAL;
+
+		if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) {
+			go->format = GO7007_FORMAT_MPEG2;
+			go->bitrate = 9800000;
+			go->gop_size = 15;
+			go->pali = 0x48;
+			go->closed_gop = 1;
+			go->repeat_seqhead = 0;
+			go->seq_header_enable = 1;
+			go->gop_header_enable = 1;
+			go->dvd_mode = 1;
+		} else {
+			switch (mpeg->mpeg_video_standard) {
+			case GO7007_MPEG_VIDEO_MPEG1:
+				go->format = GO7007_FORMAT_MPEG1;
+				go->pali = 0;
+				break;
+			case GO7007_MPEG_VIDEO_MPEG2:
+				go->format = GO7007_FORMAT_MPEG2;
+				if (mpeg->pali >> 24 == 2)
+					go->pali = mpeg->pali & 0xff;
+				else
+					go->pali = 0x48;
+				break;
+			case GO7007_MPEG_VIDEO_MPEG4:
+				go->format = GO7007_FORMAT_MPEG4;
+				if (mpeg->pali >> 24 == 4)
+					go->pali = mpeg->pali & 0xff;
+				else
+					go->pali = 0xf5;
+				break;
+			default:
+				return -EINVAL;
+			}
+			go->gop_header_enable =
+				mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER
+				? 0 : 1;
+			if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER)
+				go->repeat_seqhead = 1;
+			else
+				go->repeat_seqhead = 0;
+			go->dvd_mode = 0;
+		}
+		/* fall-through */
+	}
+	case GO7007IOC_G_MPEG_PARAMS:
+	{
+		struct go7007_mpeg_params *mpeg = arg;
+
+		memset(mpeg, 0, sizeof(*mpeg));
+		switch (go->format) {
+		case GO7007_FORMAT_MPEG1:
+			mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1;
+			mpeg->pali = 0;
+			break;
+		case GO7007_FORMAT_MPEG2:
+			mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2;
+			mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali);
+			break;
+		case GO7007_FORMAT_MPEG4:
+			mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4;
+			mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali);
+			break;
+		default:
+			return -EINVAL;
+		}
+		if (!go->gop_header_enable)
+			mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER;
+		if (go->repeat_seqhead)
+			mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER;
+		if (go->dvd_mode)
+			mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE;
+		return 0;
+	}
+	case GO7007IOC_S_MD_PARAMS:
+	{
+		struct go7007_md_params *mdp = arg;
+
+		if (mdp->region > 3)
+			return -EINVAL;
+		if (mdp->trigger > 0) {
+			go->modet[mdp->region].pixel_threshold =
+					mdp->pixel_threshold >> 1;
+			go->modet[mdp->region].motion_threshold =
+					mdp->motion_threshold >> 1;
+			go->modet[mdp->region].mb_threshold =
+					mdp->trigger >> 1;
+			go->modet[mdp->region].enable = 1;
+		} else
+			go->modet[mdp->region].enable = 0;
+		/* fall-through */
+	}
+	case GO7007IOC_G_MD_PARAMS:
+	{
+		struct go7007_md_params *mdp = arg;
+		int region = mdp->region;
+
+		if (mdp->region > 3)
+			return -EINVAL;
+		memset(mdp, 0, sizeof(struct go7007_md_params));
+		mdp->region = region;
+		if (!go->modet[region].enable)
+			return 0;
+		mdp->pixel_threshold =
+			(go->modet[region].pixel_threshold << 1) + 1;
+		mdp->motion_threshold =
+			(go->modet[region].motion_threshold << 1) + 1;
+		mdp->trigger =
+			(go->modet[region].mb_threshold << 1) + 1;
+		return 0;
+	}
+	case GO7007IOC_S_MD_REGION:
+	{
+		struct go7007_md_region *region = arg;
+
+		if (region->region < 1 || region->region > 3)
+			return -EINVAL;
+		return clip_to_modet_map(go, region->region, region->clips);
+	}
+	default:
+		printk(KERN_DEBUG "go7007: unsupported ioctl %d\n", cmd);
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+
+unlock_and_return:
+	up(&gofh->lock);
+	return retval;
+}
+
+static int go7007_ioctl(struct inode *inode, struct file *file,
+		unsigned int cmd, unsigned long arg)
+{
+	struct go7007_file *gofh = file->private_data;
+
+	if (gofh->go->status != STATUS_ONLINE)
+		return -EIO;
+
+	return video_usercopy(inode, file, cmd, arg, go7007_do_ioctl);
+}
+
+static ssize_t go7007_read(struct file *file, char __user *data,
+		size_t count, loff_t *ppos)
+{
+	return -EINVAL;
+}
+
+static void go7007_vm_open(struct vm_area_struct *vma)
+{
+	struct go7007_buffer *gobuf = vma->vm_private_data;
+
+	++gobuf->mapped;
+}
+
+static void go7007_vm_close(struct vm_area_struct *vma)
+{
+	struct go7007_buffer *gobuf = vma->vm_private_data;
+	unsigned long flags;
+
+	if (--gobuf->mapped == 0) {
+		spin_lock_irqsave(&gobuf->go->spinlock, flags);
+		deactivate_buffer(gobuf);
+		spin_unlock_irqrestore(&gobuf->go->spinlock, flags);
+	}
+}
+
+/* Copied from videobuf-dma-sg.c */
+static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+	struct page *page;
+
+	page = alloc_page(GFP_USER | __GFP_DMA32);
+	if (!page)
+		return VM_FAULT_OOM;
+	clear_user_page(page_address(page), (unsigned long)vmf->virtual_address,
+			page);
+	vmf->page = page;
+	return 0;
+}
+
+static struct vm_operations_struct go7007_vm_ops = {
+	.open	= go7007_vm_open,
+	.close	= go7007_vm_close,
+	.fault	= go7007_vm_fault,
+};
+
+static int go7007_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct go7007_file *gofh = file->private_data;
+	unsigned int index;
+
+	if (gofh->go->status != STATUS_ONLINE)
+		return -EIO;
+	if (!(vma->vm_flags & VM_SHARED))
+		return -EINVAL; /* only support VM_SHARED mapping */
+	if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE)
+		return -EINVAL; /* must map exactly one full buffer */
+	down(&gofh->lock);
+	index = vma->vm_pgoff / GO7007_BUF_PAGES;
+	if (index >= gofh->buf_count) {
+		up(&gofh->lock);
+		return -EINVAL; /* trying to map beyond requested buffers */
+	}
+	if (index * GO7007_BUF_PAGES != vma->vm_pgoff) {
+		up(&gofh->lock);
+		return -EINVAL; /* offset is not aligned on buffer boundary */
+	}
+	if (gofh->bufs[index].mapped > 0) {
+		up(&gofh->lock);
+		return -EBUSY;
+	}
+	gofh->bufs[index].mapped = 1;
+	gofh->bufs[index].user_addr = vma->vm_start;
+	vma->vm_ops = &go7007_vm_ops;
+	vma->vm_flags |= VM_DONTEXPAND;
+	vma->vm_flags &= ~VM_IO;
+	vma->vm_private_data = &gofh->bufs[index];
+	up(&gofh->lock);
+	return 0;
+}
+
+static unsigned int go7007_poll(struct file *file, poll_table *wait)
+{
+	struct go7007_file *gofh = file->private_data;
+	struct go7007_buffer *gobuf;
+
+	if (list_empty(&gofh->go->stream))
+		return POLLERR;
+	gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream);
+	poll_wait(file, &gofh->go->frame_waitq, wait);
+	if (gobuf->state == BUF_STATE_DONE)
+		return POLLIN | POLLRDNORM;
+	return 0;
+}
+
+static void go7007_vfl_release(struct video_device *vfd)
+{
+	struct go7007 *go = video_get_drvdata(vfd);
+
+	video_device_release(vfd);
+	if (--go->ref_count == 0)
+		kfree(go);
+}
+
+static struct file_operations go7007_fops = {
+	.owner		= THIS_MODULE,
+	.open		= go7007_open,
+	.release	= go7007_release,
+	.ioctl		= go7007_ioctl,
+	.llseek		= no_llseek,
+	.read		= go7007_read,
+	.mmap		= go7007_mmap,
+	.poll		= go7007_poll,
+};
+
+static struct video_device go7007_template = {
+	.name		= "go7007",
+	.fops		= &go7007_fops,
+	.minor		= -1,
+	.release	= go7007_vfl_release,
+};
+
+int go7007_v4l2_init(struct go7007 *go)
+{
+	int rv;
+
+	go->video_dev = video_device_alloc();
+	if (go->video_dev == NULL)
+		return -ENOMEM;
+	memcpy(go->video_dev, &go7007_template, sizeof(go7007_template));
+	go->video_dev->parent = go->dev;
+	rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1);
+	if (rv < 0) {
+		video_device_release(go->video_dev);
+		go->video_dev = NULL;
+		return rv;
+	}
+	video_set_drvdata(go->video_dev, go);
+	++go->ref_count;
+
+	return 0;
+}
+
+void go7007_v4l2_remove(struct go7007 *go)
+{
+	unsigned long flags;
+
+	down(&go->hw_lock);
+	if (go->streaming) {
+		go->streaming = 0;
+		go7007_stream_stop(go);
+		spin_lock_irqsave(&go->spinlock, flags);
+		abort_queued(go);
+		spin_unlock_irqrestore(&go->spinlock, flags);
+	}
+	up(&go->hw_lock);
+	if (go->video_dev)
+		video_unregister_device(go->video_dev);
+}
diff --git a/drivers/staging/go7007/go7007.h b/drivers/staging/go7007/go7007.h
new file mode 100644
index 0000000..7399c91
--- /dev/null
+++ b/drivers/staging/go7007/go7007.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and the associated README documentation file (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.
+ *
+ * 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.
+ */
+
+/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS
+ * to select between MPEG1, MPEG2, and MPEG4 */
+#define V4L2_PIX_FMT_MPEG4     v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */
+
+/* These will be replaced with a better interface
+ * soon, so don't get too attached to them */
+#define	GO7007IOC_S_BITRATE	_IOW('V', BASE_VIDIOC_PRIVATE + 0, int)
+#define	GO7007IOC_G_BITRATE	_IOR('V', BASE_VIDIOC_PRIVATE + 1, int)
+
+enum go7007_aspect_ratio {
+	GO7007_ASPECT_RATIO_1_1 = 0,
+	GO7007_ASPECT_RATIO_4_3_NTSC = 1,
+	GO7007_ASPECT_RATIO_4_3_PAL = 2,
+	GO7007_ASPECT_RATIO_16_9_NTSC = 3,
+	GO7007_ASPECT_RATIO_16_9_PAL = 4,
+};
+
+/* Used to set generic compression parameters */
+struct go7007_comp_params {
+	__u32 gop_size;
+	__u32 max_b_frames;
+	enum go7007_aspect_ratio aspect_ratio;
+	__u32 flags;
+	__u32 reserved[8];
+};
+
+#define GO7007_COMP_CLOSED_GOP		0x00000001
+#define GO7007_COMP_OMIT_SEQ_HEADER	0x00000002
+
+enum go7007_mpeg_video_standard {
+	GO7007_MPEG_VIDEO_MPEG1 = 0,
+	GO7007_MPEG_VIDEO_MPEG2 = 1,
+	GO7007_MPEG_VIDEO_MPEG4 = 2,
+};
+
+/* Used to set parameters for V4L2_PIX_FMT_MPEG format */
+struct go7007_mpeg_params {
+	enum go7007_mpeg_video_standard mpeg_video_standard;
+	__u32 flags;
+	__u32 pali;
+	__u32 reserved[8];
+};
+
+#define GO7007_MPEG_FORCE_DVD_MODE	0x00000001
+#define GO7007_MPEG_OMIT_GOP_HEADER	0x00000002
+#define GO7007_MPEG_REPEAT_SEQHEADER	0x00000004
+
+#define GO7007_MPEG_PROFILE(format, pali)	(((format)<<24)|(pali))
+
+#define GO7007_MPEG2_PROFILE_MAIN_MAIN		GO7007_MPEG_PROFILE(2, 0x48)
+
+#define GO7007_MPEG4_PROFILE_S_L0		GO7007_MPEG_PROFILE(4, 0x08)
+#define GO7007_MPEG4_PROFILE_S_L1		GO7007_MPEG_PROFILE(4, 0x01)
+#define GO7007_MPEG4_PROFILE_S_L2		GO7007_MPEG_PROFILE(4, 0x02)
+#define GO7007_MPEG4_PROFILE_S_L3		GO7007_MPEG_PROFILE(4, 0x03)
+#define GO7007_MPEG4_PROFILE_ARTS_L1		GO7007_MPEG_PROFILE(4, 0x91)
+#define GO7007_MPEG4_PROFILE_ARTS_L2		GO7007_MPEG_PROFILE(4, 0x92)
+#define GO7007_MPEG4_PROFILE_ARTS_L3		GO7007_MPEG_PROFILE(4, 0x93)
+#define GO7007_MPEG4_PROFILE_ARTS_L4		GO7007_MPEG_PROFILE(4, 0x94)
+#define GO7007_MPEG4_PROFILE_AS_L0		GO7007_MPEG_PROFILE(4, 0xf0)
+#define GO7007_MPEG4_PROFILE_AS_L1		GO7007_MPEG_PROFILE(4, 0xf1)
+#define GO7007_MPEG4_PROFILE_AS_L2		GO7007_MPEG_PROFILE(4, 0xf2)
+#define GO7007_MPEG4_PROFILE_AS_L3		GO7007_MPEG_PROFILE(4, 0xf3)
+#define GO7007_MPEG4_PROFILE_AS_L4		GO7007_MPEG_PROFILE(4, 0xf4)
+#define GO7007_MPEG4_PROFILE_AS_L5		GO7007_MPEG_PROFILE(4, 0xf5)
+
+struct go7007_md_params {
+	__u16 region;
+	__u16 trigger;
+	__u16 pixel_threshold;
+	__u16 motion_threshold;
+	__u32 reserved[8];
+};
+
+struct go7007_md_region {
+	__u16 region;
+	__u16 flags;
+	struct v4l2_clip *clips;
+	__u32 reserved[8];
+};
+
+#define	GO7007IOC_S_MPEG_PARAMS	_IOWR('V', BASE_VIDIOC_PRIVATE + 2, \
+					struct go7007_mpeg_params)
+#define	GO7007IOC_G_MPEG_PARAMS	_IOR('V', BASE_VIDIOC_PRIVATE + 3, \
+					struct go7007_mpeg_params)
+#define	GO7007IOC_S_COMP_PARAMS	_IOWR('V', BASE_VIDIOC_PRIVATE + 4, \
+					struct go7007_comp_params)
+#define	GO7007IOC_G_COMP_PARAMS	_IOR('V', BASE_VIDIOC_PRIVATE + 5, \
+					struct go7007_comp_params)
+#define	GO7007IOC_S_MD_PARAMS	_IOWR('V', BASE_VIDIOC_PRIVATE + 6, \
+					struct go7007_md_params)
+#define	GO7007IOC_G_MD_PARAMS	_IOR('V', BASE_VIDIOC_PRIVATE + 7, \
+					struct go7007_md_params)
+#define	GO7007IOC_S_MD_REGION	_IOW('V', BASE_VIDIOC_PRIVATE + 8, \
+					struct go7007_md_region)
diff --git a/drivers/staging/go7007/saa7134-go7007.c b/drivers/staging/go7007/saa7134-go7007.c
new file mode 100644
index 0000000..c4a6d8e
--- /dev/null
+++ b/drivers/staging/go7007/saa7134-go7007.c
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <asm/byteorder.h>
+#include <media/audiochip.h>
+
+#include "saa7134-reg.h"
+#include "saa7134.h"
+#include "go7007-priv.h"
+
+#define GO7007_HPI_DEBUG
+
+enum hpi_address {
+	HPI_ADDR_VIDEO_BUFFER = 0xe4,
+	HPI_ADDR_INIT_BUFFER = 0xea,
+	HPI_ADDR_INTR_RET_VALUE = 0xee,
+	HPI_ADDR_INTR_RET_DATA = 0xec,
+	HPI_ADDR_INTR_STATUS = 0xf4,
+	HPI_ADDR_INTR_WR_PARAM = 0xf6,
+	HPI_ADDR_INTR_WR_INDEX = 0xf8,
+};
+
+enum gpio_command {
+	GPIO_COMMAND_RESET = 0x00, /* 000b */
+	GPIO_COMMAND_REQ1  = 0x04, /* 001b */
+	GPIO_COMMAND_WRITE = 0x20, /* 010b */
+	GPIO_COMMAND_REQ2  = 0x24, /* 011b */
+	GPIO_COMMAND_READ  = 0x80, /* 100b */
+	GPIO_COMMAND_VIDEO = 0x84, /* 101b */
+	GPIO_COMMAND_IDLE  = 0xA0, /* 110b */
+	GPIO_COMMAND_ADDR  = 0xA4, /* 111b */
+};
+
+struct saa7134_go7007 {
+	struct saa7134_dev *dev;
+	u8 *top;
+	u8 *bottom;
+	dma_addr_t top_dma;
+	dma_addr_t bottom_dma;
+};
+
+static struct go7007_board_info board_voyager = {
+	.firmware	 = "go7007tv.bin",
+	.flags		 = 0,
+	.sensor_flags	 = GO7007_SENSOR_656 |
+				GO7007_SENSOR_VALID_ENABLE |
+				GO7007_SENSOR_TV |
+				GO7007_SENSOR_VBI,
+	.audio_flags	= GO7007_AUDIO_I2S_MODE_1 |
+				GO7007_AUDIO_WORD_16,
+	.audio_rate	 = 48000,
+	.audio_bclk_div	 = 8,
+	.audio_main_div	 = 2,
+	.hpi_buffer_cap  = 7,
+	.num_inputs	 = 1,
+	.inputs 	 = {
+		{
+			.name		= "SAA7134",
+		},
+	},
+};
+
+/********************* Driver for GPIO HPI interface *********************/
+
+static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data)
+{
+	saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+	/* Write HPI address */
+	saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+	/* Write low byte */
+	saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+	/* Write high byte */
+	saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+	return 0;
+}
+
+static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data)
+{
+	saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+	/* Write HPI address */
+	saa_writeb(SAA7134_GPIO_GPSTATUS0, addr);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+	saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
+
+	/* Read low byte */
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
+	saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	*data = saa_readb(SAA7134_GPIO_GPSTATUS0);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+	/* Read high byte */
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ);
+	saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	*data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8;
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+
+	return 0;
+}
+
+static int saa7134_go7007_interface_reset(struct go7007 *go)
+{
+	struct saa7134_go7007 *saa = go->hpi_context;
+	struct saa7134_dev *dev = saa->dev;
+	u32 status;
+	u16 intr_val, intr_data;
+	int count = 20;
+
+	saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */
+	saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4);
+	saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET);
+	msleep(1);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
+	msleep(10);
+
+	saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+	saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+
+	status = saa_readb(SAA7134_GPIO_GPSTATUS2);
+	/*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */
+
+	/* enter command mode...(?) */
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2);
+
+	do {
+		saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+		saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN);
+		status = saa_readb(SAA7134_GPIO_GPSTATUS2);
+		/*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */
+	} while (--count > 0);
+
+	/* Wait for an interrupt to indicate successful hardware reset */
+	if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 ||
+			(intr_val & ~0x1) != 0x55aa) {
+		printk(KERN_ERR
+			"saa7134-go7007: unable to reset the GO7007\n");
+		return -1;
+	}
+	return 0;
+}
+
+static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data)
+{
+	struct saa7134_go7007 *saa = go->hpi_context;
+	struct saa7134_dev *dev = saa->dev;
+	int i;
+	u16 status_reg;
+
+#ifdef GO7007_HPI_DEBUG
+	printk(KERN_DEBUG
+		"saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data);
+#endif
+
+	for (i = 0; i < 100; ++i) {
+		gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
+		if (!(status_reg & 0x0010))
+			break;
+		msleep(10);
+	}
+	if (i == 100) {
+		printk(KERN_ERR
+			"saa7134-go7007: device is hung, status reg = 0x%04x\n",
+			status_reg);
+		return -1;
+	}
+	gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data);
+	gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr);
+
+	return 0;
+}
+
+static int saa7134_go7007_read_interrupt(struct go7007 *go)
+{
+	struct saa7134_go7007 *saa = go->hpi_context;
+	struct saa7134_dev *dev = saa->dev;
+
+	/* XXX we need to wait if there is no interrupt available */
+	go->interrupt_available = 1;
+	gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value);
+	gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data);
+#ifdef GO7007_HPI_DEBUG
+	printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n",
+			go->interrupt_value, go->interrupt_data);
+#endif
+	return 0;
+}
+
+static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev,
+						unsigned long status)
+{
+	struct go7007 *go = video_get_drvdata(dev->empress_dev);
+	struct saa7134_go7007 *saa = go->hpi_context;
+
+	if (!go->streaming)
+		return;
+	if (0 != (status & 0x000f0000))
+		printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n",
+				(status >> 16) & 0x0f);
+	if (status & 0x100000) {
+		dma_sync_single(&dev->pci->dev,
+				saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+		go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE);
+		saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
+	} else {
+		dma_sync_single(&dev->pci->dev,
+				saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE);
+		go7007_parse_video_stream(go, saa->top, PAGE_SIZE);
+		saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
+	}
+}
+
+static int saa7134_go7007_stream_start(struct go7007 *go)
+{
+	struct saa7134_go7007 *saa = go->hpi_context;
+	struct saa7134_dev *dev = saa->dev;
+
+	saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top),
+			0, PAGE_SIZE, DMA_FROM_DEVICE);
+	if (!saa->top_dma)
+		return -ENOMEM;
+	saa->bottom_dma = dma_map_page(&dev->pci->dev,
+			virt_to_page(saa->bottom),
+			0, PAGE_SIZE, DMA_FROM_DEVICE);
+	if (!saa->bottom_dma) {
+		dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
+				DMA_FROM_DEVICE);
+		return -ENOMEM;
+	}
+
+	saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000);
+	saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200);
+
+	/* Set HPI interface for video */
+	saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+	saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER);
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+	saa_writeb(SAA7134_GPIO_GPMODE0, 0x00);
+
+	/* Enable TS interface */
+	saa_writeb(SAA7134_TS_PARALLEL, 0xe6);
+
+	/* Reset TS interface */
+	saa_setb(SAA7134_TS_SERIAL1, 0x01);
+	saa_clearb(SAA7134_TS_SERIAL1, 0x01);
+
+	/* Set up transfer block size */
+	saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1);
+	saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1);
+	saa_writeb(SAA7134_TS_DMA1, 0);
+	saa_writeb(SAA7134_TS_DMA2, 0);
+
+	/* Enable video streaming mode */
+	saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO);
+
+	saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma));
+	saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma));
+	saa_writel(SAA7134_RS_PITCH(5), 128);
+	saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX);
+
+	/* Enable TS FIFO */
+	saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
+
+	/* Enable DMA IRQ */
+	saa_setl(SAA7134_IRQ1,
+			SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
+
+	return 0;
+}
+
+static int saa7134_go7007_stream_stop(struct go7007 *go)
+{
+	struct saa7134_go7007 *saa = go->hpi_context;
+	struct saa7134_dev *dev = saa->dev;
+
+	/* Shut down TS FIFO */
+	saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5);
+
+	/* Disable DMA IRQ */
+	saa_clearl(SAA7134_IRQ1,
+			SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0);
+
+	/* Disable TS interface */
+	saa_clearb(SAA7134_TS_PARALLEL, 0x80);
+
+	dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE,
+			DMA_FROM_DEVICE);
+	dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE,
+			DMA_FROM_DEVICE);
+
+	return 0;
+}
+
+static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len)
+{
+	struct saa7134_go7007 *saa = go->hpi_context;
+	struct saa7134_dev *dev = saa->dev;
+	u16 status_reg;
+	int i;
+
+#ifdef GO7007_HPI_DEBUG
+	printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer "
+			"sending %d bytes\n", len);
+#endif
+
+	while (len > 0) {
+		i = len > 64 ? 64 : len;
+		saa_writeb(SAA7134_GPIO_GPMODE0, 0xff);
+		saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER);
+		saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR);
+		saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+		while (i-- > 0) {
+			saa_writeb(SAA7134_GPIO_GPSTATUS0, *data);
+			saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE);
+			saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE);
+			++data;
+			--len;
+		}
+		for (i = 0; i < 100; ++i) {
+			gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg);
+			if (!(status_reg & 0x0002))
+				break;
+		}
+		if (i == 100) {
+			printk(KERN_ERR "saa7134-go7007: device is hung, "
+					"status reg = 0x%04x\n", status_reg);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static struct go7007_hpi_ops saa7134_go7007_hpi_ops = {
+	.interface_reset	= saa7134_go7007_interface_reset,
+	.write_interrupt	= saa7134_go7007_write_interrupt,
+	.read_interrupt		= saa7134_go7007_read_interrupt,
+	.stream_start		= saa7134_go7007_stream_start,
+	.stream_stop		= saa7134_go7007_stream_stop,
+	.send_firmware		= saa7134_go7007_send_firmware,
+};
+
+/********************* Add/remove functions *********************/
+
+static int saa7134_go7007_init(struct saa7134_dev *dev)
+{
+	struct go7007 *go;
+	struct saa7134_go7007 *saa;
+
+	printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n");
+
+	saa = kmalloc(sizeof(struct saa7134_go7007), GFP_KERNEL);
+	if (saa == NULL)
+		return -ENOMEM;
+	memset(saa, 0, sizeof(struct saa7134_go7007));
+
+	/* Allocate a couple pages for receiving the compressed stream */
+	saa->top = (u8 *)get_zeroed_page(GFP_KERNEL);
+	if (!saa->top)
+		goto allocfail;
+	saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL);
+	if (!saa->bottom)
+		goto allocfail;
+
+	go = go7007_alloc(&board_voyager, &dev->pci->dev);
+	if (go == NULL)
+		goto allocfail;
+	go->board_id = GO7007_BOARDID_PCI_VOYAGER;
+	strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name));
+	go->hpi_ops = &saa7134_go7007_hpi_ops;
+	go->hpi_context = saa;
+	saa->dev = dev;
+
+	/* Boot the GO7007 */
+	if (go7007_boot_encoder(go, go->board_info->flags &
+					GO7007_BOARD_USE_ONBOARD_I2C) < 0)
+		goto initfail;
+
+	/* Do any final GO7007 initialization, then register the
+	 * V4L2 and ALSA interfaces */
+	if (go7007_register_encoder(go) < 0)
+		goto initfail;
+	dev->empress_dev = go->video_dev;
+	video_set_drvdata(dev->empress_dev, go);
+
+	go->status = STATUS_ONLINE;
+	return 0;
+
+initfail:
+	go->status = STATUS_SHUTDOWN;
+	return 0;
+
+allocfail:
+	if (saa->top)
+		free_page((unsigned long)saa->top);
+	if (saa->bottom)
+		free_page((unsigned long)saa->bottom);
+	kfree(saa);
+	return -ENOMEM;
+}
+
+static int saa7134_go7007_fini(struct saa7134_dev *dev)
+{
+	struct go7007 *go;
+	struct saa7134_go7007 *saa;
+
+	if (NULL == dev->empress_dev)
+		return 0;
+
+	go = video_get_drvdata(dev->empress_dev);
+	saa = go->hpi_context;
+	go->status = STATUS_SHUTDOWN;
+	free_page((unsigned long)saa->top);
+	free_page((unsigned long)saa->bottom);
+	kfree(saa);
+	go7007_remove(go);
+	dev->empress_dev = NULL;
+
+	return 0;
+}
+
+static struct saa7134_mpeg_ops saa7134_go7007_ops = {
+	.type          = SAA7134_MPEG_GO7007,
+	.init          = saa7134_go7007_init,
+	.fini          = saa7134_go7007_fini,
+	.irq_ts_done   = saa7134_go7007_irq_ts_done,
+};
+
+static int __init saa7134_go7007_mod_init(void)
+{
+	return saa7134_ts_register(&saa7134_go7007_ops);
+}
+
+static void __exit saa7134_go7007_mod_cleanup(void)
+{
+	saa7134_ts_unregister(&saa7134_go7007_ops);
+}
+
+module_init(saa7134_go7007_mod_init);
+module_exit(saa7134_go7007_mod_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/snd-go7007.c b/drivers/staging/go7007/snd-go7007.c
new file mode 100644
index 0000000..f5cac08
--- /dev/null
+++ b/drivers/staging/go7007/snd-go7007.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/time.h>
+#include <linux/mm.h>
+#include <linux/i2c.h>
+#include <linux/semaphore.h>
+#include <linux/uaccess.h>
+#include <asm/system.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/initval.h>
+
+#include "go7007-priv.h"
+
+static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR;
+static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+module_param_array(id, charp, NULL, 0444);
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(index, "Index value for the go7007 audio driver");
+MODULE_PARM_DESC(index, "ID string for the go7007 audio driver");
+MODULE_PARM_DESC(index, "Enable for the go7007 audio driver");
+
+struct go7007_snd {
+	struct snd_card *card;
+	struct snd_pcm *pcm;
+	struct snd_pcm_substream *substream;
+	spinlock_t lock;
+	int w_idx;
+	int hw_ptr;
+	int avail;
+	int capturing;
+};
+
+static struct snd_pcm_hardware go7007_snd_capture_hw = {
+	.info			= (SNDRV_PCM_INFO_MMAP |
+					SNDRV_PCM_INFO_INTERLEAVED |
+					SNDRV_PCM_INFO_BLOCK_TRANSFER |
+					SNDRV_PCM_INFO_MMAP_VALID),
+	.formats		= SNDRV_PCM_FMTBIT_S16_LE,
+	.rates			= SNDRV_PCM_RATE_48000,
+	.rate_min		= 48000,
+	.rate_max		= 48000,
+	.channels_min		= 2,
+	.channels_max		= 2,
+	.buffer_bytes_max	= (128*1024),
+	.period_bytes_min	= 4096,
+	.period_bytes_max	= (128*1024),
+	.periods_min		= 1,
+	.periods_max		= 32,
+};
+
+static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length)
+{
+	struct go7007_snd *gosnd = go->snd_context;
+	struct snd_pcm_runtime *runtime = gosnd->substream->runtime;
+	int frames = bytes_to_frames(runtime, length);
+
+	spin_lock(&gosnd->lock);
+	gosnd->hw_ptr += frames;
+	if (gosnd->hw_ptr >= runtime->buffer_size)
+		gosnd->hw_ptr -= runtime->buffer_size;
+	gosnd->avail += frames;
+	spin_unlock(&gosnd->lock);
+	if (gosnd->w_idx + length > runtime->dma_bytes) {
+		int cpy = runtime->dma_bytes - gosnd->w_idx;
+
+		memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy);
+		length -= cpy;
+		buf += cpy;
+		gosnd->w_idx = 0;
+	}
+	memcpy(runtime->dma_area + gosnd->w_idx, buf, length);
+	gosnd->w_idx += length;
+	spin_lock(&gosnd->lock);
+	if (gosnd->avail < runtime->period_size) {
+		spin_unlock(&gosnd->lock);
+		return;
+	}
+	gosnd->avail -= runtime->period_size;
+	spin_unlock(&gosnd->lock);
+	if (gosnd->capturing)
+		snd_pcm_period_elapsed(gosnd->substream);
+}
+
+static int go7007_snd_hw_params(struct snd_pcm_substream *substream,
+				struct snd_pcm_hw_params *hw_params)
+{
+	struct go7007 *go = snd_pcm_substream_chip(substream);
+	unsigned int bytes;
+
+	bytes = params_buffer_bytes(hw_params);
+	if (substream->runtime->dma_bytes > 0)
+		vfree(substream->runtime->dma_area);
+	substream->runtime->dma_bytes = 0;
+	substream->runtime->dma_area = vmalloc(bytes);
+	if (substream->runtime->dma_area == NULL)
+		return -ENOMEM;
+	substream->runtime->dma_bytes = bytes;
+	go->audio_deliver = parse_audio_stream_data;
+	return 0;
+}
+
+static int go7007_snd_hw_free(struct snd_pcm_substream *substream)
+{
+	struct go7007 *go = snd_pcm_substream_chip(substream);
+
+	go->audio_deliver = NULL;
+	if (substream->runtime->dma_bytes > 0)
+		vfree(substream->runtime->dma_area);
+	substream->runtime->dma_bytes = 0;
+	return 0;
+}
+
+static int go7007_snd_capture_open(struct snd_pcm_substream *substream)
+{
+	struct go7007 *go = snd_pcm_substream_chip(substream);
+	struct go7007_snd *gosnd = go->snd_context;
+	unsigned long flags;
+	int r;
+
+	spin_lock_irqsave(&gosnd->lock, flags);
+	if (gosnd->substream == NULL) {
+		gosnd->substream = substream;
+		substream->runtime->hw = go7007_snd_capture_hw;
+		r = 0;
+	} else
+		r = -EBUSY;
+	spin_unlock_irqrestore(&gosnd->lock, flags);
+	return r;
+}
+
+static int go7007_snd_capture_close(struct snd_pcm_substream *substream)
+{
+	struct go7007 *go = snd_pcm_substream_chip(substream);
+	struct go7007_snd *gosnd = go->snd_context;
+
+	gosnd->substream = NULL;
+	return 0;
+}
+
+static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct go7007 *go = snd_pcm_substream_chip(substream);
+	struct go7007_snd *gosnd = go->snd_context;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		/* Just set a flag to indicate we should signal ALSA when
+		 * sound comes in */
+		gosnd->capturing = 1;
+		return 0;
+	case SNDRV_PCM_TRIGGER_STOP:
+		gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
+		gosnd->capturing = 0;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream)
+{
+	struct go7007 *go = snd_pcm_substream_chip(substream);
+	struct go7007_snd *gosnd = go->snd_context;
+
+	return gosnd->hw_ptr;
+}
+
+static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream,
+					unsigned long offset)
+{
+	return vmalloc_to_page(substream->runtime->dma_area + offset);
+}
+
+static struct snd_pcm_ops go7007_snd_capture_ops = {
+	.open		= go7007_snd_capture_open,
+	.close		= go7007_snd_capture_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= go7007_snd_hw_params,
+	.hw_free	= go7007_snd_hw_free,
+	.prepare	= go7007_snd_pcm_prepare,
+	.trigger	= go7007_snd_pcm_trigger,
+	.pointer	= go7007_snd_pcm_pointer,
+	.page		= go7007_snd_pcm_page,
+};
+
+static int go7007_snd_free(struct snd_device *device)
+{
+	struct go7007 *go = device->device_data;
+
+	kfree(go->snd_context);
+	go->snd_context = NULL;
+	if (--go->ref_count == 0)
+		kfree(go);
+	return 0;
+}
+
+static struct snd_device_ops go7007_snd_device_ops = {
+	.dev_free	= go7007_snd_free,
+};
+
+int go7007_snd_init(struct go7007 *go)
+{
+	static int dev;
+	struct go7007_snd *gosnd;
+	int ret = 0;
+
+	if (dev >= SNDRV_CARDS)
+		return -ENODEV;
+	if (!enable[dev]) {
+		dev++;
+		return -ENOENT;
+	}
+	gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL);
+	if (gosnd == NULL)
+		return -ENOMEM;
+	spin_lock_init(&gosnd->lock);
+	gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0;
+	gosnd->capturing = 0;
+	gosnd->card = snd_card_new(index[dev], id[dev], THIS_MODULE, 0);
+	if (gosnd->card == NULL) {
+		kfree(gosnd);
+		return -ENOMEM;
+	}
+	ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go,
+			&go7007_snd_device_ops);
+	if (ret < 0) {
+		kfree(gosnd);
+		return ret;
+	}
+	snd_card_set_dev(gosnd->card, go->dev);
+	ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm);
+	if (ret < 0) {
+		snd_card_free(gosnd->card);
+		kfree(gosnd);
+		return ret;
+	}
+	strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver));
+	strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver));
+	strncpy(gosnd->card->longname, gosnd->card->shortname,
+			sizeof(gosnd->card->longname));
+
+	gosnd->pcm->private_data = go;
+	snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE,
+			&go7007_snd_capture_ops);
+
+	ret = snd_card_register(gosnd->card);
+	if (ret < 0) {
+		snd_card_free(gosnd->card);
+		kfree(gosnd);
+		return ret;
+	}
+
+	gosnd->substream = NULL;
+	go->snd_context = gosnd;
+	++dev;
+	++go->ref_count;
+
+	return 0;
+}
+EXPORT_SYMBOL(go7007_snd_init);
+
+int go7007_snd_remove(struct go7007 *go)
+{
+	struct go7007_snd *gosnd = go->snd_context;
+
+	snd_card_disconnect(gosnd->card);
+	snd_card_free_when_closed(gosnd->card);
+	return 0;
+}
+EXPORT_SYMBOL(go7007_snd_remove);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/wis-i2c.h b/drivers/staging/go7007/wis-i2c.h
new file mode 100644
index 0000000..993f658
--- /dev/null
+++ b/drivers/staging/go7007/wis-i2c.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+/* Temporary I2C IDs -- these need to be replaced with real registered IDs */
+#define	I2C_DRIVERID_WIS_SAA7115	0xf0f0
+#define	I2C_DRIVERID_WIS_UDA1342	0xf0f1
+#define	I2C_DRIVERID_WIS_SONY_TUNER	0xf0f2
+#define	I2C_DRIVERID_WIS_TW9903		0xf0f3
+#define	I2C_DRIVERID_WIS_SAA7113	0xf0f4
+#define	I2C_DRIVERID_WIS_OV7640		0xf0f5
+#define	I2C_DRIVERID_WIS_TW2804		0xf0f6
+#define	I2C_ALGO_GO7007			0xf00000
+#define	I2C_ALGO_GO7007_USB		0xf10000
+
+/* Flag to indicate that the client needs to be accessed with SCCB semantics */
+/* We re-use the I2C_M_TEN value so the flag passes through the masks in the
+ * core I2C code.  Major kludge, but the I2C layer ain't exactly flexible. */
+#define	I2C_CLIENT_SCCB			0x10
+
+typedef int (*found_proc) (struct i2c_adapter *, int, int);
+int wis_i2c_add_driver(unsigned int id, found_proc found_proc);
+void wis_i2c_del_driver(found_proc found_proc);
+
+int wis_i2c_probe_device(struct i2c_adapter *adapter,
+				unsigned int id, int addr);
+
+/* Definitions for new video decoder commands */
+
+struct video_decoder_resolution {
+	unsigned int width;
+	unsigned int height;
+};
+
+#define	DECODER_SET_RESOLUTION	_IOW('d', 200, struct video_decoder_resolution)
+#define	DECODER_SET_CHANNEL	_IOW('d', 201, int)
+
+/* Sony tuner types */
+
+#define TUNER_SONY_BTF_PG472Z		200
+#define TUNER_SONY_BTF_PK467Z		201
+#define TUNER_SONY_BTF_PB463Z		202
diff --git a/drivers/staging/go7007/wis-ov7640.c b/drivers/staging/go7007/wis-ov7640.c
new file mode 100644
index 0000000..815615a
--- /dev/null
+++ b/drivers/staging/go7007/wis-ov7640.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+
+#include "wis-i2c.h"
+
+struct wis_ov7640 {
+	int brightness;
+	int contrast;
+	int saturation;
+	int hue;
+};
+
+static u8 initial_registers[] =
+{
+	0x12, 0x80,
+	0x12, 0x54,
+	0x14, 0x24,
+	0x15, 0x01,
+	0x28, 0x20,
+	0x75, 0x82,
+	0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */
+};
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+	int i;
+
+	for (i = 0; regs[i] != 0xFF; i += 2)
+		if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+			return -1;
+	return 0;
+}
+
+static struct i2c_driver wis_ov7640_driver;
+
+static struct i2c_client wis_ov7640_client_templ = {
+	.name		= "OV7640 (WIS)",
+	.driver		= &wis_ov7640_driver,
+};
+
+static int wis_ov7640_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+	struct i2c_client *client;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+	memcpy(client, &wis_ov7640_client_templ,
+			sizeof(wis_ov7640_client_templ));
+	client->adapter = adapter;
+	client->addr = addr;
+	client->flags = I2C_CLIENT_SCCB;
+
+	printk(KERN_DEBUG
+		"wis-ov7640: initializing OV7640 at address %d on %s\n",
+		addr, adapter->name);
+
+	if (write_regs(client, initial_registers) < 0) {
+		printk(KERN_ERR "wis-ov7640: error initializing OV7640\n");
+		kfree(client);
+		return 0;
+	}
+
+	i2c_attach_client(client);
+	return 0;
+}
+
+static int wis_ov7640_detach(struct i2c_client *client)
+{
+	int r;
+
+	r = i2c_detach_client(client);
+	if (r < 0)
+		return r;
+
+	kfree(client);
+	return 0;
+}
+
+static struct i2c_driver wis_ov7640_driver = {
+	.driver = {
+		.name	= "WIS OV7640 I2C driver",
+	},
+	.id		= I2C_DRIVERID_WIS_OV7640,
+	.detach_client	= wis_ov7640_detach,
+};
+
+static int __init wis_ov7640_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wis_ov7640_driver);
+	if (r < 0)
+		return r;
+	return wis_i2c_add_driver(wis_ov7640_driver.id, wis_ov7640_detect);
+}
+
+static void __exit wis_ov7640_cleanup(void)
+{
+	wis_i2c_del_driver(wis_ov7640_detect);
+	i2c_del_driver(&wis_ov7640_driver);
+}
+
+module_init(wis_ov7640_init);
+module_exit(wis_ov7640_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/wis-saa7113.c b/drivers/staging/go7007/wis-saa7113.c
new file mode 100644
index 0000000..4b14ca8
--- /dev/null
+++ b/drivers/staging/go7007/wis-saa7113.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+#include <linux/ioctl.h>
+
+#include "wis-i2c.h"
+
+struct wis_saa7113 {
+	int norm;
+	int brightness;
+	int contrast;
+	int saturation;
+	int hue;
+};
+
+static u8 initial_registers[] =
+{
+	0x01, 0x08,
+	0x02, 0xc0,
+	0x03, 0x33,
+	0x04, 0x00,
+	0x05, 0x00,
+	0x06, 0xe9,
+	0x07, 0x0d,
+	0x08, 0xd8,
+	0x09, 0x40,
+	0x0a, 0x80,
+	0x0b, 0x47,
+	0x0c, 0x40,
+	0x0d, 0x00,
+	0x0e, 0x01,
+	0x0f, 0x2a,
+	0x10, 0x40,
+	0x11, 0x0c,
+	0x12, 0xfe,
+	0x13, 0x00,
+	0x14, 0x00,
+	0x15, 0x04,
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0x00,
+	0x19, 0x00,
+	0x1a, 0x00,
+	0x1b, 0x00,
+	0x1c, 0x00,
+	0x1d, 0x00,
+	0x1e, 0x00,
+	0x1f, 0xc8,
+	0x40, 0x00,
+	0x41, 0xff,
+	0x42, 0xff,
+	0x43, 0xff,
+	0x44, 0xff,
+	0x45, 0xff,
+	0x46, 0xff,
+	0x47, 0xff,
+	0x48, 0xff,
+	0x49, 0xff,
+	0x4a, 0xff,
+	0x4b, 0xff,
+	0x4c, 0xff,
+	0x4d, 0xff,
+	0x4e, 0xff,
+	0x4f, 0xff,
+	0x50, 0xff,
+	0x51, 0xff,
+	0x52, 0xff,
+	0x53, 0xff,
+	0x54, 0xff,
+	0x55, 0xff,
+	0x56, 0xff,
+	0x57, 0xff,
+	0x58, 0x00,
+	0x59, 0x54,
+	0x5a, 0x07,
+	0x5b, 0x83,
+	0x5c, 0x00,
+	0x5d, 0x00,
+	0x5e, 0x00,
+	0x5f, 0x00,
+	0x60, 0x00,
+	0x61, 0x00,
+	0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+	int i;
+
+	for (i = 0; regs[i] != 0x00; i += 2)
+		if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+			return -1;
+	return 0;
+}
+
+static int wis_saa7113_command(struct i2c_client *client,
+				unsigned int cmd, void *arg)
+{
+	struct wis_saa7113 *dec = i2c_get_clientdata(client);
+
+	switch (cmd) {
+	case DECODER_SET_INPUT:
+	{
+		int *input = arg;
+
+		i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
+		i2c_smbus_write_byte_data(client, 0x09,
+				*input < 6 ? 0x40 : 0x80);
+		break;
+	}
+	case DECODER_SET_NORM:
+	{
+		int *input = arg;
+		dec->norm = *input;
+		switch (dec->norm) {
+		case VIDEO_MODE_PAL:
+			write_reg(client, 0x0e, 0x01);
+			write_reg(client, 0x10, 0x48);
+			break;
+		case VIDEO_MODE_NTSC:
+			write_reg(client, 0x0e, 0x01);
+			write_reg(client, 0x10, 0x40);
+			break;
+		case VIDEO_MODE_SECAM:
+			write_reg(client, 0x0e, 0x50);
+			write_reg(client, 0x10, 0x48);
+			break;
+		}
+		break;
+	}
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 128;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 71;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_SATURATION:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 64;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_HUE:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+			ctrl->minimum = -128;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 0;
+			ctrl->flags = 0;
+			break;
+		}
+		break;
+	}
+	case VIDIOC_S_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			if (ctrl->value > 255)
+				dec->brightness = 255;
+			else if (ctrl->value < 0)
+				dec->brightness = 0;
+			else
+				dec->brightness = ctrl->value;
+			write_reg(client, 0x0a, dec->brightness);
+			break;
+		case V4L2_CID_CONTRAST:
+			if (ctrl->value > 127)
+				dec->contrast = 127;
+			else if (ctrl->value < 0)
+				dec->contrast = 0;
+			else
+				dec->contrast = ctrl->value;
+			write_reg(client, 0x0b, dec->contrast);
+			break;
+		case V4L2_CID_SATURATION:
+			if (ctrl->value > 127)
+				dec->saturation = 127;
+			else if (ctrl->value < 0)
+				dec->saturation = 0;
+			else
+				dec->saturation = ctrl->value;
+			write_reg(client, 0x0c, dec->saturation);
+			break;
+		case V4L2_CID_HUE:
+			if (ctrl->value > 127)
+				dec->hue = 127;
+			else if (ctrl->value < -128)
+				dec->hue = -128;
+			else
+				dec->hue = ctrl->value;
+			write_reg(client, 0x0d, dec->hue);
+			break;
+		}
+		break;
+	}
+	case VIDIOC_G_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->value = dec->brightness;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->value = dec->contrast;
+			break;
+		case V4L2_CID_SATURATION:
+			ctrl->value = dec->saturation;
+			break;
+		case V4L2_CID_HUE:
+			ctrl->value = dec->hue;
+			break;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+	return 0;
+}
+
+static struct i2c_driver wis_saa7113_driver;
+
+static struct i2c_client wis_saa7113_client_templ = {
+	.name		= "SAA7113 (WIS)",
+	.driver		= &wis_saa7113_driver,
+};
+
+static int wis_saa7113_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+	struct i2c_client *client;
+	struct wis_saa7113 *dec;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+	memcpy(client, &wis_saa7113_client_templ,
+			sizeof(wis_saa7113_client_templ));
+	client->adapter = adapter;
+	client->addr = addr;
+
+	dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL);
+	if (dec == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	dec->norm = VIDEO_MODE_NTSC;
+	dec->brightness = 128;
+	dec->contrast = 71;
+	dec->saturation = 64;
+	dec->hue = 0;
+	i2c_set_clientdata(client, dec);
+
+	printk(KERN_DEBUG
+		"wis-saa7113: initializing SAA7113 at address %d on %s\n",
+		addr, adapter->name);
+
+	if (write_regs(client, initial_registers) < 0) {
+		printk(KERN_ERR
+			"wis-saa7113: error initializing SAA7113\n");
+		kfree(client);
+		kfree(dec);
+		return 0;
+	}
+
+	i2c_attach_client(client);
+	return 0;
+}
+
+static int wis_saa7113_detach(struct i2c_client *client)
+{
+	struct wis_saa7113 *dec = i2c_get_clientdata(client);
+	int r;
+
+	r = i2c_detach_client(client);
+	if (r < 0)
+		return r;
+
+	kfree(client);
+	kfree(dec);
+	return 0;
+}
+
+static struct i2c_driver wis_saa7113_driver = {
+	.driver = {
+		.name	= "WIS SAA7113 I2C driver",
+	},
+	.id		= I2C_DRIVERID_WIS_SAA7113,
+	.detach_client	= wis_saa7113_detach,
+	.command	= wis_saa7113_command,
+};
+
+static int __init wis_saa7113_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wis_saa7113_driver);
+	if (r < 0)
+		return r;
+	return wis_i2c_add_driver(wis_saa7113_driver.id, wis_saa7113_detect);
+}
+
+static void __exit wis_saa7113_cleanup(void)
+{
+	wis_i2c_del_driver(wis_saa7113_detect);
+	i2c_del_driver(&wis_saa7113_driver);
+}
+
+module_init(wis_saa7113_init);
+module_exit(wis_saa7113_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/wis-saa7115.c b/drivers/staging/go7007/wis-saa7115.c
new file mode 100644
index 0000000..bd40bf4
--- /dev/null
+++ b/drivers/staging/go7007/wis-saa7115.c
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+#include <linux/ioctl.h>
+
+#include "wis-i2c.h"
+
+struct wis_saa7115 {
+	int norm;
+	int brightness;
+	int contrast;
+	int saturation;
+	int hue;
+};
+
+static u8 initial_registers[] =
+{
+	0x01, 0x08,
+	0x02, 0xc0,
+	0x03, 0x20,
+	0x04, 0x80,
+	0x05, 0x80,
+	0x06, 0xeb,
+	0x07, 0xe0,
+	0x08, 0xf0,	/* always toggle FID */
+	0x09, 0x40,
+	0x0a, 0x80,
+	0x0b, 0x40,
+	0x0c, 0x40,
+	0x0d, 0x00,
+	0x0e, 0x03,
+	0x0f, 0x2a,
+	0x10, 0x0e,
+	0x11, 0x00,
+	0x12, 0x8d,
+	0x13, 0x00,
+	0x14, 0x00,
+	0x15, 0x11,
+	0x16, 0x01,
+	0x17, 0xda,
+	0x18, 0x40,
+	0x19, 0x80,
+	0x1a, 0x00,
+	0x1b, 0x42,
+	0x1c, 0xa9,
+	0x30, 0x66,
+	0x31, 0x90,
+	0x32, 0x01,
+	0x34, 0x00,
+	0x35, 0x00,
+	0x36, 0x20,
+	0x38, 0x03,
+	0x39, 0x20,
+	0x3a, 0x88,
+	0x40, 0x00,
+	0x41, 0xff,
+	0x42, 0xff,
+	0x43, 0xff,
+	0x44, 0xff,
+	0x45, 0xff,
+	0x46, 0xff,
+	0x47, 0xff,
+	0x48, 0xff,
+	0x49, 0xff,
+	0x4a, 0xff,
+	0x4b, 0xff,
+	0x4c, 0xff,
+	0x4d, 0xff,
+	0x4e, 0xff,
+	0x4f, 0xff,
+	0x50, 0xff,
+	0x51, 0xff,
+	0x52, 0xff,
+	0x53, 0xff,
+	0x54, 0xf4 /*0xff*/,
+	0x55, 0xff,
+	0x56, 0xff,
+	0x57, 0xff,
+	0x58, 0x40,
+	0x59, 0x47,
+	0x5a, 0x06 /*0x03*/,
+	0x5b, 0x83,
+	0x5d, 0x06,
+	0x5e, 0x00,
+	0x80, 0x30, /* window defined scaler operation, task A and B enabled */
+	0x81, 0x03, /* use scaler datapath generated V */
+	0x83, 0x00,
+	0x84, 0x00,
+	0x85, 0x00,
+	0x86, 0x45,
+	0x87, 0x31,
+	0x88, 0xc0,
+	0x90, 0x02, /* task A process top field */
+	0x91, 0x08,
+	0x92, 0x09,
+	0x93, 0x80,
+	0x94, 0x06,
+	0x95, 0x00,
+	0x96, 0xc0,
+	0x97, 0x02,
+	0x98, 0x12,
+	0x99, 0x00,
+	0x9a, 0xf2,
+	0x9b, 0x00,
+	0x9c, 0xd0,
+	0x9d, 0x02,
+	0x9e, 0xf2,
+	0x9f, 0x00,
+	0xa0, 0x01,
+	0xa1, 0x01,
+	0xa2, 0x01,
+	0xa4, 0x80,
+	0xa5, 0x40,
+	0xa6, 0x40,
+	0xa8, 0x00,
+	0xa9, 0x04,
+	0xaa, 0x00,
+	0xac, 0x00,
+	0xad, 0x02,
+	0xae, 0x00,
+	0xb0, 0x00,
+	0xb1, 0x04,
+	0xb2, 0x00,
+	0xb3, 0x04,
+	0xb4, 0x00,
+	0xb8, 0x00,
+	0xbc, 0x00,
+	0xc0, 0x03,	/* task B process bottom field */
+	0xc1, 0x08,
+	0xc2, 0x09,
+	0xc3, 0x80,
+	0xc4, 0x06,
+	0xc5, 0x00,
+	0xc6, 0xc0,
+	0xc7, 0x02,
+	0xc8, 0x12,
+	0xc9, 0x00,
+	0xca, 0xf2,
+	0xcb, 0x00,
+	0xcc, 0xd0,
+	0xcd, 0x02,
+	0xce, 0xf2,
+	0xcf, 0x00,
+	0xd0, 0x01,
+	0xd1, 0x01,
+	0xd2, 0x01,
+	0xd4, 0x80,
+	0xd5, 0x40,
+	0xd6, 0x40,
+	0xd8, 0x00,
+	0xd9, 0x04,
+	0xda, 0x00,
+	0xdc, 0x00,
+	0xdd, 0x02,
+	0xde, 0x00,
+	0xe0, 0x00,
+	0xe1, 0x04,
+	0xe2, 0x00,
+	0xe3, 0x04,
+	0xe4, 0x00,
+	0xe8, 0x00,
+	0x88, 0xf0, /* End of original static list */
+	0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+	int i;
+
+	for (i = 0; regs[i] != 0x00; i += 2)
+		if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+			return -1;
+	return 0;
+}
+
+static int wis_saa7115_command(struct i2c_client *client,
+				unsigned int cmd, void *arg)
+{
+	struct wis_saa7115 *dec = i2c_get_clientdata(client);
+
+	switch (cmd) {
+	case DECODER_SET_INPUT:
+	{
+		int *input = arg;
+
+		i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input);
+		i2c_smbus_write_byte_data(client, 0x09,
+				*input < 6 ? 0x40 : 0xC0);
+		break;
+	}
+	case DECODER_SET_RESOLUTION:
+	{
+		struct video_decoder_resolution *res = arg;
+		/* Course-grained scaler */
+		int h_integer_scaler = res->width < 704 ? 704 / res->width : 1;
+		/* Fine-grained scaler to take care of remainder */
+		int h_scaling_increment = (704 / h_integer_scaler) *
+					1024 / res->width;
+		/* Fine-grained scaler only */
+		int v_scaling_increment = (dec->norm == VIDEO_MODE_NTSC ?
+				240 : 288) * 1024 / res->height;
+		u8 regs[] = {
+			0x88,	0xc0,
+			0x9c,	res->width & 0xff,
+			0x9d,	res->width >> 8,
+			0x9e,	res->height & 0xff,
+			0x9f,	res->height >> 8,
+			0xa0,	h_integer_scaler,
+			0xa1,	1,
+			0xa2,	1,
+			0xa8,	h_scaling_increment & 0xff,
+			0xa9,	h_scaling_increment >> 8,
+			0xac,	(h_scaling_increment / 2) & 0xff,
+			0xad,	(h_scaling_increment / 2) >> 8,
+			0xb0,	v_scaling_increment & 0xff,
+			0xb1,	v_scaling_increment >> 8,
+			0xb2,	v_scaling_increment & 0xff,
+			0xb3,	v_scaling_increment >> 8,
+			0xcc,	res->width & 0xff,
+			0xcd,	res->width >> 8,
+			0xce,	res->height & 0xff,
+			0xcf,	res->height >> 8,
+			0xd0,	h_integer_scaler,
+			0xd1,	1,
+			0xd2,	1,
+			0xd8,	h_scaling_increment & 0xff,
+			0xd9,	h_scaling_increment >> 8,
+			0xdc,	(h_scaling_increment / 2) & 0xff,
+			0xdd,	(h_scaling_increment / 2) >> 8,
+			0xe0,	v_scaling_increment & 0xff,
+			0xe1,	v_scaling_increment >> 8,
+			0xe2,	v_scaling_increment & 0xff,
+			0xe3,	v_scaling_increment >> 8,
+			0x88,	0xf0,
+			0,	0,
+		};
+		write_regs(client, regs);
+		break;
+	}
+	case DECODER_SET_NORM:
+	{
+		int *input = arg;
+		u8 regs[] = {
+			0x88,	0xc0,
+			0x98,	*input == VIDEO_MODE_NTSC ? 0x12 : 0x16,
+			0x9a,	*input == VIDEO_MODE_NTSC ? 0xf2 : 0x20,
+			0x9b,	*input == VIDEO_MODE_NTSC ? 0x00 : 0x01,
+			0xc8,	*input == VIDEO_MODE_NTSC ? 0x12 : 0x16,
+			0xca,	*input == VIDEO_MODE_NTSC ? 0xf2 : 0x20,
+			0xcb,	*input == VIDEO_MODE_NTSC ? 0x00 : 0x01,
+			0x88,	0xf0,
+			0x30,	*input == VIDEO_MODE_NTSC ? 0x66 : 0x00,
+			0x31,	*input == VIDEO_MODE_NTSC ? 0x90 : 0xe0,
+			0,	0,
+		};
+		write_regs(client, regs);
+		dec->norm = *input;
+		break;
+	}
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 128;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 64;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_SATURATION:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 64;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_HUE:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+			ctrl->minimum = -128;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 0;
+			ctrl->flags = 0;
+			break;
+		}
+		break;
+	}
+	case VIDIOC_S_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			if (ctrl->value > 255)
+				dec->brightness = 255;
+			else if (ctrl->value < 0)
+				dec->brightness = 0;
+			else
+				dec->brightness = ctrl->value;
+			write_reg(client, 0x0a, dec->brightness);
+			break;
+		case V4L2_CID_CONTRAST:
+			if (ctrl->value > 127)
+				dec->contrast = 127;
+			else if (ctrl->value < 0)
+				dec->contrast = 0;
+			else
+				dec->contrast = ctrl->value;
+			write_reg(client, 0x0b, dec->contrast);
+			break;
+		case V4L2_CID_SATURATION:
+			if (ctrl->value > 127)
+				dec->saturation = 127;
+			else if (ctrl->value < 0)
+				dec->saturation = 0;
+			else
+				dec->saturation = ctrl->value;
+			write_reg(client, 0x0c, dec->saturation);
+			break;
+		case V4L2_CID_HUE:
+			if (ctrl->value > 127)
+				dec->hue = 127;
+			else if (ctrl->value < -128)
+				dec->hue = -128;
+			else
+				dec->hue = ctrl->value;
+			write_reg(client, 0x0d, dec->hue);
+			break;
+		}
+		break;
+	}
+	case VIDIOC_G_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->value = dec->brightness;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->value = dec->contrast;
+			break;
+		case V4L2_CID_SATURATION:
+			ctrl->value = dec->saturation;
+			break;
+		case V4L2_CID_HUE:
+			ctrl->value = dec->hue;
+			break;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+	return 0;
+}
+
+static struct i2c_driver wis_saa7115_driver;
+
+static struct i2c_client wis_saa7115_client_templ = {
+	.name		= "SAA7115 (WIS)",
+	.driver		= &wis_saa7115_driver,
+};
+
+static int wis_saa7115_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+	struct i2c_client *client;
+	struct wis_saa7115 *dec;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+	memcpy(client, &wis_saa7115_client_templ,
+			sizeof(wis_saa7115_client_templ));
+	client->adapter = adapter;
+	client->addr = addr;
+
+	dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL);
+	if (dec == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	dec->norm = VIDEO_MODE_NTSC;
+	dec->brightness = 128;
+	dec->contrast = 64;
+	dec->saturation = 64;
+	dec->hue = 0;
+	i2c_set_clientdata(client, dec);
+
+	printk(KERN_DEBUG
+		"wis-saa7115: initializing SAA7115 at address %d on %s\n",
+		addr, adapter->name);
+
+	if (write_regs(client, initial_registers) < 0) {
+		printk(KERN_ERR
+			"wis-saa7115: error initializing SAA7115\n");
+		kfree(client);
+		kfree(dec);
+		return 0;
+	}
+
+	i2c_attach_client(client);
+	return 0;
+}
+
+static int wis_saa7115_detach(struct i2c_client *client)
+{
+	struct wis_saa7115 *dec = i2c_get_clientdata(client);
+	int r;
+
+	r = i2c_detach_client(client);
+	if (r < 0)
+		return r;
+
+	kfree(client);
+	kfree(dec);
+	return 0;
+}
+
+static struct i2c_driver wis_saa7115_driver = {
+	.driver = {
+		.name	= "WIS SAA7115 I2C driver",
+	},
+	.id		= I2C_DRIVERID_WIS_SAA7115,
+	.detach_client	= wis_saa7115_detach,
+	.command	= wis_saa7115_command,
+};
+
+static int __init wis_saa7115_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wis_saa7115_driver);
+	if (r < 0)
+		return r;
+	return wis_i2c_add_driver(wis_saa7115_driver.id, wis_saa7115_detect);
+}
+
+static void __exit wis_saa7115_cleanup(void)
+{
+	wis_i2c_del_driver(wis_saa7115_detect);
+	i2c_del_driver(&wis_saa7115_driver);
+}
+
+module_init(wis_saa7115_init);
+module_exit(wis_saa7115_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/wis-sony-tuner.c b/drivers/staging/go7007/wis-sony-tuner.c
new file mode 100644
index 0000000..82e66d6
--- /dev/null
+++ b/drivers/staging/go7007/wis-sony-tuner.c
@@ -0,0 +1,741 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+
+#include "wis-i2c.h"
+
+/* #define MPX_DEBUG */
+
+/* AS(IF/MPX) pin:      LOW      HIGH/OPEN
+ * IF/MPX address:   0x42/0x40   0x43/0x44
+ */
+#define IF_I2C_ADDR	0x43
+#define MPX_I2C_ADDR	0x44
+
+static v4l2_std_id force_band;
+static char force_band_str[] = "-";
+module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644);
+static int force_mpx_mode = -1;
+module_param(force_mpx_mode, int, 0644);
+
+/* Store tuner info in the same format as tuner.c, so maybe we can put the
+ * Sony tuner support in there. */
+struct sony_tunertype {
+	char *name;
+	unsigned char Vendor; /* unused here */
+	unsigned char Type; /* unused here */
+
+	unsigned short thresh1; /*  band switch VHF_LO <=> VHF_HI */
+	unsigned short thresh2; /*  band switch VHF_HI <=> UHF */
+	unsigned char VHF_L;
+	unsigned char VHF_H;
+	unsigned char UHF;
+	unsigned char config;
+	unsigned short IFPCoff;
+};
+
+/* This array is indexed by (tuner_type - 200) */
+static struct sony_tunertype sony_tuners[] = {
+	{ "Sony PAL+SECAM (BTF-PG472Z)", 0, 0,
+	  16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623},
+	{ "Sony NTSC_JP (BTF-PK467Z)", 0, 0,
+	  16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940},
+	{ "Sony NTSC (BTF-PB463Z)", 0, 0,
+	  16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732},
+};
+
+struct wis_sony_tuner {
+	int type;
+	v4l2_std_id std;
+	unsigned int freq;
+	int mpxmode;
+	u32 audmode;
+};
+
+/* Basically the same as default_set_tv_freq() in tuner.c */
+static int set_freq(struct i2c_client *client, int freq)
+{
+	struct wis_sony_tuner *t = i2c_get_clientdata(client);
+	char *band_name;
+	int n;
+	int band_select;
+	struct sony_tunertype *tun;
+	u8 buffer[4];
+
+	tun = &sony_tuners[t->type - 200];
+	if (freq < tun->thresh1) {
+		band_name = "VHF_L";
+		band_select = tun->VHF_L;
+	} else if (freq < tun->thresh2) {
+		band_name = "VHF_H";
+		band_select = tun->VHF_H;
+	} else {
+		band_name = "UHF";
+		band_select = tun->UHF;
+	}
+	printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n",
+			freq / 16, (freq % 16) * 625, band_name);
+	n = freq + tun->IFPCoff;
+
+	buffer[0] = n >> 8;
+	buffer[1] = n & 0xff;
+	buffer[2] = tun->config;
+	buffer[3] = band_select;
+	i2c_master_send(client, buffer, 4);
+
+	return 0;
+}
+
+static int mpx_write(struct i2c_client *client, int dev, int addr, int val)
+{
+	u8 buffer[5];
+	struct i2c_msg msg;
+
+	buffer[0] = dev;
+	buffer[1] = addr >> 8;
+	buffer[2] = addr & 0xff;
+	buffer[3] = val >> 8;
+	buffer[4] = val & 0xff;
+	msg.addr = MPX_I2C_ADDR;
+	msg.flags = 0;
+	msg.len = 5;
+	msg.buf = buffer;
+	i2c_transfer(client->adapter, &msg, 1);
+	return 0;
+}
+
+/*
+ * MPX register values for the BTF-PG472Z:
+ *
+ *                                 FM_     NICAM_  SCART_
+ *          MODUS  SOURCE    ACB   PRESCAL PRESCAL PRESCAL SYSTEM  VOLUME
+ *         10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000
+ *         ---------------------------------------------------------------
+ * Auto     1003    0020    0100    2603    5000    XXXX    0001    7500
+ *
+ * B/G
+ *  Mono    1003    0020    0100    2603    5000    XXXX    0003    7500
+ *  A2      1003    0020    0100    2601    5000    XXXX    0003    7500
+ *  NICAM   1003    0120    0100    2603    5000    XXXX    0008    7500
+ *
+ * I
+ *  Mono    1003    0020    0100    2603    7900    XXXX    000A    7500
+ *  NICAM   1003    0120    0100    2603    7900    XXXX    000A    7500
+ *
+ * D/K
+ *  Mono    1003    0020    0100    2603    5000    XXXX    0004    7500
+ *  A2-1    1003    0020    0100    2601    5000    XXXX    0004    7500
+ *  A2-2    1003    0020    0100    2601    5000    XXXX    0005    7500
+ *  A2-3    1003    0020    0100    2601    5000    XXXX    0007    7500
+ *  NICAM   1003    0120    0100    2603    5000    XXXX    000B    7500
+ *
+ * L/L'
+ *  Mono    0003    0200    0100    7C03    5000    2200    0009    7500
+ *  NICAM   0003    0120    0100    7C03    5000    XXXX    0009    7500
+ *
+ * M
+ *  Mono    1003    0200    0100    2B03    5000    2B00    0002    7500
+ *
+ * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX.
+ *
+ * Bilingual selection in A2/NICAM:
+ *
+ *         High byte of SOURCE     Left chan   Right chan
+ *                 0x01              MAIN         SUB
+ *                 0x03              MAIN         MAIN
+ *                 0x04              SUB          SUB
+ *
+ * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or
+ * 0x00 (all other bands).  Force mono in A2 with FMONO_A2:
+ *
+ *                      FMONO_A2
+ *                      10/0022
+ *                      --------
+ *     Forced mono ON     07F0
+ *     Forced mono OFF    0190
+ */
+
+static struct {
+	enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode;
+	u16 modus;
+	u16 source;
+	u16 acb;
+	u16 fm_prescale;
+	u16 nicam_prescale;
+	u16 scart_prescale;
+	u16 system;
+	u16 volume;
+} mpx_audio_modes[] = {
+	/* Auto */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603,
+					0x5000, 0x0000, 0x0001, 0x7500 },
+	/* B/G Mono */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603,
+					0x5000, 0x0000, 0x0003, 0x7500 },
+	/* B/G A2 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601,
+					0x5000, 0x0000, 0x0003, 0x7500 },
+	/* B/G NICAM */ { AUD_NICAM,	0x1003, 0x0120, 0x0100, 0x2603,
+					0x5000, 0x0000, 0x0008, 0x7500 },
+	/* I Mono */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603,
+					0x7900, 0x0000, 0x000A, 0x7500 },
+	/* I NICAM */	{ AUD_NICAM,	0x1003, 0x0120, 0x0100, 0x2603,
+					0x7900, 0x0000, 0x000A, 0x7500 },
+	/* D/K Mono */	{ AUD_MONO,	0x1003, 0x0020, 0x0100, 0x2603,
+					0x5000, 0x0000, 0x0004, 0x7500 },
+	/* D/K A2-1 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601,
+					0x5000, 0x0000, 0x0004, 0x7500 },
+	/* D/K A2-2 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601,
+					0x5000, 0x0000, 0x0005, 0x7500 },
+	/* D/K A2-3 */	{ AUD_A2,	0x1003, 0x0020, 0x0100, 0x2601,
+					0x5000, 0x0000, 0x0007, 0x7500 },
+	/* D/K NICAM */	{ AUD_NICAM,	0x1003, 0x0120, 0x0100, 0x2603,
+					0x5000, 0x0000, 0x000B, 0x7500 },
+	/* L/L' Mono */	{ AUD_MONO,	0x0003, 0x0200, 0x0100, 0x7C03,
+					0x5000, 0x2200, 0x0009, 0x7500 },
+	/* L/L' NICAM */{ AUD_NICAM_L,	0x0003, 0x0120, 0x0100, 0x7C03,
+					0x5000, 0x0000, 0x0009, 0x7500 },
+};
+
+#define MPX_NUM_MODES	ARRAY_SIZE(mpx_audio_modes)
+
+static int mpx_setup(struct i2c_client *client)
+{
+	struct wis_sony_tuner *t = i2c_get_clientdata(client);
+	u16 source = 0;
+	u8 buffer[3];
+	struct i2c_msg msg;
+
+	/* reset MPX */
+	buffer[0] = 0x00;
+	buffer[1] = 0x80;
+	buffer[2] = 0x00;
+	msg.addr = MPX_I2C_ADDR;
+	msg.flags = 0;
+	msg.len = 3;
+	msg.buf = buffer;
+	i2c_transfer(client->adapter, &msg, 1);
+	buffer[1] = 0x00;
+	i2c_transfer(client->adapter, &msg, 1);
+
+	if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) {
+		switch (t->audmode) {
+		case V4L2_TUNER_MODE_MONO:
+			switch (mpx_audio_modes[t->mpxmode].audio_mode) {
+			case AUD_A2:
+				source = mpx_audio_modes[t->mpxmode].source;
+				break;
+			case AUD_NICAM:
+				source = 0x0000;
+				break;
+			case AUD_NICAM_L:
+				source = 0x0200;
+				break;
+			default:
+				break;
+			}
+			break;
+		case V4L2_TUNER_MODE_STEREO:
+			source = mpx_audio_modes[t->mpxmode].source;
+			break;
+		case V4L2_TUNER_MODE_LANG1:
+			source = 0x0300;
+			break;
+		case V4L2_TUNER_MODE_LANG2:
+			source = 0x0400;
+			break;
+		}
+		source |= mpx_audio_modes[t->mpxmode].source & 0x00ff;
+	} else
+		source = mpx_audio_modes[t->mpxmode].source;
+
+	mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus);
+	mpx_write(client, 0x12, 0x0008, source);
+	mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb);
+	mpx_write(client, 0x12, 0x000e,
+			mpx_audio_modes[t->mpxmode].fm_prescale);
+	mpx_write(client, 0x12, 0x0010,
+			mpx_audio_modes[t->mpxmode].nicam_prescale);
+	mpx_write(client, 0x12, 0x000d,
+			mpx_audio_modes[t->mpxmode].scart_prescale);
+	mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system);
+	mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume);
+	if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2)
+		mpx_write(client, 0x10, 0x0022,
+			t->audmode == V4L2_TUNER_MODE_MONO ?  0x07f0 : 0x0190);
+
+#ifdef MPX_DEBUG
+	{
+		u8 buf1[3], buf2[2];
+		struct i2c_msg msgs[2];
+
+		printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x "
+				"%04x %04x %04x %04x %04x %04x\n",
+				mpx_audio_modes[t->mpxmode].modus,
+				source,
+				mpx_audio_modes[t->mpxmode].acb,
+				mpx_audio_modes[t->mpxmode].fm_prescale,
+				mpx_audio_modes[t->mpxmode].nicam_prescale,
+				mpx_audio_modes[t->mpxmode].scart_prescale,
+				mpx_audio_modes[t->mpxmode].system,
+				mpx_audio_modes[t->mpxmode].volume);
+		buf1[0] = 0x11;
+		buf1[1] = 0x00;
+		buf1[2] = 0x7e;
+		msgs[0].addr = MPX_I2C_ADDR;
+		msgs[0].flags = 0;
+		msgs[0].len = 3;
+		msgs[0].buf = buf1;
+		msgs[1].addr = MPX_I2C_ADDR;
+		msgs[1].flags = I2C_M_RD;
+		msgs[1].len = 2;
+		msgs[1].buf = buf2;
+		i2c_transfer(client->adapter, msgs, 2);
+		printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n",
+				buf2[0], buf2[1]);
+		buf1[0] = 0x11;
+		buf1[1] = 0x02;
+		buf1[2] = 0x00;
+		i2c_transfer(client->adapter, msgs, 2);
+		printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n",
+				buf2[0], buf2[1]);
+	}
+#endif
+	return 0;
+}
+
+/*
+ * IF configuration values for the BTF-PG472Z:
+ *
+ *	B/G: 0x94 0x70 0x49
+ *	I:   0x14 0x70 0x4a
+ *	D/K: 0x14 0x70 0x4b
+ *	L:   0x04 0x70 0x4b
+ *	L':  0x44 0x70 0x53
+ *	M:   0x50 0x30 0x4c
+ */
+
+static int set_if(struct i2c_client *client)
+{
+	struct wis_sony_tuner *t = i2c_get_clientdata(client);
+	u8 buffer[4];
+	struct i2c_msg msg;
+	int default_mpx_mode = 0;
+
+	/* configure IF */
+	buffer[0] = 0;
+	if (t->std & V4L2_STD_PAL_BG) {
+		buffer[1] = 0x94;
+		buffer[2] = 0x70;
+		buffer[3] = 0x49;
+		default_mpx_mode = 1;
+	} else if (t->std & V4L2_STD_PAL_I) {
+		buffer[1] = 0x14;
+		buffer[2] = 0x70;
+		buffer[3] = 0x4a;
+		default_mpx_mode = 4;
+	} else if (t->std & V4L2_STD_PAL_DK) {
+		buffer[1] = 0x14;
+		buffer[2] = 0x70;
+		buffer[3] = 0x4b;
+		default_mpx_mode = 6;
+	} else if (t->std & V4L2_STD_SECAM_L) {
+		buffer[1] = 0x04;
+		buffer[2] = 0x70;
+		buffer[3] = 0x4b;
+		default_mpx_mode = 11;
+	}
+	msg.addr = IF_I2C_ADDR;
+	msg.flags = 0;
+	msg.len = 4;
+	msg.buf = buffer;
+	i2c_transfer(client->adapter, &msg, 1);
+
+	/* Select MPX mode if not forced by the user */
+	if (force_mpx_mode >= 0 || force_mpx_mode < MPX_NUM_MODES)
+		t->mpxmode = force_mpx_mode;
+	else
+		t->mpxmode = default_mpx_mode;
+	printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n",
+			t->mpxmode);
+	mpx_setup(client);
+
+	return 0;
+}
+
+static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
+{
+	struct wis_sony_tuner *t = i2c_get_clientdata(client);
+
+	switch (cmd) {
+#ifdef TUNER_SET_TYPE_ADDR
+	case TUNER_SET_TYPE_ADDR:
+	{
+		struct tuner_setup *tun_setup = arg;
+		int *type = &tun_setup->type;
+#else
+	case TUNER_SET_TYPE:
+	{
+		int *type = arg;
+#endif
+
+		if (t->type >= 0) {
+			if (t->type != *type)
+				printk(KERN_ERR "wis-sony-tuner: type already "
+					"set to %d, ignoring request for %d\n",
+					t->type, *type);
+			break;
+		}
+		t->type = *type;
+		switch (t->type) {
+		case TUNER_SONY_BTF_PG472Z:
+			switch (force_band_str[0]) {
+			case 'b':
+			case 'B':
+			case 'g':
+			case 'G':
+				printk(KERN_INFO "wis-sony-tuner: forcing "
+						"tuner to PAL-B/G bands\n");
+				force_band = V4L2_STD_PAL_BG;
+				break;
+			case 'i':
+			case 'I':
+				printk(KERN_INFO "wis-sony-tuner: forcing "
+						"tuner to PAL-I band\n");
+				force_band = V4L2_STD_PAL_I;
+				break;
+			case 'd':
+			case 'D':
+			case 'k':
+			case 'K':
+				printk(KERN_INFO "wis-sony-tuner: forcing "
+						"tuner to PAL-D/K bands\n");
+				force_band = V4L2_STD_PAL_I;
+				break;
+			case 'l':
+			case 'L':
+				printk(KERN_INFO "wis-sony-tuner: forcing "
+						"tuner to SECAM-L band\n");
+				force_band = V4L2_STD_SECAM_L;
+				break;
+			default:
+				force_band = 0;
+				break;
+			}
+			if (force_band)
+				t->std = force_band;
+			else
+				t->std = V4L2_STD_PAL_BG;
+			set_if(client);
+			break;
+		case TUNER_SONY_BTF_PK467Z:
+			t->std = V4L2_STD_NTSC_M_JP;
+			break;
+		case TUNER_SONY_BTF_PB463Z:
+			t->std = V4L2_STD_NTSC_M;
+			break;
+		default:
+			printk(KERN_ERR "wis-sony-tuner: tuner type %d is not "
+					"supported by this module\n", *type);
+			break;
+		}
+		if (type >= 0)
+			printk(KERN_INFO
+				"wis-sony-tuner: type set to %d (%s)\n",
+				t->type, sony_tuners[t->type - 200].name);
+		break;
+	}
+	case VIDIOC_G_FREQUENCY:
+	{
+		struct v4l2_frequency *f = arg;
+
+		f->frequency = t->freq;
+		break;
+	}
+	case VIDIOC_S_FREQUENCY:
+	{
+		struct v4l2_frequency *f = arg;
+
+		t->freq = f->frequency;
+		set_freq(client, t->freq);
+		break;
+	}
+	case VIDIOC_ENUMSTD:
+	{
+		struct v4l2_standard *std = arg;
+
+		switch (t->type) {
+		case TUNER_SONY_BTF_PG472Z:
+			switch (std->index) {
+			case 0:
+				v4l2_video_std_construct(std,
+						V4L2_STD_PAL_BG, "PAL-B/G");
+				break;
+			case 1:
+				v4l2_video_std_construct(std,
+						V4L2_STD_PAL_I, "PAL-I");
+				break;
+			case 2:
+				v4l2_video_std_construct(std,
+						V4L2_STD_PAL_DK, "PAL-D/K");
+				break;
+			case 3:
+				v4l2_video_std_construct(std,
+						V4L2_STD_SECAM_L, "SECAM-L");
+				break;
+			default:
+				std->id = 0; /* hack to indicate EINVAL */
+				break;
+			}
+			break;
+		case TUNER_SONY_BTF_PK467Z:
+			if (std->index != 0) {
+				std->id = 0; /* hack to indicate EINVAL */
+				break;
+			}
+			v4l2_video_std_construct(std,
+					V4L2_STD_NTSC_M_JP, "NTSC-J");
+			break;
+		case TUNER_SONY_BTF_PB463Z:
+			if (std->index != 0) {
+				std->id = 0; /* hack to indicate EINVAL */
+				break;
+			}
+			v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC");
+			break;
+		}
+		break;
+	}
+	case VIDIOC_G_STD:
+	{
+		v4l2_std_id *std = arg;
+
+		*std = t->std;
+		break;
+	}
+	case VIDIOC_S_STD:
+	{
+		v4l2_std_id *std = arg;
+		v4l2_std_id old = t->std;
+
+		switch (t->type) {
+		case TUNER_SONY_BTF_PG472Z:
+			if (force_band && (*std & force_band) != *std &&
+					*std != V4L2_STD_PAL &&
+					*std != V4L2_STD_SECAM) {
+				printk(KERN_DEBUG "wis-sony-tuner: ignoring "
+						"requested TV standard in "
+						"favor of force_band value\n");
+				t->std = force_band;
+			} else if (*std & V4L2_STD_PAL_BG) { /* default */
+				t->std = V4L2_STD_PAL_BG;
+			} else if (*std & V4L2_STD_PAL_I) {
+				t->std = V4L2_STD_PAL_I;
+			} else if (*std & V4L2_STD_PAL_DK) {
+				t->std = V4L2_STD_PAL_DK;
+			} else if (*std & V4L2_STD_SECAM_L) {
+				t->std = V4L2_STD_SECAM_L;
+			} else {
+				printk(KERN_ERR "wis-sony-tuner: TV standard "
+						"not supported\n");
+				*std = 0; /* hack to indicate EINVAL */
+				break;
+			}
+			if (old != t->std)
+				set_if(client);
+			break;
+		case TUNER_SONY_BTF_PK467Z:
+			if (!(*std & V4L2_STD_NTSC_M_JP)) {
+				printk(KERN_ERR "wis-sony-tuner: TV standard "
+						"not supported\n");
+				*std = 0; /* hack to indicate EINVAL */
+			}
+			break;
+		case TUNER_SONY_BTF_PB463Z:
+			if (!(*std & V4L2_STD_NTSC_M)) {
+				printk(KERN_ERR "wis-sony-tuner: TV standard "
+						"not supported\n");
+				*std = 0; /* hack to indicate EINVAL */
+			}
+			break;
+		}
+		break;
+	}
+	case VIDIOC_QUERYSTD:
+	{
+		v4l2_std_id *std = arg;
+
+		switch (t->type) {
+		case TUNER_SONY_BTF_PG472Z:
+			if (force_band)
+				*std = force_band;
+			else
+				*std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I |
+					V4L2_STD_PAL_DK | V4L2_STD_SECAM_L;
+			break;
+		case TUNER_SONY_BTF_PK467Z:
+			*std = V4L2_STD_NTSC_M_JP;
+			break;
+		case TUNER_SONY_BTF_PB463Z:
+			*std = V4L2_STD_NTSC_M;
+			break;
+		}
+		break;
+	}
+	case VIDIOC_G_TUNER:
+	{
+		struct v4l2_tuner *tun = arg;
+
+		memset(t, 0, sizeof(*tun));
+		strcpy(tun->name, "Television");
+		tun->type = V4L2_TUNER_ANALOG_TV;
+		tun->rangelow = 0UL; /* does anything use these? */
+		tun->rangehigh = 0xffffffffUL;
+		switch (t->type) {
+		case TUNER_SONY_BTF_PG472Z:
+			tun->capability = V4L2_TUNER_CAP_NORM |
+				V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+				V4L2_TUNER_CAP_LANG2;
+			tun->rxsubchans = V4L2_TUNER_SUB_MONO |
+				V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 |
+				V4L2_TUNER_SUB_LANG2;
+			break;
+		case TUNER_SONY_BTF_PK467Z:
+		case TUNER_SONY_BTF_PB463Z:
+			tun->capability = V4L2_TUNER_CAP_STEREO;
+			tun->rxsubchans = V4L2_TUNER_SUB_MONO |
+						V4L2_TUNER_SUB_STEREO;
+			break;
+		}
+		tun->audmode = t->audmode;
+		return 0;
+	}
+	case VIDIOC_S_TUNER:
+	{
+		struct v4l2_tuner *tun = arg;
+
+		switch (t->type) {
+		case TUNER_SONY_BTF_PG472Z:
+			if (tun->audmode != t->audmode) {
+				t->audmode = tun->audmode;
+				mpx_setup(client);
+			}
+			break;
+		case TUNER_SONY_BTF_PK467Z:
+		case TUNER_SONY_BTF_PB463Z:
+			break;
+		}
+		return 0;
+	}
+	default:
+		break;
+	}
+	return 0;
+}
+
+static struct i2c_driver wis_sony_tuner_driver;
+
+static struct i2c_client wis_sony_tuner_client_templ = {
+	.name		= "Sony TV Tuner (WIS)",
+	.driver		= &wis_sony_tuner_driver,
+};
+
+static int wis_sony_tuner_detect(struct i2c_adapter *adapter,
+					int addr, int kind)
+{
+	struct i2c_client *client;
+	struct wis_sony_tuner *t;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+	memcpy(client, &wis_sony_tuner_client_templ,
+			sizeof(wis_sony_tuner_client_templ));
+	client->adapter = adapter;
+	client->addr = addr;
+
+	t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL);
+	if (t == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	t->type = -1;
+	t->freq = 0;
+	t->mpxmode = 0;
+	t->audmode = V4L2_TUNER_MODE_STEREO;
+	i2c_set_clientdata(client, t);
+
+	printk(KERN_DEBUG
+		"wis-sony-tuner: initializing tuner at address %d on %s\n",
+		addr, adapter->name);
+
+	i2c_attach_client(client);
+
+	return 0;
+}
+
+static int wis_sony_tuner_detach(struct i2c_client *client)
+{
+	struct wis_sony_tuner *t = i2c_get_clientdata(client);
+	int r;
+
+	r = i2c_detach_client(client);
+	if (r < 0)
+		return r;
+
+	kfree(t);
+	kfree(client);
+	return 0;
+}
+
+static struct i2c_driver wis_sony_tuner_driver = {
+	.driver = {
+		.name	= "WIS Sony TV Tuner I2C driver",
+	},
+	.id		= I2C_DRIVERID_WIS_SONY_TUNER,
+	.detach_client	= wis_sony_tuner_detach,
+	.command	= tuner_command,
+};
+
+static int __init wis_sony_tuner_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wis_sony_tuner_driver);
+	if (r < 0)
+		return r;
+	return wis_i2c_add_driver(wis_sony_tuner_driver.id,
+					wis_sony_tuner_detect);
+}
+
+static void __exit wis_sony_tuner_cleanup(void)
+{
+	wis_i2c_del_driver(wis_sony_tuner_detect);
+	i2c_del_driver(&wis_sony_tuner_driver);
+}
+
+module_init(wis_sony_tuner_init);
+module_exit(wis_sony_tuner_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/wis-tw2804.c b/drivers/staging/go7007/wis-tw2804.c
new file mode 100644
index 0000000..69ed7bf
--- /dev/null
+++ b/drivers/staging/go7007/wis-tw2804.c
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+#include <linux/ioctl.h>
+
+#include "wis-i2c.h"
+
+struct wis_tw2804 {
+	int channel;
+	int norm;
+	int brightness;
+	int contrast;
+	int saturation;
+	int hue;
+};
+
+static u8 global_registers[] =
+{
+	0x39, 0x00,
+	0x3a, 0xff,
+	0x3b, 0x84,
+	0x3c, 0x80,
+	0x3d, 0x80,
+	0x3e, 0x82,
+	0x3f, 0x82,
+	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
+};
+
+static u8 channel_registers[] =
+{
+	0x01, 0xc4,
+	0x02, 0xa5,
+	0x03, 0x20,
+	0x04, 0xd0,
+	0x05, 0x20,
+	0x06, 0xd0,
+	0x07, 0x88,
+	0x08, 0x20,
+	0x09, 0x07,
+	0x0a, 0xf0,
+	0x0b, 0x07,
+	0x0c, 0xf0,
+	0x0d, 0x40,
+	0x0e, 0xd2,
+	0x0f, 0x80,
+	0x10, 0x80,
+	0x11, 0x80,
+	0x12, 0x80,
+	0x13, 0x1f,
+	0x14, 0x00,
+	0x15, 0x00,
+	0x16, 0x00,
+	0x17, 0x00,
+	0x18, 0xff,
+	0x19, 0xff,
+	0x1a, 0xff,
+	0x1b, 0xff,
+	0x1c, 0xff,
+	0x1d, 0xff,
+	0x1e, 0xff,
+	0x1f, 0xff,
+	0x20, 0x07,
+	0x21, 0x07,
+	0x22, 0x00,
+	0x23, 0x91,
+	0x24, 0x51,
+	0x25, 0x03,
+	0x26, 0x00,
+	0x27, 0x00,
+	0x28, 0x00,
+	0x29, 0x00,
+	0x2a, 0x00,
+	0x2b, 0x00,
+	0x2c, 0x00,
+	0x2d, 0x00,
+	0x2e, 0x00,
+	0x2f, 0x00,
+	0x30, 0x00,
+	0x31, 0x00,
+	0x32, 0x00,
+	0x33, 0x00,
+	0x34, 0x00,
+	0x35, 0x00,
+	0x36, 0x00,
+	0x37, 0x00,
+	0xff, 0xff, /* Terminator (reg 0xff does not exist) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel)
+{
+	return i2c_smbus_write_byte_data(client, reg | (channel << 6), value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs, int channel)
+{
+	int i;
+
+	for (i = 0; regs[i] != 0xff; i += 2)
+		if (i2c_smbus_write_byte_data(client,
+				regs[i] | (channel << 6), regs[i + 1]) < 0)
+			return -1;
+	return 0;
+}
+
+static int wis_tw2804_command(struct i2c_client *client,
+				unsigned int cmd, void *arg)
+{
+	struct wis_tw2804 *dec = i2c_get_clientdata(client);
+
+	if (cmd == DECODER_SET_CHANNEL) {
+		int *input = arg;
+
+		if (*input < 0 || *input > 3) {
+			printk(KERN_ERR "wis-tw2804: channel %d is not "
+					"between 0 and 3!\n", *input);
+			return 0;
+		}
+		dec->channel = *input;
+		printk(KERN_DEBUG "wis-tw2804: initializing TW2804 "
+				"channel %d\n", dec->channel);
+		if (dec->channel == 0 &&
+				write_regs(client, global_registers, 0) < 0) {
+			printk(KERN_ERR "wis-tw2804: error initializing "
+					"TW2804 global registers\n");
+			return 0;
+		}
+		if (write_regs(client, channel_registers, dec->channel) < 0) {
+			printk(KERN_ERR "wis-tw2804: error initializing "
+					"TW2804 channel %d\n", dec->channel);
+			return 0;
+		}
+		return 0;
+	}
+
+	if (dec->channel < 0) {
+		printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until "
+				"channel number is set\n", cmd);
+		return 0;
+	}
+
+	switch (cmd) {
+	case DECODER_SET_NORM:
+	{
+		int *input = arg;
+		u8 regs[] = {
+			0x01, *input == VIDEO_MODE_NTSC ? 0xc4 : 0x84,
+			0x09, *input == VIDEO_MODE_NTSC ? 0x07 : 0x04,
+			0x0a, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
+			0x0b, *input == VIDEO_MODE_NTSC ? 0x07 : 0x04,
+			0x0c, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
+			0x0d, *input == VIDEO_MODE_NTSC ? 0x40 : 0x4a,
+			0x16, *input == VIDEO_MODE_NTSC ? 0x00 : 0x40,
+			0x17, *input == VIDEO_MODE_NTSC ? 0x00 : 0x40,
+			0x20, *input == VIDEO_MODE_NTSC ? 0x07 : 0x0f,
+			0x21, *input == VIDEO_MODE_NTSC ? 0x07 : 0x0f,
+			0xff,	0xff,
+		};
+		write_regs(client, regs, dec->channel);
+		dec->norm = *input;
+		break;
+	}
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 128;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 128;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_SATURATION:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 128;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_HUE:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 128;
+			ctrl->flags = 0;
+			break;
+		}
+		break;
+	}
+	case VIDIOC_S_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			if (ctrl->value > 255)
+				dec->brightness = 255;
+			else if (ctrl->value < 0)
+				dec->brightness = 0;
+			else
+				dec->brightness = ctrl->value;
+			write_reg(client, 0x12, dec->brightness, dec->channel);
+			break;
+		case V4L2_CID_CONTRAST:
+			if (ctrl->value > 255)
+				dec->contrast = 255;
+			else if (ctrl->value < 0)
+				dec->contrast = 0;
+			else
+				dec->contrast = ctrl->value;
+			write_reg(client, 0x11, dec->contrast, dec->channel);
+			break;
+		case V4L2_CID_SATURATION:
+			if (ctrl->value > 255)
+				dec->saturation = 255;
+			else if (ctrl->value < 0)
+				dec->saturation = 0;
+			else
+				dec->saturation = ctrl->value;
+			write_reg(client, 0x10, dec->saturation, dec->channel);
+			break;
+		case V4L2_CID_HUE:
+			if (ctrl->value > 255)
+				dec->hue = 255;
+			else if (ctrl->value < 0)
+				dec->hue = 0;
+			else
+				dec->hue = ctrl->value;
+			write_reg(client, 0x0f, dec->hue, dec->channel);
+			break;
+		}
+		break;
+	}
+	case VIDIOC_G_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->value = dec->brightness;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->value = dec->contrast;
+			break;
+		case V4L2_CID_SATURATION:
+			ctrl->value = dec->saturation;
+			break;
+		case V4L2_CID_HUE:
+			ctrl->value = dec->hue;
+			break;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+	return 0;
+}
+
+static struct i2c_driver wis_tw2804_driver;
+
+static struct i2c_client wis_tw2804_client_templ = {
+	.name		= "TW2804 (WIS)",
+	.driver		= &wis_tw2804_driver,
+};
+
+static int wis_tw2804_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+	struct i2c_client *client;
+	struct wis_tw2804 *dec;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+	memcpy(client, &wis_tw2804_client_templ,
+			sizeof(wis_tw2804_client_templ));
+	client->adapter = adapter;
+	client->addr = addr;
+
+	dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL);
+	if (dec == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	dec->channel = -1;
+	dec->norm = VIDEO_MODE_NTSC;
+	dec->brightness = 128;
+	dec->contrast = 128;
+	dec->saturation = 128;
+	dec->hue = 128;
+	i2c_set_clientdata(client, dec);
+
+	printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n",
+		addr, adapter->name);
+
+	i2c_attach_client(client);
+	return 0;
+}
+
+static int wis_tw2804_detach(struct i2c_client *client)
+{
+	struct wis_tw2804 *dec = i2c_get_clientdata(client);
+	int r;
+
+	r = i2c_detach_client(client);
+	if (r < 0)
+		return r;
+
+	kfree(client);
+	kfree(dec);
+	return 0;
+}
+
+static struct i2c_driver wis_tw2804_driver = {
+	.driver = {
+		.name	= "WIS TW2804 I2C driver",
+	},
+	.id		= I2C_DRIVERID_WIS_TW2804,
+	.detach_client	= wis_tw2804_detach,
+	.command	= wis_tw2804_command,
+};
+
+static int __init wis_tw2804_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wis_tw2804_driver);
+	if (r < 0)
+		return r;
+	return wis_i2c_add_driver(wis_tw2804_driver.id, wis_tw2804_detect);
+}
+
+static void __exit wis_tw2804_cleanup(void)
+{
+	wis_i2c_del_driver(wis_tw2804_detect);
+	i2c_del_driver(&wis_tw2804_driver);
+}
+
+module_init(wis_tw2804_init);
+module_exit(wis_tw2804_cleanup);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/staging/go7007/wis-tw9903.c b/drivers/staging/go7007/wis-tw9903.c
new file mode 100644
index 0000000..1cdf01a
--- /dev/null
+++ b/drivers/staging/go7007/wis-tw9903.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (C) 2005-2006 Micronas USA Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License (Version 2) as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/version.h>
+#include <linux/i2c.h>
+#include <linux/videodev.h>
+#include <linux/video_decoder.h>
+#include <linux/ioctl.h>
+
+#include "wis-i2c.h"
+
+struct wis_tw9903 {
+	int norm;
+	int brightness;
+	int contrast;
+	int hue;
+};
+
+static u8 initial_registers[] =
+{
+	0x02, 0x44, /* input 1, composite */
+	0x03, 0x92, /* correct digital format */
+	0x04, 0x00,
+	0x05, 0x80, /* or 0x00 for PAL */
+	0x06, 0x40, /* second internal current reference */
+	0x07, 0x02, /* window */
+	0x08, 0x14, /* window */
+	0x09, 0xf0, /* window */
+	0x0a, 0x81, /* window */
+	0x0b, 0xd0, /* window */
+	0x0c, 0x8c,
+	0x0d, 0x00, /* scaling */
+	0x0e, 0x11, /* scaling */
+	0x0f, 0x00, /* scaling */
+	0x10, 0x00, /* brightness */
+	0x11, 0x60, /* contrast */
+	0x12, 0x01, /* sharpness */
+	0x13, 0x7f, /* U gain */
+	0x14, 0x5a, /* V gain */
+	0x15, 0x00, /* hue */
+	0x16, 0xc3, /* sharpness */
+	0x18, 0x00,
+	0x19, 0x58, /* vbi */
+	0x1a, 0x80,
+	0x1c, 0x0f, /* video norm */
+	0x1d, 0x7f, /* video norm */
+	0x20, 0xa0, /* clamping gain (working 0x50) */
+	0x21, 0x22,
+	0x22, 0xf0,
+	0x23, 0xfe,
+	0x24, 0x3c,
+	0x25, 0x38,
+	0x26, 0x44,
+	0x27, 0x20,
+	0x28, 0x00,
+	0x29, 0x15,
+	0x2a, 0xa0,
+	0x2b, 0x44,
+	0x2c, 0x37,
+	0x2d, 0x00,
+	0x2e, 0xa5, /* burst PLL control (working: a9) */
+	0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */
+	0x31, 0x00,
+	0x33, 0x22,
+	0x34, 0x11,
+	0x35, 0x35,
+	0x3b, 0x05,
+	0x06, 0xc0, /* reset device */
+	0x00, 0x00, /* Terminator (reg 0x00 is read-only) */
+};
+
+static int write_reg(struct i2c_client *client, u8 reg, u8 value)
+{
+	return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static int write_regs(struct i2c_client *client, u8 *regs)
+{
+	int i;
+
+	for (i = 0; regs[i] != 0x00; i += 2)
+		if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0)
+			return -1;
+	return 0;
+}
+
+static int wis_tw9903_command(struct i2c_client *client,
+				unsigned int cmd, void *arg)
+{
+	struct wis_tw9903 *dec = i2c_get_clientdata(client);
+
+	switch (cmd) {
+	case DECODER_SET_INPUT:
+	{
+		int *input = arg;
+
+		i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1));
+		break;
+	}
+#if 0   /* The scaler on this thing seems to be horribly broken */
+	case DECODER_SET_RESOLUTION:
+	{
+		struct video_decoder_resolution *res = arg;
+		/*int hscale = 256 * 720 / res->width;*/
+		int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8));
+		int vscale = 256 * (dec->norm == VIDEO_MODE_NTSC ?  240 : 288)
+				/ res->height;
+		u8 regs[] = {
+			0x0d, vscale & 0xff,
+			0x0f, hscale & 0xff,
+			0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8),
+			0x06, 0xc0, /* reset device */
+			0,	0,
+		};
+		printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n",
+				vscale, hscale);
+		/*write_regs(client, regs);*/
+		break;
+	}
+#endif
+	case DECODER_SET_NORM:
+	{
+		int *input = arg;
+		u8 regs[] = {
+			0x05, *input == VIDEO_MODE_NTSC ? 0x80 : 0x00,
+			0x07, *input == VIDEO_MODE_NTSC ? 0x02 : 0x12,
+			0x08, *input == VIDEO_MODE_NTSC ? 0x14 : 0x18,
+			0x09, *input == VIDEO_MODE_NTSC ? 0xf0 : 0x20,
+			0,	0,
+		};
+		write_regs(client, regs);
+		dec->norm = *input;
+		break;
+	}
+	case VIDIOC_QUERYCTRL:
+	{
+		struct v4l2_queryctrl *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Brightness", sizeof(ctrl->name));
+			ctrl->minimum = -128;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 0x00;
+			ctrl->flags = 0;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Contrast", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 255;
+			ctrl->step = 1;
+			ctrl->default_value = 0x60;
+			ctrl->flags = 0;
+			break;
+#if 0
+		/* I don't understand how the Chroma Gain registers work... */
+		case V4L2_CID_SATURATION:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Saturation", sizeof(ctrl->name));
+			ctrl->minimum = 0;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 64;
+			ctrl->flags = 0;
+			break;
+#endif
+		case V4L2_CID_HUE:
+			ctrl->type = V4L2_CTRL_TYPE_INTEGER;
+			strncpy(ctrl->name, "Hue", sizeof(ctrl->name));
+			ctrl->minimum = -128;
+			ctrl->maximum = 127;
+			ctrl->step = 1;
+			ctrl->default_value = 0;
+			ctrl->flags = 0;
+			break;
+		}
+		break;
+	}
+	case VIDIOC_S_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			if (ctrl->value > 127)
+				dec->brightness = 127;
+			else if (ctrl->value < -128)
+				dec->brightness = -128;
+			else
+				dec->brightness = ctrl->value;
+			write_reg(client, 0x10, dec->brightness);
+			break;
+		case V4L2_CID_CONTRAST:
+			if (ctrl->value > 255)
+				dec->contrast = 255;
+			else if (ctrl->value < 0)
+				dec->contrast = 0;
+			else
+				dec->contrast = ctrl->value;
+			write_reg(client, 0x11, dec->contrast);
+			break;
+#if 0
+		case V4L2_CID_SATURATION:
+			if (ctrl->value > 127)
+				dec->saturation = 127;
+			else if (ctrl->value < 0)
+				dec->saturation = 0;
+			else
+				dec->saturation = ctrl->value;
+			/*write_reg(client, 0x0c, dec->saturation);*/
+			break;
+#endif
+		case V4L2_CID_HUE:
+			if (ctrl->value > 127)
+				dec->hue = 127;
+			else if (ctrl->value < -128)
+				dec->hue = -128;
+			else
+				dec->hue = ctrl->value;
+			write_reg(client, 0x15, dec->hue);
+			break;
+		}
+		break;
+	}
+	case VIDIOC_G_CTRL:
+	{
+		struct v4l2_control *ctrl = arg;
+
+		switch (ctrl->id) {
+		case V4L2_CID_BRIGHTNESS:
+			ctrl->value = dec->brightness;
+			break;
+		case V4L2_CID_CONTRAST:
+			ctrl->value = dec->contrast;
+			break;
+#if 0
+		case V4L2_CID_SATURATION:
+			ctrl->value = dec->saturation;
+			break;
+#endif
+		case V4L2_CID_HUE:
+			ctrl->value = dec->hue;
+			break;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+	return 0;
+}
+
+static struct i2c_driver wis_tw9903_driver;
+
+static struct i2c_client wis_tw9903_client_templ = {
+	.name		= "TW9903 (WIS)",
+	.driver		= &wis_tw9903_driver,
+};
+
+static int wis_tw9903_detect(struct i2c_adapter *adapter, int addr, int kind)
+{
+	struct i2c_client *client;
+	struct wis_tw9903 *dec;
+
+	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+		return 0;
+
+	client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
+	if (client == NULL)
+		return -ENOMEM;
+	memcpy(client, &wis_tw9903_client_templ,
+			sizeof(wis_tw9903_client_templ));
+	client->adapter = adapter;
+	client->addr = addr;
+
+	dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL);
+	if (dec == NULL) {
+		kfree(client);
+		return -ENOMEM;
+	}
+	dec->norm = VIDEO_MODE_NTSC;
+	dec->brightness = 0;
+	dec->contrast = 0x60;
+	dec->hue = 0;
+	i2c_set_clientdata(client, dec);
+
+	printk(KERN_DEBUG
+		"wis-tw9903: initializing TW9903 at address %d on %s\n",
+		addr, adapter->name);
+
+	if (write_regs(client, initial_registers) < 0) {
+		printk(KERN_ERR "wis-tw9903: error initializing TW9903\n");
+		kfree(client);
+		kfree(dec);
+		return 0;
+	}
+
+	i2c_attach_client(client);
+	return 0;
+}
+
+static int wis_tw9903_detach(struct i2c_client *client)
+{
+	struct wis_tw9903 *dec = i2c_get_clientdata(client);
+	int r;
+
+	r = i2c_detach_client(client);
+	if (r < 0)
+		return r;
+
+	kfree(client);
+	kfree(dec);
+	return 0;
+}
+
+static struct i2c_driver wis_tw9903_driver = {
+	.driver = {
+		.name	= "WIS TW9903 I2C driver",
+	},
+	.id		= I2C_DRIVERID_WIS_TW9903,
+	.detach_client	= wis_tw9903_detach,
+	.command	= wis_tw9903_command,
+};
+
+static int __init wis_tw9903_init(void)
+{
+	int r;
+
+	r = i2c_add_driver(&wis_tw9903_driver);
+	if (r < 0)
+		return r;
+	return wis_i2c_add_driver(wis_tw9903_driver.id, wis_tw9903_detect);
+}
+
+static void __exit wis_tw9903_cleanup(void)
+{
+