This commit was manufactured by cvs2svn to create branch 'ckrm'.
authorPlanet-Lab Support <support@planet-lab.org>
Fri, 21 Jan 2005 03:41:44 +0000 (03:41 +0000)
committerPlanet-Lab Support <support@planet-lab.org>
Fri, 21 Jan 2005 03:41:44 +0000 (03:41 +0000)
17 files changed:
arch/ia64/sn/include/ioerror.h [new file with mode: 0644]
arch/ia64/sn/include/tio.h [new file with mode: 0644]
arch/ia64/sn/include/xtalk/hubdev.h [new file with mode: 0644]
arch/ia64/sn/include/xtalk/xbow.h [new file with mode: 0644]
arch/ia64/sn/include/xtalk/xwidgetdev.h [new file with mode: 0644]
arch/ia64/sn/kernel/bte_error.c [new file with mode: 0644]
arch/ia64/sn/kernel/klconflib.c [new file with mode: 0644]
arch/ia64/sn/pci/Makefile [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/Makefile [new file with mode: 0644]
arch/ia64/sn/pci/pcibr/pcibr_reg.c [new file with mode: 0644]
drivers/scsi/ahci.c [new file with mode: 0644]
drivers/scsi/sata_uli.c [new file with mode: 0644]
drivers/usb/atm/Makefile [new file with mode: 0644]
drivers/usb/atm/speedtch.c [new file with mode: 0644]
drivers/usb/atm/usb_atm.c [new file with mode: 0644]
drivers/usb/atm/usb_atm.h [new file with mode: 0644]
include/asm-ia64/sn/shub_mmr.h [new file with mode: 0644]

diff --git a/arch/ia64/sn/include/ioerror.h b/arch/ia64/sn/include/ioerror.h
new file mode 100644 (file)
index 0000000..e68f2b0
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2003 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_IOERROR_H
+#define _ASM_IA64_SN_IOERROR_H
+
+/*
+ * IO error structure.
+ *
+ * This structure would expand to hold the information retrieved from
+ * all IO related error registers.
+ *
+ * This structure is defined to hold all system specific
+ * information related to a single error.
+ *
+ * This serves a couple of purpose.
+ *      - Error handling often involves translating one form of address to other
+ *        form. So, instead of having different data structures at each level,
+ *        we have a single structure, and the appropriate fields get filled in
+ *        at each layer.
+ *      - This provides a way to dump all error related information in any layer
+ *        of erorr handling (debugging aid).
+ *
+ * A second possibility is to allow each layer to define its own error
+ * data structure, and fill in the proper fields. This has the advantage
+ * of isolating the layers.
+ * A big concern is the potential stack usage (and overflow), if each layer
+ * defines these structures on stack (assuming we don't want to do kmalloc.
+ *
+ * Any layer wishing to pass extra information to a layer next to it in
+ * error handling hierarchy, can do so as a separate parameter.
+ */
+
+typedef struct io_error_s {
+    /* Bit fields indicating which structure fields are valid */
+    union {
+       struct {
+           unsigned                ievb_errortype:1;
+           unsigned                ievb_widgetnum:1;
+           unsigned                ievb_widgetdev:1;
+           unsigned                ievb_srccpu:1;
+           unsigned                ievb_srcnode:1;
+           unsigned                ievb_errnode:1;
+           unsigned                ievb_sysioaddr:1;
+           unsigned                ievb_xtalkaddr:1;
+           unsigned                ievb_busspace:1;
+           unsigned                ievb_busaddr:1;
+           unsigned                ievb_vaddr:1;
+           unsigned                ievb_memaddr:1;
+           unsigned                ievb_epc:1;
+           unsigned                ievb_ef:1;
+           unsigned                ievb_tnum:1;
+       } iev_b;
+       unsigned                iev_a;
+    } ie_v;
+
+    short                   ie_errortype;      /* error type: extra info about error */
+    short                   ie_widgetnum;      /* Widget number that's in error */
+    short                   ie_widgetdev;      /* Device within widget in error */
+    cpuid_t                 ie_srccpu; /* CPU on srcnode generating error */
+    cnodeid_t               ie_srcnode;                /* Node which caused the error   */
+    cnodeid_t               ie_errnode;                /* Node where error was noticed  */
+    iopaddr_t               ie_sysioaddr;      /* Sys specific IO address       */
+    iopaddr_t               ie_xtalkaddr;      /* Xtalk (48bit) addr of Error   */
+    iopaddr_t               ie_busspace;       /* Bus specific address space    */
+    iopaddr_t               ie_busaddr;                /* Bus specific address          */
+    caddr_t                 ie_vaddr;  /* Virtual address of error      */
+    iopaddr_t               ie_memaddr;                /* Physical memory address       */
+    caddr_t                ie_epc;             /* pc when error reported        */
+    caddr_t                ie_ef;              /* eframe when error reported    */
+    short                  ie_tnum;            /* Xtalk TNUM field */
+} ioerror_t;
+
+#define        IOERROR_INIT(e)         do { (e)->ie_v.iev_a = 0; } while (0)
+#define        IOERROR_SETVALUE(e,f,v) do { (e)->ie_ ## f = (v); (e)->ie_v.iev_b.ievb_ ## f = 1; } while (0)
+
+#endif /* _ASM_IA64_SN_IOERROR_H */
diff --git a/arch/ia64/sn/include/tio.h b/arch/ia64/sn/include/tio.h
new file mode 100644 (file)
index 0000000..0139124
--- /dev/null
@@ -0,0 +1,37 @@
+/* 
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_TIO_H
+#define _ASM_IA64_SN_TIO_H
+
+#define        TIO_MMR_ADDR_MOD
+
+#define TIO_NODE_ID     TIO_MMR_ADDR_MOD(0x0000000090060e80)
+
+#define TIO_ITTE_BASE   0xb0008800        /* base of translation table entries */
+#define TIO_ITTE(bigwin)        (TIO_ITTE_BASE + 8*(bigwin))
+
+#define TIO_ITTE_OFFSET_BITS    8       /* size of offset field */
+#define TIO_ITTE_OFFSET_MASK    ((1<<TIO_ITTE_OFFSET_BITS)-1)
+#define TIO_ITTE_OFFSET_SHIFT   0
+
+#define TIO_ITTE_WIDGET_BITS    2       /* size of widget field */
+#define TIO_ITTE_WIDGET_MASK    ((1<<TIO_ITTE_WIDGET_BITS)-1)
+#define TIO_ITTE_WIDGET_SHIFT   12
+#define TIO_ITTE_VALID_MASK    0x1
+#define TIO_ITTE_VALID_SHIFT   16
+
+
+#define TIO_ITTE_PUT(nasid, bigwin, widget, addr, valid) \
+        REMOTE_HUB_S((nasid), TIO_ITTE(bigwin), \
+                (((((addr) >> TIO_BWIN_SIZE_BITS) & \
+                   TIO_ITTE_OFFSET_MASK) << TIO_ITTE_OFFSET_SHIFT) | \
+                (((widget) & TIO_ITTE_WIDGET_MASK) << TIO_ITTE_WIDGET_SHIFT)) | \
+               (( (valid) & TIO_ITTE_VALID_MASK) << TIO_ITTE_VALID_SHIFT))
+
+#endif /*  _ASM_IA64_SN_TIO_H */
diff --git a/arch/ia64/sn/include/xtalk/hubdev.h b/arch/ia64/sn/include/xtalk/hubdev.h
new file mode 100644 (file)
index 0000000..868e7ec
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+#ifndef _ASM_IA64_SN_XTALK_HUBDEV_H
+#define _ASM_IA64_SN_XTALK_HUBDEV_H
+
+#define HUB_WIDGET_ID_MAX 0xf
+#define DEV_PER_WIDGET (2*2*8)
+#define IIO_ITTE_WIDGET_BITS    4       /* size of widget field */
+#define IIO_ITTE_WIDGET_MASK    ((1<<IIO_ITTE_WIDGET_BITS)-1)
+#define IIO_ITTE_WIDGET_SHIFT   8
+
+/*
+ * Use the top big window as a surrogate for the first small window
+ */
+#define SWIN0_BIGWIN            HUB_NUM_BIG_WINDOW
+#define IIO_NUM_ITTES   7
+#define HUB_NUM_BIG_WINDOW      (IIO_NUM_ITTES - 1)
+
+struct sn_flush_device_list {
+       int sfdl_bus;
+       int sfdl_slot;
+       int sfdl_pin;
+       struct bar_list {
+               unsigned long start;
+               unsigned long end;
+       } sfdl_bar_list[6];
+       unsigned long sfdl_force_int_addr;
+       unsigned long sfdl_flush_value;
+       volatile unsigned long *sfdl_flush_addr;
+       uint64_t sfdl_persistent_busnum;
+       struct pcibus_info *sfdl_pcibus_info;
+       spinlock_t sfdl_flush_lock;
+};
+
+/*
+ * **widget_p - Used as an array[wid_num][device] of sn_flush_device_list.
+ */
+struct sn_flush_nasid_entry  {
+       struct sn_flush_device_list **widget_p; /* Used as a array of wid_num */
+       uint64_t iio_itte[8];
+};
+
+struct hubdev_info {
+       geoid_t                         hdi_geoid;
+       short                           hdi_nasid;
+       short                           hdi_peer_nasid;   /* Dual Porting Peer */
+
+       struct sn_flush_nasid_entry     hdi_flush_nasid_list;
+       struct xwidget_info             hdi_xwidget_info[HUB_WIDGET_ID_MAX + 1];
+
+
+       void                            *hdi_nodepda;
+       void                            *hdi_node_vertex;
+       void                            *hdi_xtalk_vertex;
+};
+
+extern void hubdev_init_node(nodepda_t *, cnodeid_t);
+extern void hub_error_init(struct hubdev_info *);
+extern void ice_error_init(struct hubdev_info *);
+
+
+#endif /* _ASM_IA64_SN_XTALK_HUBDEV_H */
diff --git a/arch/ia64/sn/include/xtalk/xbow.h b/arch/ia64/sn/include/xtalk/xbow.h
new file mode 100644 (file)
index 0000000..ec56b34
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2004 Silicon Graphics, Inc. All Rights Reserved.
+ */
+#ifndef _ASM_IA64_SN_XTALK_XBOW_H
+#define _ASM_IA64_SN_XTALK_XBOW_H
+
+#define XBOW_PORT_8    0x8
+#define XBOW_PORT_C    0xc
+#define XBOW_PORT_F    0xf
+
+#define MAX_XBOW_PORTS 8       /* number of ports on xbow chip */
+#define BASE_XBOW_PORT XBOW_PORT_8     /* Lowest external port */
+
+#define        XBOW_CREDIT     4
+
+#define MAX_XBOW_NAME  16
+
+/* Register set for each xbow link */
+typedef volatile struct xb_linkregs_s {
+/* 
+ * we access these through synergy unswizzled space, so the address
+ * gets twiddled (i.e. references to 0x4 actually go to 0x0 and vv.)
+ * That's why we put the register first and filler second.
+ */
+    uint32_t               link_ibf;
+    uint32_t               filler0;    /* filler for proper alignment */
+    uint32_t               link_control;
+    uint32_t               filler1;
+    uint32_t               link_status;
+    uint32_t               filler2;
+    uint32_t               link_arb_upper;
+    uint32_t               filler3;
+    uint32_t               link_arb_lower;
+    uint32_t               filler4;
+    uint32_t               link_status_clr;
+    uint32_t               filler5;
+    uint32_t               link_reset;
+    uint32_t               filler6;
+    uint32_t               link_aux_status;
+    uint32_t               filler7;
+} xb_linkregs_t;
+
+typedef volatile struct xbow_s {
+    /* standard widget configuration                       0x000000-0x000057 */
+    struct widget_cfg            xb_widget;  /* 0x000000 */
+
+    /* helper fieldnames for accessing bridge widget */
+
+#define xb_wid_id                       xb_widget.w_id
+#define xb_wid_stat                     xb_widget.w_status
+#define xb_wid_err_upper                xb_widget.w_err_upper_addr
+#define xb_wid_err_lower                xb_widget.w_err_lower_addr
+#define xb_wid_control                  xb_widget.w_control
+#define xb_wid_req_timeout              xb_widget.w_req_timeout
+#define xb_wid_int_upper                xb_widget.w_intdest_upper_addr
+#define xb_wid_int_lower                xb_widget.w_intdest_lower_addr
+#define xb_wid_err_cmdword              xb_widget.w_err_cmd_word
+#define xb_wid_llp                      xb_widget.w_llp_cfg
+#define xb_wid_stat_clr                 xb_widget.w_tflush
+
+/* 
+ * we access these through synergy unswizzled space, so the address
+ * gets twiddled (i.e. references to 0x4 actually go to 0x0 and vv.)
+ * That's why we put the register first and filler second.
+ */
+    /* xbow-specific widget configuration                  0x000058-0x0000FF */
+    uint32_t               xb_wid_arb_reload;  /* 0x00005C */
+    uint32_t               _pad_000058;
+    uint32_t               xb_perf_ctr_a;      /* 0x000064 */
+    uint32_t               _pad_000060;
+    uint32_t               xb_perf_ctr_b;      /* 0x00006c */
+    uint32_t               _pad_000068;
+    uint32_t               xb_nic;     /* 0x000074 */
+    uint32_t               _pad_000070;
+
+    /* Xbridge only */
+    uint32_t               xb_w0_rst_fnc;      /* 0x00007C */
+    uint32_t               _pad_000078;
+    uint32_t               xb_l8_rst_fnc;      /* 0x000084 */
+    uint32_t               _pad_000080;
+    uint32_t               xb_l9_rst_fnc;      /* 0x00008c */
+    uint32_t               _pad_000088;
+    uint32_t               xb_la_rst_fnc;      /* 0x000094 */
+    uint32_t               _pad_000090;
+    uint32_t               xb_lb_rst_fnc;      /* 0x00009c */
+    uint32_t               _pad_000098;
+    uint32_t               xb_lc_rst_fnc;      /* 0x0000a4 */
+    uint32_t               _pad_0000a0;
+    uint32_t               xb_ld_rst_fnc;      /* 0x0000ac */
+    uint32_t               _pad_0000a8;
+    uint32_t               xb_le_rst_fnc;      /* 0x0000b4 */
+    uint32_t               _pad_0000b0;
+    uint32_t               xb_lf_rst_fnc;      /* 0x0000bc */
+    uint32_t               _pad_0000b8;
+    uint32_t               xb_lock;            /* 0x0000c4 */
+    uint32_t               _pad_0000c0;
+    uint32_t               xb_lock_clr;        /* 0x0000cc */
+    uint32_t               _pad_0000c8;
+    /* end of Xbridge only */
+    uint32_t               _pad_0000d0[12];
+
+    /* Link Specific Registers, port 8..15                 0x000100-0x000300 */
+    xb_linkregs_t           xb_link_raw[MAX_XBOW_PORTS];
+#define xb_link(p)      xb_link_raw[(p) & (MAX_XBOW_PORTS - 1)]
+
+} xbow_t;
+
+#define XB_FLAGS_EXISTS                0x1     /* device exists */
+#define XB_FLAGS_MASTER                0x2
+#define XB_FLAGS_SLAVE         0x0
+#define XB_FLAGS_GBR           0x4
+#define XB_FLAGS_16BIT         0x8
+#define XB_FLAGS_8BIT          0x0
+
+/* is widget port number valid?  (based on version 7.0 of xbow spec) */
+#define XBOW_WIDGET_IS_VALID(wid) ((wid) >= XBOW_PORT_8 && (wid) <= XBOW_PORT_F)
+
+/* whether to use upper or lower arbitration register, given source widget id */
+#define XBOW_ARB_IS_UPPER(wid)         ((wid) >= XBOW_PORT_8 && (wid) <= XBOW_PORT_B)
+#define XBOW_ARB_IS_LOWER(wid)         ((wid) >= XBOW_PORT_C && (wid) <= XBOW_PORT_F)
+
+/* offset of arbitration register, given source widget id */
+#define XBOW_ARB_OFF(wid)      (XBOW_ARB_IS_UPPER(wid) ? 0x1c : 0x24)
+
+#define        XBOW_WID_ID             WIDGET_ID
+#define        XBOW_WID_STAT           WIDGET_STATUS
+#define        XBOW_WID_ERR_UPPER      WIDGET_ERR_UPPER_ADDR
+#define        XBOW_WID_ERR_LOWER      WIDGET_ERR_LOWER_ADDR
+#define        XBOW_WID_CONTROL        WIDGET_CONTROL
+#define        XBOW_WID_REQ_TO         WIDGET_REQ_TIMEOUT
+#define        XBOW_WID_INT_UPPER      WIDGET_INTDEST_UPPER_ADDR
+#define        XBOW_WID_INT_LOWER      WIDGET_INTDEST_LOWER_ADDR
+#define        XBOW_WID_ERR_CMDWORD    WIDGET_ERR_CMD_WORD
+#define        XBOW_WID_LLP            WIDGET_LLP_CFG
+#define        XBOW_WID_STAT_CLR       WIDGET_TFLUSH
+#define XBOW_WID_ARB_RELOAD    0x5c
+#define XBOW_WID_PERF_CTR_A    0x64
+#define XBOW_WID_PERF_CTR_B    0x6c
+#define XBOW_WID_NIC           0x74
+
+/* Xbridge only */
+#define XBOW_W0_RST_FNC                0x00007C
+#define        XBOW_L8_RST_FNC         0x000084
+#define        XBOW_L9_RST_FNC         0x00008c
+#define        XBOW_LA_RST_FNC         0x000094
+#define        XBOW_LB_RST_FNC         0x00009c
+#define        XBOW_LC_RST_FNC         0x0000a4
+#define        XBOW_LD_RST_FNC         0x0000ac
+#define        XBOW_LE_RST_FNC         0x0000b4
+#define        XBOW_LF_RST_FNC         0x0000bc
+#define XBOW_RESET_FENCE(x) ((x) > 7 && (x) < 16) ? \
+                               (XBOW_W0_RST_FNC + ((x) - 7) * 8) : \
+                               ((x) == 0) ? XBOW_W0_RST_FNC : 0
+#define XBOW_LOCK              0x0000c4
+#define XBOW_LOCK_CLR          0x0000cc
+/* End of Xbridge only */
+
+/* used only in ide, but defined here within the reserved portion */
+/*              of the widget0 address space (before 0xf4) */
+#define        XBOW_WID_UNDEF          0xe4
+
+/* xbow link register set base, legal value for x is 0x8..0xf */
+#define        XB_LINK_BASE            0x100
+#define        XB_LINK_OFFSET          0x40
+#define        XB_LINK_REG_BASE(x)     (XB_LINK_BASE + ((x) & (MAX_XBOW_PORTS - 1)) * XB_LINK_OFFSET)
+
+#define        XB_LINK_IBUF_FLUSH(x)   (XB_LINK_REG_BASE(x) + 0x4)
+#define        XB_LINK_CTRL(x)         (XB_LINK_REG_BASE(x) + 0xc)
+#define        XB_LINK_STATUS(x)       (XB_LINK_REG_BASE(x) + 0x14)
+#define        XB_LINK_ARB_UPPER(x)    (XB_LINK_REG_BASE(x) + 0x1c)
+#define        XB_LINK_ARB_LOWER(x)    (XB_LINK_REG_BASE(x) + 0x24)
+#define        XB_LINK_STATUS_CLR(x)   (XB_LINK_REG_BASE(x) + 0x2c)
+#define        XB_LINK_RESET(x)        (XB_LINK_REG_BASE(x) + 0x34)
+#define        XB_LINK_AUX_STATUS(x)   (XB_LINK_REG_BASE(x) + 0x3c)
+
+/* link_control(x) */
+#define        XB_CTRL_LINKALIVE_IE            0x80000000      /* link comes alive */
+     /* reserved:                      0x40000000 */
+#define        XB_CTRL_PERF_CTR_MODE_MSK       0x30000000      /* perf counter mode */
+#define        XB_CTRL_IBUF_LEVEL_MSK          0x0e000000      /* input packet buffer level */
+#define        XB_CTRL_8BIT_MODE               0x01000000      /* force link into 8 bit mode */
+#define XB_CTRL_BAD_LLP_PKT            0x00800000      /* force bad LLP packet */
+#define XB_CTRL_WIDGET_CR_MSK          0x007c0000      /* LLP widget credit mask */
+#define XB_CTRL_WIDGET_CR_SHFT 18                      /* LLP widget credit shift */
+#define XB_CTRL_ILLEGAL_DST_IE         0x00020000      /* illegal destination */
+#define XB_CTRL_OALLOC_IBUF_IE         0x00010000      /* overallocated input buffer */
+     /* reserved:                      0x0000fe00 */
+#define XB_CTRL_BNDWDTH_ALLOC_IE       0x00000100      /* bandwidth alloc */
+#define XB_CTRL_RCV_CNT_OFLOW_IE       0x00000080      /* rcv retry overflow */
+#define XB_CTRL_XMT_CNT_OFLOW_IE       0x00000040      /* xmt retry overflow */
+#define XB_CTRL_XMT_MAX_RTRY_IE                0x00000020      /* max transmit retry */
+#define XB_CTRL_RCV_IE                 0x00000010      /* receive */
+#define XB_CTRL_XMT_RTRY_IE            0x00000008      /* transmit retry */
+     /* reserved:                      0x00000004 */
+#define        XB_CTRL_MAXREQ_TOUT_IE          0x00000002      /* maximum request timeout */
+#define        XB_CTRL_SRC_TOUT_IE             0x00000001      /* source timeout */
+
+/* link_status(x) */
+#define        XB_STAT_LINKALIVE               XB_CTRL_LINKALIVE_IE
+     /* reserved:                      0x7ff80000 */
+#define        XB_STAT_MULTI_ERR               0x00040000      /* multi error */
+#define        XB_STAT_ILLEGAL_DST_ERR         XB_CTRL_ILLEGAL_DST_IE
+#define        XB_STAT_OALLOC_IBUF_ERR         XB_CTRL_OALLOC_IBUF_IE
+#define        XB_STAT_BNDWDTH_ALLOC_ID_MSK    0x0000ff00      /* port bitmask */
+#define        XB_STAT_RCV_CNT_OFLOW_ERR       XB_CTRL_RCV_CNT_OFLOW_IE
+#define        XB_STAT_XMT_CNT_OFLOW_ERR       XB_CTRL_XMT_CNT_OFLOW_IE
+#define        XB_STAT_XMT_MAX_RTRY_ERR        XB_CTRL_XMT_MAX_RTRY_IE
+#define        XB_STAT_RCV_ERR                 XB_CTRL_RCV_IE
+#define        XB_STAT_XMT_RTRY_ERR            XB_CTRL_XMT_RTRY_IE
+     /* reserved:                      0x00000004 */
+#define        XB_STAT_MAXREQ_TOUT_ERR         XB_CTRL_MAXREQ_TOUT_IE
+#define        XB_STAT_SRC_TOUT_ERR            XB_CTRL_SRC_TOUT_IE
+
+/* link_aux_status(x) */
+#define        XB_AUX_STAT_RCV_CNT     0xff000000
+#define        XB_AUX_STAT_XMT_CNT     0x00ff0000
+#define        XB_AUX_STAT_TOUT_DST    0x0000ff00
+#define        XB_AUX_LINKFAIL_RST_BAD 0x00000040
+#define        XB_AUX_STAT_PRESENT     0x00000020
+#define        XB_AUX_STAT_PORT_WIDTH  0x00000010
+     /*        reserved:               0x0000000f */
+
+/*
+ * link_arb_upper/link_arb_lower(x), (reg) should be the link_arb_upper
+ * register if (x) is 0x8..0xb, link_arb_lower if (x) is 0xc..0xf
+ */
+#define        XB_ARB_GBR_MSK          0x1f
+#define        XB_ARB_RR_MSK           0x7
+#define        XB_ARB_GBR_SHFT(x)      (((x) & 0x3) * 8)
+#define        XB_ARB_RR_SHFT(x)       (((x) & 0x3) * 8 + 5)
+#define        XB_ARB_GBR_CNT(reg,x)   ((reg) >> XB_ARB_GBR_SHFT(x) & XB_ARB_GBR_MSK)
+#define        XB_ARB_RR_CNT(reg,x)    ((reg) >> XB_ARB_RR_SHFT(x) & XB_ARB_RR_MSK)
+
+/* XBOW_WID_STAT */
+#define        XB_WID_STAT_LINK_INTR_SHFT      (24)
+#define        XB_WID_STAT_LINK_INTR_MASK      (0xFF << XB_WID_STAT_LINK_INTR_SHFT)
+#define        XB_WID_STAT_LINK_INTR(x)        (0x1 << (((x)&7) + XB_WID_STAT_LINK_INTR_SHFT))
+#define        XB_WID_STAT_WIDGET0_INTR        0x00800000
+#define XB_WID_STAT_SRCID_MASK         0x000003c0      /* Xbridge only */
+#define        XB_WID_STAT_REG_ACC_ERR         0x00000020
+#define XB_WID_STAT_RECV_TOUT          0x00000010      /* Xbridge only */
+#define XB_WID_STAT_ARB_TOUT           0x00000008      /* Xbridge only */
+#define        XB_WID_STAT_XTALK_ERR           0x00000004
+#define XB_WID_STAT_DST_TOUT           0x00000002      /* Xbridge only */
+#define        XB_WID_STAT_MULTI_ERR           0x00000001
+
+#define XB_WID_STAT_SRCID_SHFT         6
+
+/* XBOW_WID_CONTROL */
+#define XB_WID_CTRL_REG_ACC_IE         XB_WID_STAT_REG_ACC_ERR
+#define XB_WID_CTRL_RECV_TOUT          XB_WID_STAT_RECV_TOUT
+#define XB_WID_CTRL_ARB_TOUT           XB_WID_STAT_ARB_TOUT
+#define XB_WID_CTRL_XTALK_IE           XB_WID_STAT_XTALK_ERR
+
+/* XBOW_WID_INT_UPPER */
+/* defined in xwidget.h for WIDGET_INTDEST_UPPER_ADDR */
+
+/* XBOW WIDGET part number, in the ID register */
+#define XBOW_WIDGET_PART_NUM   0x0             /* crossbow */
+#define XXBOW_WIDGET_PART_NUM  0xd000          /* Xbridge */
+#define        XBOW_WIDGET_MFGR_NUM    0x0
+#define        XXBOW_WIDGET_MFGR_NUM   0x0
+#define PXBOW_WIDGET_PART_NUM   0xd100          /* PIC */
+
+#define        XBOW_REV_1_0            0x1     /* xbow rev 1.0 is "1" */
+#define        XBOW_REV_1_1            0x2     /* xbow rev 1.1 is "2" */
+#define XBOW_REV_1_2           0x3     /* xbow rev 1.2 is "3" */
+#define XBOW_REV_1_3           0x4     /* xbow rev 1.3 is "4" */
+#define XBOW_REV_2_0           0x5     /* xbow rev 2.0 is "5" */
+
+#define XXBOW_PART_REV_1_0             (XXBOW_WIDGET_PART_NUM << 4 | 0x1 )
+#define XXBOW_PART_REV_2_0             (XXBOW_WIDGET_PART_NUM << 4 | 0x2 )
+
+/* XBOW_WID_ARB_RELOAD */
+#define        XBOW_WID_ARB_RELOAD_INT 0x3f    /* GBR reload interval */
+
+#define IS_XBRIDGE_XBOW(wid) \
+        (XWIDGET_PART_NUM(wid) == XXBOW_WIDGET_PART_NUM && \
+                        XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM)
+
+#define IS_PIC_XBOW(wid) \
+        (XWIDGET_PART_NUM(wid) == PXBOW_WIDGET_PART_NUM && \
+                        XWIDGET_MFG_NUM(wid) == XXBOW_WIDGET_MFGR_NUM)
+
+#define XBOW_WAR_ENABLED(pv, widid) ((1 << XWIDGET_REV_NUM(widid)) & pv)
+
+#endif                          /* _ASM_IA64_SN_XTALK_XBOW_H */
diff --git a/arch/ia64/sn/include/xtalk/xwidgetdev.h b/arch/ia64/sn/include/xtalk/xwidgetdev.h
new file mode 100644 (file)
index 0000000..c5f4bc5
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992-1997,2000-2003 Silicon Graphics, Inc. All Rights Reserved.
+ */
+#ifndef _ASM_IA64_SN_XTALK_XWIDGET_H
+#define _ASM_IA64_SN_XTALK_XWIDGET_H
+
+/* WIDGET_ID */
+#define WIDGET_REV_NUM                  0xf0000000
+#define WIDGET_PART_NUM                 0x0ffff000
+#define WIDGET_MFG_NUM                  0x00000ffe
+#define WIDGET_REV_NUM_SHFT             28
+#define WIDGET_PART_NUM_SHFT            12
+#define WIDGET_MFG_NUM_SHFT             1
+
+#define XWIDGET_PART_NUM(widgetid) (((widgetid) & WIDGET_PART_NUM) >> WIDGET_PART_NUM_SHFT)
+#define XWIDGET_REV_NUM(widgetid) (((widgetid) & WIDGET_REV_NUM) >> WIDGET_REV_NUM_SHFT)
+#define XWIDGET_MFG_NUM(widgetid) (((widgetid) & WIDGET_MFG_NUM) >> WIDGET_MFG_NUM_SHFT)
+#define XWIDGET_PART_REV_NUM(widgetid) ((XWIDGET_PART_NUM(widgetid) << 4) | \
+                                        XWIDGET_REV_NUM(widgetid))
+#define XWIDGET_PART_REV_NUM_REV(partrev) (partrev & 0xf)
+
+/* widget configuration registers */
+struct widget_cfg{
+       uint32_t        w_id;   /* 0x04 */
+       uint32_t        w_pad_0;        /* 0x00 */
+       uint32_t        w_status;       /* 0x0c */
+       uint32_t        w_pad_1;        /* 0x08 */
+       uint32_t        w_err_upper_addr;       /* 0x14 */
+       uint32_t        w_pad_2;        /* 0x10 */
+       uint32_t        w_err_lower_addr;       /* 0x1c */
+       uint32_t        w_pad_3;        /* 0x18 */
+       uint32_t        w_control;      /* 0x24 */
+       uint32_t        w_pad_4;        /* 0x20 */
+       uint32_t        w_req_timeout;  /* 0x2c */
+       uint32_t        w_pad_5;        /* 0x28 */
+       uint32_t        w_intdest_upper_addr;   /* 0x34 */
+       uint32_t        w_pad_6;        /* 0x30 */
+       uint32_t        w_intdest_lower_addr;   /* 0x3c */
+       uint32_t        w_pad_7;        /* 0x38 */
+       uint32_t        w_err_cmd_word; /* 0x44 */
+       uint32_t        w_pad_8;        /* 0x40 */
+       uint32_t        w_llp_cfg;      /* 0x4c */
+       uint32_t        w_pad_9;        /* 0x48 */
+       uint32_t        w_tflush;       /* 0x54 */
+       uint32_t        w_pad_10;       /* 0x50 */
+};
+
+/*
+ * Crosstalk Widget Hardware Identification, as defined in the Crosstalk spec.
+ */
+struct xwidget_hwid{
+       int             mfg_num;
+       int             rev_num;
+       int             part_num;
+};
+
+struct xwidget_info{
+
+       struct xwidget_hwid     xwi_hwid;       /* Widget Identification */
+       char                    xwi_masterxid;  /* Hub's Widget Port Number */
+       void                    *xwi_hubinfo;     /* Hub's provider private info */
+       uint64_t                *xwi_hub_provider; /* prom provider functions */
+       void                    *xwi_vertex;
+};
+
+#endif                          /* _ASM_IA64_SN_XTALK_XWIDGET_H */
diff --git a/arch/ia64/sn/kernel/bte_error.c b/arch/ia64/sn/kernel/bte_error.c
new file mode 100644 (file)
index 0000000..3591c2c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+ */
+
+#include <linux/types.h>
+#include <asm/sn/sn_sal.h>
+#include "ioerror.h"
+#include <asm/sn/addrs.h>
+#include "shubio.h"
+#include <asm/sn/geo.h>
+#include "xtalk/xwidgetdev.h"
+#include "xtalk/hubdev.h"
+#include <asm/sn/bte.h>
+
+/*
+ * Bte error handling is done in two parts.  The first captures
+ * any crb related errors.  Since there can be multiple crbs per
+ * interface and multiple interfaces active, we need to wait until
+ * all active crbs are completed.  This is the first job of the
+ * second part error handler.  When all bte related CRBs are cleanly
+ * completed, it resets the interfaces and gets them ready for new
+ * transfers to be queued.
+ */
+
+void bte_error_handler(unsigned long);
+
+/*
+ * Wait until all BTE related CRBs are completed
+ * and then reset the interfaces.
+ */
+void bte_error_handler(unsigned long _nodepda)
+{
+       struct nodepda_s *err_nodepda = (struct nodepda_s *)_nodepda;
+       spinlock_t *recovery_lock = &err_nodepda->bte_recovery_lock;
+       struct timer_list *recovery_timer = &err_nodepda->bte_recovery_timer;
+       nasid_t nasid;
+       int i;
+       int valid_crbs;
+       unsigned long irq_flags;
+       volatile u64 *notify;
+       bte_result_t bh_error;
+       ii_imem_u_t imem;       /* II IMEM Register */
+       ii_icrb0_d_u_t icrbd;   /* II CRB Register D */
+       ii_ibcr_u_t ibcr;
+       ii_icmr_u_t icmr;
+
+       BTE_PRINTK(("bte_error_handler(%p) - %d\n", err_nodepda,
+                   smp_processor_id()));
+
+       spin_lock_irqsave(recovery_lock, irq_flags);
+
+       if ((err_nodepda->bte_if[0].bh_error == BTE_SUCCESS) &&
+           (err_nodepda->bte_if[1].bh_error == BTE_SUCCESS)) {
+               BTE_PRINTK(("eh:%p:%d Nothing to do.\n", err_nodepda,
+                           smp_processor_id()));
+               spin_unlock_irqrestore(recovery_lock, irq_flags);
+               return;
+       }
+       /*
+        * Lock all interfaces on this node to prevent new transfers
+        * from being queued.
+        */
+       for (i = 0; i < BTES_PER_NODE; i++) {
+               if (err_nodepda->bte_if[i].cleanup_active) {
+                       continue;
+               }
+               spin_lock(&err_nodepda->bte_if[i].spinlock);
+               BTE_PRINTK(("eh:%p:%d locked %d\n", err_nodepda,
+                           smp_processor_id(), i));
+               err_nodepda->bte_if[i].cleanup_active = 1;
+       }
+
+       /* Determine information about our hub */
+       nasid = cnodeid_to_nasid(err_nodepda->bte_if[0].bte_cnode);
+
+       /*
+        * A BTE transfer can use multiple CRBs.  We need to make sure
+        * that all the BTE CRBs are complete (or timed out) before
+        * attempting to clean up the error.  Resetting the BTE while
+        * there are still BTE CRBs active will hang the BTE.
+        * We should look at all the CRBs to see if they are allocated
+        * to the BTE and see if they are still active.  When none
+        * are active, we can continue with the cleanup.
+        *
+        * We also want to make sure that the local NI port is up.
+        * When a router resets the NI port can go down, while it
+        * goes through the LLP handshake, but then comes back up.
+        */
+       icmr.ii_icmr_regval = REMOTE_HUB_L(nasid, IIO_ICMR);
+       if (icmr.ii_icmr_fld_s.i_crb_mark != 0) {
+               /*
+                * There are errors which still need to be cleaned up by
+                * hubiio_crb_error_handler
+                */
+               mod_timer(recovery_timer, HZ * 5);
+               BTE_PRINTK(("eh:%p:%d Marked Giving up\n", err_nodepda,
+                           smp_processor_id()));
+               spin_unlock_irqrestore(recovery_lock, irq_flags);
+               return;
+       }
+       if (icmr.ii_icmr_fld_s.i_crb_vld != 0) {
+
+               valid_crbs = icmr.ii_icmr_fld_s.i_crb_vld;
+
+               for (i = 0; i < IIO_NUM_CRBS; i++) {
+                       if (!((1 << i) & valid_crbs)) {
+                               /* This crb was not marked as valid, ignore */
+                               continue;
+                       }
+                       icrbd.ii_icrb0_d_regval =
+                           REMOTE_HUB_L(nasid, IIO_ICRB_D(i));
+                       if (icrbd.d_bteop) {
+                               mod_timer(recovery_timer, HZ * 5);
+                               BTE_PRINTK(("eh:%p:%d Valid %d, Giving up\n",
+                                           err_nodepda, smp_processor_id(),
+                                           i));
+                               spin_unlock_irqrestore(recovery_lock,
+                                                      irq_flags);
+                               return;
+                       }
+               }
+       }
+
+       BTE_PRINTK(("eh:%p:%d Cleaning up\n", err_nodepda, smp_processor_id()));
+       /* Reenable both bte interfaces */
+       imem.ii_imem_regval = REMOTE_HUB_L(nasid, IIO_IMEM);
+       imem.ii_imem_fld_s.i_b0_esd = imem.ii_imem_fld_s.i_b1_esd = 1;
+       REMOTE_HUB_S(nasid, IIO_IMEM, imem.ii_imem_regval);
+
+       /* Reinitialize both BTE state machines. */
+       ibcr.ii_ibcr_regval = REMOTE_HUB_L(nasid, IIO_IBCR);
+       ibcr.ii_ibcr_fld_s.i_soft_reset = 1;
+       REMOTE_HUB_S(nasid, IIO_IBCR, ibcr.ii_ibcr_regval);
+
+       for (i = 0; i < BTES_PER_NODE; i++) {
+               bh_error = err_nodepda->bte_if[i].bh_error;
+               if (bh_error != BTE_SUCCESS) {
+                       /* There is an error which needs to be notified */
+                       notify = err_nodepda->bte_if[i].most_rcnt_na;
+                       BTE_PRINTK(("cnode %d bte %d error=0x%lx\n",
+                                   err_nodepda->bte_if[i].bte_cnode,
+                                   err_nodepda->bte_if[i].bte_num,
+                                   IBLS_ERROR | (u64) bh_error));
+                       *notify = IBLS_ERROR | bh_error;
+                       err_nodepda->bte_if[i].bh_error = BTE_SUCCESS;
+               }
+
+               err_nodepda->bte_if[i].cleanup_active = 0;
+               BTE_PRINTK(("eh:%p:%d Unlocked %d\n", err_nodepda,
+                           smp_processor_id(), i));
+               spin_unlock(&pda->cpu_bte_if[i]->spinlock);
+       }
+
+       del_timer(recovery_timer);
+
+       spin_unlock_irqrestore(recovery_lock, irq_flags);
+}
+
+/*
+ * First part error handler.  This is called whenever any error CRB interrupt
+ * is generated by the II.
+ */
+void
+bte_crb_error_handler(cnodeid_t cnode, int btenum,
+                      int crbnum, ioerror_t * ioe, int bteop)
+{
+       struct bteinfo_s *bte;
+
+
+       bte = &(NODEPDA(cnode)->bte_if[btenum]);
+
+       /*
+        * The caller has already figured out the error type, we save that
+        * in the bte handle structure for the thread excercising the
+        * interface to consume.
+        */
+       bte->bh_error = ioe->ie_errortype + BTEFAIL_OFFSET;
+       bte->bte_error_count++;
+
+       BTE_PRINTK(("Got an error on cnode %d bte %d: HW error type 0x%x\n",
+               bte->bte_cnode, bte->bte_num, ioe->ie_errortype));
+       bte_error_handler((unsigned long) NODEPDA(cnode));
+}
+
diff --git a/arch/ia64/sn/kernel/klconflib.c b/arch/ia64/sn/kernel/klconflib.c
new file mode 100644 (file)
index 0000000..0f11a32
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1992 - 1997, 2000-2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <asm/sn/types.h>
+#include <asm/sn/module.h>
+#include <asm/sn/l1.h>
+
+char brick_types[MAX_BRICK_TYPES + 1] = "cri.xdpn%#=vo^kjbf890123456789...";
+/*
+ * Format a module id for printing.
+ *
+ * There are three possible formats:
+ *
+ *   MODULE_FORMAT_BRIEF       is the brief 6-character format, including
+ *                             the actual brick-type as recorded in the 
+ *                             moduleid_t, eg. 002c15 for a C-brick, or
+ *                             101#17 for a PX-brick.
+ *
+ *   MODULE_FORMAT_LONG                is the hwgraph format, eg. rack/002/bay/15
+ *                             of rack/101/bay/17 (note that the brick
+ *                             type does not appear in this format).
+ *
+ *   MODULE_FORMAT_LCD         is like MODULE_FORMAT_BRIEF, except that it
+ *                             ensures that the module id provided appears
+ *                             exactly as it would on the LCD display of
+ *                             the corresponding brick, eg. still 002c15
+ *                             for a C-brick, but 101p17 for a PX-brick.
+ *
+ * maule (9/13/04):  Removed top-level check for (fmt == MODULE_FORMAT_LCD)
+ * making MODULE_FORMAT_LCD equivalent to MODULE_FORMAT_BRIEF.  It was
+ * decided that all callers should assume the returned string should be what
+ * is displayed on the brick L1 LCD.
+ */
+void
+format_module_id(char *buffer, moduleid_t m, int fmt)
+{
+       int rack, position;
+       unsigned char brickchar;
+
+       rack = MODULE_GET_RACK(m);
+       brickchar = MODULE_GET_BTCHAR(m);
+
+       /* Be sure we use the same brick type character as displayed
+        * on the brick's LCD
+        */
+       switch (brickchar) 
+       {
+       case L1_BRICKTYPE_GA:
+       case L1_BRICKTYPE_OPUS_TIO:
+               brickchar = L1_BRICKTYPE_C;
+               break;
+
+       case L1_BRICKTYPE_PX:
+       case L1_BRICKTYPE_PE:
+       case L1_BRICKTYPE_PA:
+       case L1_BRICKTYPE_SA: /* we can move this to the "I's" later
+                              * if that makes more sense
+                              */
+               brickchar = L1_BRICKTYPE_P;
+               break;
+
+       case L1_BRICKTYPE_IX:
+       case L1_BRICKTYPE_IA:
+
+               brickchar = L1_BRICKTYPE_I;
+               break;
+       }
+
+       position = MODULE_GET_BPOS(m);
+
+       if ((fmt == MODULE_FORMAT_BRIEF) || (fmt == MODULE_FORMAT_LCD)) {
+           /* Brief module number format, eg. 002c15 */
+
+           /* Decompress the rack number */
+           *buffer++ = '0' + RACK_GET_CLASS(rack);
+           *buffer++ = '0' + RACK_GET_GROUP(rack);
+           *buffer++ = '0' + RACK_GET_NUM(rack);
+
+           /* Add the brick type */
+           *buffer++ = brickchar;
+       }
+       else if (fmt == MODULE_FORMAT_LONG) {
+           /* Fuller hwgraph format, eg. rack/002/bay/15 */
+
+           strcpy(buffer, "rack" "/");  buffer += strlen(buffer);
+
+           *buffer++ = '0' + RACK_GET_CLASS(rack);
+           *buffer++ = '0' + RACK_GET_GROUP(rack);
+           *buffer++ = '0' + RACK_GET_NUM(rack);
+
+           strcpy(buffer, "/" "bay" "/");  buffer += strlen(buffer);
+       }
+
+       /* Add the bay position, using at least two digits */
+       if (position < 10)
+           *buffer++ = '0';
+       sprintf(buffer, "%d", position);
+
+}
diff --git a/arch/ia64/sn/pci/Makefile b/arch/ia64/sn/pci/Makefile
new file mode 100644 (file)
index 0000000..b5dca00
--- /dev/null
@@ -0,0 +1,10 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2000-2004 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# Makefile for the sn pci general routines.
+
+obj-y := pci_dma.o pcibr/ 
diff --git a/arch/ia64/sn/pci/pcibr/Makefile b/arch/ia64/sn/pci/pcibr/Makefile
new file mode 100644 (file)
index 0000000..1850c4a
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Copyright (C) 2002-2004 Silicon Graphics, Inc.  All Rights Reserved.
+#
+# Makefile for the sn2 io routines.
+
+obj-y                          +=  pcibr_dma.o pcibr_reg.o \
+                                   pcibr_ate.o pcibr_provider.o
diff --git a/arch/ia64/sn/pci/pcibr/pcibr_reg.c b/arch/ia64/sn/pci/pcibr/pcibr_reg.c
new file mode 100644 (file)
index 0000000..74a74a7
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 2004 Silicon Graphics, Inc. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include "pci/pcibus_provider_defs.h"
+#include "pci/pcidev.h"
+#include "pci/tiocp.h"
+#include "pci/pic.h"
+#include "pci/pcibr_provider.h"
+
+union br_ptr {
+       struct tiocp tio;
+       struct pic pic;
+};
+
+/*
+ * Control Register Access -- Read/Write                            0000_0020
+ */
+void pcireg_control_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_control &= ~bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_wid_control &= ~bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_control_bit_clr: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+void pcireg_control_bit_set(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_control |= bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_wid_control |= bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_control_bit_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * PCI/PCIX Target Flush Register Access -- Read Only              0000_0050
+ */
+uint64_t pcireg_tflush_get(struct pcibus_info *pcibus_info)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t ret = 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret = ptr->tio.cp_tflush;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret = ptr->pic.p_wid_tflush;
+                       break;
+               default:
+                       panic
+                           ("pcireg_tflush_get: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+
+       /* Read of the Target Flush should always return zero */
+       if (ret != 0)
+               panic("pcireg_tflush_get:Target Flush failed\n");
+
+       return ret;
+}
+
+/*
+ * Interrupt Status Register Access -- Read Only                   0000_0100
+ */
+uint64_t pcireg_intr_status_get(struct pcibus_info * pcibus_info)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t ret = 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret = ptr->tio.cp_int_status;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret = ptr->pic.p_int_status;
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_status_get: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+       return ret;
+}
+
+/*
+ * Interrupt Enable Register Access -- Read/Write                   0000_0108
+ */
+void pcireg_intr_enable_bit_clr(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_enable &= ~bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_enable &= ~bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_enable_bit_clr: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+void pcireg_intr_enable_bit_set(struct pcibus_info *pcibus_info, uint64_t bits)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_enable |= bits;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_enable |= bits;
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_enable_bit_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * Intr Host Address Register (int_addr) -- Read/Write  0000_0130 - 0000_0168
+ */
+void pcireg_intr_addr_addr_set(struct pcibus_info *pcibus_info, int int_n,
+                              uint64_t addr)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_addr[int_n] &= ~TIOCP_HOST_INTR_ADDR;
+                       ptr->tio.cp_int_addr[int_n] |=
+                           (addr & TIOCP_HOST_INTR_ADDR);
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_addr[int_n] &= ~PIC_HOST_INTR_ADDR;
+                       ptr->pic.p_int_addr[int_n] |=
+                           (addr & PIC_HOST_INTR_ADDR);
+                       break;
+               default:
+                       panic
+                           ("pcireg_intr_addr_addr_get: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * Force Interrupt Register Access -- Write Only       0000_01C0 - 0000_01F8
+ */
+void pcireg_force_intr_set(struct pcibus_info *pcibus_info, int int_n)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_force_pin[int_n] = 1;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_force_pin[int_n] = 1;
+                       break;
+               default:
+                       panic
+                           ("pcireg_force_intr_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+/*
+ * Device(x) Write Buffer Flush Reg Access -- Read Only 0000_0240 - 0000_0258
+ */
+uint64_t pcireg_wrb_flush_get(struct pcibus_info *pcibus_info, int device)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t ret = 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret = ptr->tio.cp_wr_req_buf[device];
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret = ptr->pic.p_wr_req_buf[device];
+                       break;
+               default:
+                     panic("pcireg_wrb_flush_get: unknown bridgetype bridge 0x%p", (void *)ptr);
+               }
+
+       }
+       /* Read of the Write Buffer Flush should always return zero */
+       return ret;
+}
+
+void pcireg_int_ate_set(struct pcibus_info *pcibus_info, int ate_index,
+                       uint64_t val)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ptr->tio.cp_int_ate_ram[ate_index] = (uint64_t) val;
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ptr->pic.p_int_ate_ram[ate_index] = (uint64_t) val;
+                       break;
+               default:
+                       panic
+                           ("pcireg_int_ate_set: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+}
+
+uint64_t *pcireg_int_ate_addr(struct pcibus_info *pcibus_info, int ate_index)
+{
+       union br_ptr *ptr = (union br_ptr *)pcibus_info->pbi_buscommon.bs_base;
+       uint64_t *ret = (uint64_t *) 0;
+
+       if (pcibus_info) {
+               switch (pcibus_info->pbi_bridge_type) {
+               case PCIBR_BRIDGETYPE_TIOCP:
+                       ret =
+                           (uint64_t *) & (ptr->tio.cp_int_ate_ram[ate_index]);
+                       break;
+               case PCIBR_BRIDGETYPE_PIC:
+                       ret =
+                           (uint64_t *) & (ptr->pic.p_int_ate_ram[ate_index]);
+                       break;
+               default:
+                       panic
+                           ("pcireg_int_ate_addr: unknown bridgetype bridge 0x%p",
+                            (void *)ptr);
+               }
+       }
+       return ret;
+}
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
new file mode 100644 (file)
index 0000000..8dd5d1f
--- /dev/null
@@ -0,0 +1,1045 @@
+/*
+ *  ahci.c - AHCI SATA support
+ *
+ *  Copyright 2004 Red Hat, Inc.
+ *
+ *  The contents of this file are subject to the Open
+ *  Software License version 1.1 that can be found at
+ *  http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ *  by reference.
+ *
+ *  Alternatively, the contents of this file may be used under the terms
+ *  of the GNU General Public License version 2 (the "GPL") as distributed
+ *  in the kernel source COPYING file, in which case the provisions of
+ *  the GPL are applicable instead of the above.  If you wish to allow
+ *  the use of your version of this file only under the terms of the
+ *  GPL and not to allow others to use your version of this file under
+ *  the OSL, indicate your decision by deleting the provisions above and
+ *  replace them with the notice and other provisions required by the GPL.
+ *  If you do not delete the provisions above, a recipient may use your
+ *  version of this file under either the OSL or the GPL.
+ *
+ * Version 1.0 of the AHCI specification:
+ * http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+#include <asm/io.h>
+
+#define DRV_NAME       "ahci"
+#define DRV_VERSION    "1.00"
+
+
+enum {
+       AHCI_PCI_BAR            = 5,
+       AHCI_MAX_SG             = 168, /* hardware max is 64K */
+       AHCI_DMA_BOUNDARY       = 0xffffffff,
+       AHCI_USE_CLUSTERING     = 0,
+       AHCI_CMD_SLOT_SZ        = 32 * 32,
+       AHCI_RX_FIS_SZ          = 256,
+       AHCI_CMD_TBL_HDR        = 0x80,
+       AHCI_CMD_TBL_SZ         = AHCI_CMD_TBL_HDR + (AHCI_MAX_SG * 16),
+       AHCI_PORT_PRIV_DMA_SZ   = AHCI_CMD_SLOT_SZ + AHCI_CMD_TBL_SZ +
+                                 AHCI_RX_FIS_SZ,
+       AHCI_IRQ_ON_SG          = (1 << 31),
+       AHCI_CMD_ATAPI          = (1 << 5),
+       AHCI_CMD_WRITE          = (1 << 6),
+
+       RX_FIS_D2H_REG          = 0x40, /* offset of D2H Register FIS data */
+
+       board_ahci              = 0,
+
+       /* global controller registers */
+       HOST_CAP                = 0x00, /* host capabilities */
+       HOST_CTL                = 0x04, /* global host control */
+       HOST_IRQ_STAT           = 0x08, /* interrupt status */
+       HOST_PORTS_IMPL         = 0x0c, /* bitmap of implemented ports */
+       HOST_VERSION            = 0x10, /* AHCI spec. version compliancy */
+
+       /* HOST_CTL bits */
+       HOST_RESET              = (1 << 0),  /* reset controller; self-clear */
+       HOST_IRQ_EN             = (1 << 1),  /* global IRQ enable */
+       HOST_AHCI_EN            = (1 << 31), /* AHCI enabled */
+
+       /* HOST_CAP bits */
+       HOST_CAP_64             = (1 << 31), /* PCI DAC (64-bit DMA) support */
+
+       /* registers for each SATA port */
+       PORT_LST_ADDR           = 0x00, /* command list DMA addr */
+       PORT_LST_ADDR_HI        = 0x04, /* command list DMA addr hi */
+       PORT_FIS_ADDR           = 0x08, /* FIS rx buf addr */
+       PORT_FIS_ADDR_HI        = 0x0c, /* FIS rx buf addr hi */
+       PORT_IRQ_STAT           = 0x10, /* interrupt status */
+       PORT_IRQ_MASK           = 0x14, /* interrupt enable/disable mask */
+       PORT_CMD                = 0x18, /* port command */
+       PORT_TFDATA             = 0x20, /* taskfile data */
+       PORT_SIG                = 0x24, /* device TF signature */
+       PORT_CMD_ISSUE          = 0x38, /* command issue */
+       PORT_SCR                = 0x28, /* SATA phy register block */
+       PORT_SCR_STAT           = 0x28, /* SATA phy register: SStatus */
+       PORT_SCR_CTL            = 0x2c, /* SATA phy register: SControl */
+       PORT_SCR_ERR            = 0x30, /* SATA phy register: SError */
+       PORT_SCR_ACT            = 0x34, /* SATA phy register: SActive */
+
+       /* PORT_IRQ_{STAT,MASK} bits */
+       PORT_IRQ_COLD_PRES      = (1 << 31), /* cold presence detect */
+       PORT_IRQ_TF_ERR         = (1 << 30), /* task file error */
+       PORT_IRQ_HBUS_ERR       = (1 << 29), /* host bus fatal error */
+       PORT_IRQ_HBUS_DATA_ERR  = (1 << 28), /* host bus data error */
+       PORT_IRQ_IF_ERR         = (1 << 27), /* interface fatal error */
+       PORT_IRQ_IF_NONFATAL    = (1 << 26), /* interface non-fatal error */
+       PORT_IRQ_OVERFLOW       = (1 << 24), /* xfer exhausted available S/G */
+       PORT_IRQ_BAD_PMP        = (1 << 23), /* incorrect port multiplier */
+
+       PORT_IRQ_PHYRDY         = (1 << 22), /* PhyRdy changed */
+       PORT_IRQ_DEV_ILCK       = (1 << 7), /* device interlock */
+       PORT_IRQ_CONNECT        = (1 << 6), /* port connect change status */
+       PORT_IRQ_SG_DONE        = (1 << 5), /* descriptor processed */
+       PORT_IRQ_UNK_FIS        = (1 << 4), /* unknown FIS rx'd */
+       PORT_IRQ_SDB_FIS        = (1 << 3), /* Set Device Bits FIS rx'd */
+       PORT_IRQ_DMAS_FIS       = (1 << 2), /* DMA Setup FIS rx'd */
+       PORT_IRQ_PIOS_FIS       = (1 << 1), /* PIO Setup FIS rx'd */
+       PORT_IRQ_D2H_REG_FIS    = (1 << 0), /* D2H Register FIS rx'd */
+
+       PORT_IRQ_FATAL          = PORT_IRQ_TF_ERR |
+                                 PORT_IRQ_HBUS_ERR |
+                                 PORT_IRQ_HBUS_DATA_ERR |
+                                 PORT_IRQ_IF_ERR,
+       DEF_PORT_IRQ            = PORT_IRQ_FATAL | PORT_IRQ_PHYRDY |
+                                 PORT_IRQ_CONNECT | PORT_IRQ_SG_DONE |
+                                 PORT_IRQ_UNK_FIS | PORT_IRQ_SDB_FIS |
+                                 PORT_IRQ_DMAS_FIS | PORT_IRQ_PIOS_FIS |
+                                 PORT_IRQ_D2H_REG_FIS,
+
+       /* PORT_CMD bits */
+       PORT_CMD_LIST_ON        = (1 << 15), /* cmd list DMA engine running */
+       PORT_CMD_FIS_ON         = (1 << 14), /* FIS DMA engine running */
+       PORT_CMD_FIS_RX         = (1 << 4), /* Enable FIS receive DMA engine */
+       PORT_CMD_POWER_ON       = (1 << 2), /* Power up device */
+       PORT_CMD_SPIN_UP        = (1 << 1), /* Spin up device */
+       PORT_CMD_START          = (1 << 0), /* Enable port DMA engine */
+
+       PORT_CMD_ICC_ACTIVE     = (0x1 << 28), /* Put i/f in active state */
+       PORT_CMD_ICC_PARTIAL    = (0x2 << 28), /* Put i/f in partial state */
+       PORT_CMD_ICC_SLUMBER    = (0x6 << 28), /* Put i/f in slumber state */
+};
+
+struct ahci_cmd_hdr {
+       u32                     opts;
+       u32                     status;
+       u32                     tbl_addr;
+       u32                     tbl_addr_hi;
+       u32                     reserved[4];
+};
+
+struct ahci_sg {
+       u32                     addr;
+       u32                     addr_hi;
+       u32                     reserved;
+       u32                     flags_size;
+};
+
+struct ahci_host_priv {
+       unsigned long           flags;
+       u32                     cap;    /* cache of HOST_CAP register */
+       u32                     port_map; /* cache of HOST_PORTS_IMPL reg */
+};
+
+struct ahci_port_priv {
+       struct ahci_cmd_hdr     *cmd_slot;
+       dma_addr_t              cmd_slot_dma;
+       void                    *cmd_tbl;
+       dma_addr_t              cmd_tbl_dma;
+       struct ahci_sg          *cmd_tbl_sg;
+       void                    *rx_fis;
+       dma_addr_t              rx_fis_dma;
+};
+
+static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static int ahci_qc_issue(struct ata_queued_cmd *qc);
+static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs);
+static void ahci_phy_reset(struct ata_port *ap);
+static void ahci_irq_clear(struct ata_port *ap);
+static void ahci_eng_timeout(struct ata_port *ap);
+static int ahci_port_start(struct ata_port *ap);
+static void ahci_port_stop(struct ata_port *ap);
+static void ahci_host_stop(struct ata_host_set *host_set);
+static void ahci_qc_prep(struct ata_queued_cmd *qc);
+static u8 ahci_check_status(struct ata_port *ap);
+static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc);
+
+static Scsi_Host_Template ahci_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .eh_strategy_handler    = ata_scsi_error,
+       .can_queue              = ATA_DEF_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = AHCI_MAX_SG,
+       .max_sectors            = ATA_MAX_SECTORS,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       .use_clustering         = AHCI_USE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = AHCI_DMA_BOUNDARY,
+       .slave_configure        = ata_scsi_slave_config,
+       .bios_param             = ata_std_bios_param,
+};
+
+static struct ata_port_operations ahci_ops = {
+       .port_disable           = ata_port_disable,
+
+       .check_status           = ahci_check_status,
+       .dev_select             = ata_noop_dev_select,
+
+       .phy_reset              = ahci_phy_reset,
+
+       .qc_prep                = ahci_qc_prep,
+       .qc_issue               = ahci_qc_issue,
+
+       .eng_timeout            = ahci_eng_timeout,
+
+       .irq_handler            = ahci_interrupt,
+       .irq_clear              = ahci_irq_clear,
+
+       .scr_read               = ahci_scr_read,
+       .scr_write              = ahci_scr_write,
+
+       .port_start             = ahci_port_start,
+       .port_stop              = ahci_port_stop,
+       .host_stop              = ahci_host_stop,
+};
+
+static struct ata_port_info ahci_port_info[] = {
+       /* board_ahci */
+       {
+               .sht            = &ahci_sht,
+               .host_flags     = ATA_FLAG_SATA | ATA_FLAG_NO_LEGACY |
+                                 ATA_FLAG_SATA_RESET | ATA_FLAG_MMIO |
+                                 ATA_FLAG_PIO_DMA,
+               .pio_mask       = 0x03, /* pio3-4 */
+               .udma_mask      = 0x7f, /* udma0-6 ; FIXME */
+               .port_ops       = &ahci_ops,
+       },
+};
+
+static struct pci_device_id ahci_pci_tbl[] = {
+       { PCI_VENDOR_ID_INTEL, 0x2652, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci },
+       { PCI_VENDOR_ID_INTEL, 0x2653, PCI_ANY_ID, PCI_ANY_ID, 0, 0,
+         board_ahci },
+       { }     /* terminate list */
+};
+
+
+static struct pci_driver ahci_pci_driver = {
+       .name                   = DRV_NAME,
+       .id_table               = ahci_pci_tbl,
+       .probe                  = ahci_init_one,
+       .remove                 = ata_pci_remove_one,
+};
+
+
+static inline unsigned long ahci_port_base_ul (unsigned long base, unsigned int port)
+{
+       return base + 0x100 + (port * 0x80);
+}
+
+static inline void *ahci_port_base (void *base, unsigned int port)
+{
+       return (void *) ahci_port_base_ul((unsigned long)base, port);
+}
+
+static void ahci_host_stop(struct ata_host_set *host_set)
+{
+       struct ahci_host_priv *hpriv = host_set->private_data;
+       kfree(hpriv);
+}
+
+static int ahci_port_start(struct ata_port *ap)
+{
+       struct pci_dev *pdev = ap->host_set->pdev;
+       struct ahci_host_priv *hpriv = ap->host_set->private_data;
+       struct ahci_port_priv *pp;
+       int rc;
+       void *mem, *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       dma_addr_t mem_dma;
+
+       rc = ata_port_start(ap);
+       if (rc)
+               return rc;
+
+       pp = kmalloc(sizeof(*pp), GFP_KERNEL);
+       if (!pp) {
+               rc = -ENOMEM;
+               goto err_out;
+       }
+       memset(pp, 0, sizeof(*pp));
+
+       mem = pci_alloc_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ, &mem_dma);
+       if (!mem) {
+               rc = -ENOMEM;
+               goto err_out_kfree;
+       }
+       memset(mem, 0, AHCI_PORT_PRIV_DMA_SZ);
+
+       /*
+        * First item in chunk of DMA memory: 32-slot command table,
+        * 32 bytes each in size
+        */
+       pp->cmd_slot = mem;
+       pp->cmd_slot_dma = mem_dma;
+
+       mem += AHCI_CMD_SLOT_SZ;
+       mem_dma += AHCI_CMD_SLOT_SZ;
+
+       /*
+        * Second item: Received-FIS area
+        */
+       pp->rx_fis = mem;
+       pp->rx_fis_dma = mem_dma;
+
+       mem += AHCI_RX_FIS_SZ;
+       mem_dma += AHCI_RX_FIS_SZ;
+
+       /*
+        * Third item: data area for storing a single command
+        * and its scatter-gather table
+        */
+       pp->cmd_tbl = mem;
+       pp->cmd_tbl_dma = mem_dma;
+
+       pp->cmd_tbl_sg = mem + AHCI_CMD_TBL_HDR;
+
+       ap->private_data = pp;
+
+       if (hpriv->cap & HOST_CAP_64)
+               writel((pp->cmd_slot_dma >> 16) >> 16, port_mmio + PORT_LST_ADDR_HI);
+       writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);
+       readl(port_mmio + PORT_LST_ADDR); /* flush */
+
+       if (hpriv->cap & HOST_CAP_64)
+               writel((pp->rx_fis_dma >> 16) >> 16, port_mmio + PORT_FIS_ADDR_HI);
+       writel(pp->rx_fis_dma & 0xffffffff, port_mmio + PORT_FIS_ADDR);
+       readl(port_mmio + PORT_FIS_ADDR); /* flush */
+
+       writel(PORT_CMD_ICC_ACTIVE | PORT_CMD_FIS_RX |
+              PORT_CMD_POWER_ON | PORT_CMD_SPIN_UP |
+              PORT_CMD_START, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+
+       return 0;
+
+err_out_kfree:
+       kfree(pp);
+err_out:
+       ata_port_stop(ap);
+       return rc;
+}
+
+
+static void ahci_port_stop(struct ata_port *ap)
+{
+       struct pci_dev *pdev = ap->host_set->pdev;
+       struct ahci_port_priv *pp = ap->private_data;
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       u32 tmp;
+
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp &= ~(PORT_CMD_START | PORT_CMD_FIS_RX);
+       writel(tmp, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+
+       /* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
+        * this is slightly incorrect.
+        */
+       msleep(500);
+
+       ap->private_data = NULL;
+       pci_free_consistent(pdev, AHCI_PORT_PRIV_DMA_SZ,
+                           pp->cmd_slot, pp->cmd_slot_dma);
+       kfree(pp);
+       ata_port_stop(ap);
+}
+
+static u32 ahci_scr_read (struct ata_port *ap, unsigned int sc_reg_in)
+{
+       unsigned int sc_reg;
+
+       switch (sc_reg_in) {
+       case SCR_STATUS:        sc_reg = 0; break;
+       case SCR_CONTROL:       sc_reg = 1; break;
+       case SCR_ERROR:         sc_reg = 2; break;
+       case SCR_ACTIVE:        sc_reg = 3; break;
+       default:
+               return 0xffffffffU;
+       }
+
+       return readl((void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+
+static void ahci_scr_write (struct ata_port *ap, unsigned int sc_reg_in,
+                              u32 val)
+{
+       unsigned int sc_reg;
+
+       switch (sc_reg_in) {
+       case SCR_STATUS:        sc_reg = 0; break;
+       case SCR_CONTROL:       sc_reg = 1; break;
+       case SCR_ERROR:         sc_reg = 2; break;
+       case SCR_ACTIVE:        sc_reg = 3; break;
+       default:
+               return;
+       }
+
+       writel(val, (void *) ap->ioaddr.scr_addr + (sc_reg * 4));
+}
+
+static void ahci_phy_reset(struct ata_port *ap)
+{
+       void __iomem *port_mmio = (void __iomem *) ap->ioaddr.cmd_addr;
+       struct ata_taskfile tf;
+       struct ata_device *dev = &ap->device[0];
+       u32 tmp;
+
+       __sata_phy_reset(ap);
+
+       if (ap->flags & ATA_FLAG_PORT_DISABLED)
+               return;
+
+       tmp = readl(port_mmio + PORT_SIG);
+       tf.lbah         = (tmp >> 24)   & 0xff;
+       tf.lbam         = (tmp >> 16)   & 0xff;
+       tf.lbal         = (tmp >> 8)    & 0xff;
+       tf.nsect        = (tmp)         & 0xff;
+
+       dev->class = ata_dev_classify(&tf);
+       if (!ata_dev_present(dev))
+               ata_port_disable(ap);
+}
+
+static u8 ahci_check_status(struct ata_port *ap)
+{
+       void *mmio = (void *) ap->ioaddr.cmd_addr;
+
+       return readl(mmio + PORT_TFDATA) & 0xFF;
+}
+
+static void ahci_fill_sg(struct ata_queued_cmd *qc)
+{
+       struct ahci_port_priv *pp = qc->ap->private_data;
+       unsigned int i;
+
+       VPRINTK("ENTER\n");
+
+       /*
+        * Next, the S/G list.
+        */
+       for (i = 0; i < qc->n_elem; i++) {
+               u32 sg_len;
+               dma_addr_t addr;
+
+               addr = sg_dma_address(&qc->sg[i]);
+               sg_len = sg_dma_len(&qc->sg[i]);
+
+               pp->cmd_tbl_sg[i].addr = cpu_to_le32(addr & 0xffffffff);
+               pp->cmd_tbl_sg[i].addr_hi = cpu_to_le32((addr >> 16) >> 16);
+               pp->cmd_tbl_sg[i].flags_size = cpu_to_le32(sg_len - 1);
+       }
+}
+
+static void ahci_qc_prep(struct ata_queued_cmd *qc)
+{
+       struct ahci_port_priv *pp = qc->ap->private_data;
+       u32 opts;
+       const u32 cmd_fis_len = 5; /* five dwords */
+
+       /*
+        * Fill in command slot information (currently only one slot,
+        * slot 0, is currently since we don't do queueing)
+        */
+
+       opts = (qc->n_elem << 16) | cmd_fis_len;
+       if (qc->tf.flags & ATA_TFLAG_WRITE)
+               opts |= AHCI_CMD_WRITE;
+
+       switch (qc->tf.protocol) {
+       case ATA_PROT_ATAPI:
+       case ATA_PROT_ATAPI_NODATA:
+       case ATA_PROT_ATAPI_DMA:
+               opts |= AHCI_CMD_ATAPI;
+               break;
+
+       default:
+               /* do nothing */
+               break;
+       }
+
+       pp->cmd_slot[0].opts = cpu_to_le32(opts);
+       pp->cmd_slot[0].status = 0;
+       pp->cmd_slot[0].tbl_addr = cpu_to_le32(pp->cmd_tbl_dma & 0xffffffff);
+       pp->cmd_slot[0].tbl_addr_hi = cpu_to_le32((pp->cmd_tbl_dma >> 16) >> 16);
+
+       /*
+        * Fill in command table information.  First, the header,
+        * a SATA Register - Host to Device command FIS.
+        */
+       ata_tf_to_fis(&qc->tf, pp->cmd_tbl, 0);
+
+       if (!(qc->flags & ATA_QCFLAG_DMAMAP))
+               return;
+
+       ahci_fill_sg(qc);
+}
+
+static inline void ahci_dma_complete (struct ata_port *ap,
+                                     struct ata_queued_cmd *qc,
+                                    int have_err)
+{
+       /* get drive status; clear intr; complete txn */
+       ata_qc_complete(ata_qc_from_tag(ap, ap->active_tag),
+                       have_err ? ATA_ERR : 0);
+}
+
+static void ahci_intr_error(struct ata_port *ap, u32 irq_stat)
+{
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       u32 tmp;
+       int work;
+
+       /* stop DMA */
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp &= PORT_CMD_START | PORT_CMD_FIS_RX;
+       writel(tmp, port_mmio + PORT_CMD);
+
+       /* wait for engine to stop.  TODO: this could be
+        * as long as 500 msec
+        */
+       work = 1000;
+       while (work-- > 0) {
+               tmp = readl(port_mmio + PORT_CMD);
+               if ((tmp & PORT_CMD_LIST_ON) == 0)
+                       break;
+               udelay(10);
+       }
+
+       /* clear SATA phy error, if any */
+       tmp = readl(port_mmio + PORT_SCR_ERR);
+       writel(tmp, port_mmio + PORT_SCR_ERR);
+
+       /* if DRQ/BSY is set, device needs to be reset.
+        * if so, issue COMRESET
+        */
+       tmp = readl(port_mmio + PORT_TFDATA);
+       if (tmp & (ATA_BUSY | ATA_DRQ)) {
+               writel(0x301, port_mmio + PORT_SCR_CTL);
+               readl(port_mmio + PORT_SCR_CTL); /* flush */
+               udelay(10);
+               writel(0x300, port_mmio + PORT_SCR_CTL);
+               readl(port_mmio + PORT_SCR_CTL); /* flush */
+       }
+
+       /* re-start DMA */
+       tmp = readl(port_mmio + PORT_CMD);
+       tmp |= PORT_CMD_START | PORT_CMD_FIS_RX;
+       writel(tmp, port_mmio + PORT_CMD);
+       readl(port_mmio + PORT_CMD); /* flush */
+
+       printk(KERN_WARNING "ata%u: error occurred, port reset\n", ap->port_no);
+}
+
+static void ahci_eng_timeout(struct ata_port *ap)
+{
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       struct ata_queued_cmd *qc;
+
+       DPRINTK("ENTER\n");
+
+       ahci_intr_error(ap, readl(port_mmio + PORT_IRQ_STAT));
+
+       qc = ata_qc_from_tag(ap, ap->active_tag);
+       if (!qc) {
+               printk(KERN_ERR "ata%u: BUG: timeout without command\n",
+                      ap->id);
+       } else {
+               /* hack alert!  We cannot use the supplied completion
+                * function from inside the ->eh_strategy_handler() thread.
+                * libata is the only user of ->eh_strategy_handler() in
+                * any kernel, so the default scsi_done() assumes it is
+                * not being called from the SCSI EH.
+                */
+               qc->scsidone = scsi_finish_command;
+               ata_qc_complete(qc, ATA_ERR);
+       }
+
+}
+
+static inline int ahci_host_intr(struct ata_port *ap, struct ata_queued_cmd *qc)
+{
+       void *mmio = ap->host_set->mmio_base;
+       void *port_mmio = ahci_port_base(mmio, ap->port_no);
+       u32 status, serr, ci;
+
+       serr = readl(port_mmio + PORT_SCR_ERR);
+       writel(serr, port_mmio + PORT_SCR_ERR);
+
+       status = readl(port_mmio + PORT_IRQ_STAT);
+       writel(status, port_mmio + PORT_IRQ_STAT);
+
+       ci = readl(port_mmio + PORT_CMD_ISSUE);
+       if (likely((ci & 0x1) == 0)) {
+               if (qc) {
+                       ata_qc_complete(qc, 0);
+                       qc = NULL;
+               }
+       }
+
+       if (status & PORT_IRQ_FATAL) {
+               ahci_intr_error(ap, status);
+               if (qc)
+                       ata_qc_complete(qc, ATA_ERR);
+       }
+
+       return 1;
+}
+
+static void ahci_irq_clear(struct ata_port *ap)
+{
+       /* TODO */
+}
+
+static irqreturn_t ahci_interrupt (int irq, void *dev_instance, struct pt_regs *regs)
+{
+       struct ata_host_set *host_set = dev_instance;
+       struct ahci_host_priv *hpriv;
+       unsigned int i, handled = 0;
+       void *mmio;
+       u32 irq_stat, irq_ack = 0;
+
+       VPRINTK("ENTER\n");
+
+       hpriv = host_set->private_data;
+       mmio = host_set->mmio_base;
+
+       /* sigh.  0xffffffff is a valid return from h/w */
+       irq_stat = readl(mmio + HOST_IRQ_STAT);
+       irq_stat &= hpriv->port_map;
+       if (!irq_stat)
+               return IRQ_NONE;
+
+        spin_lock(&host_set->lock);
+
+        for (i = 0; i < host_set->n_ports; i++) {
+               struct ata_port *ap;
+               u32 tmp;
+
+               VPRINTK("port %u\n", i);
+               ap = host_set->ports[i];
+               tmp = irq_stat & (1 << i);
+               if (tmp && ap) {
+                       struct ata_queued_cmd *qc;
+                       qc = ata_qc_from_tag(ap, ap->active_tag);
+                       if (ahci_host_intr(ap, qc))
+                               irq_ack |= (1 << i);
+               }
+       }
+
+       if (irq_ack) {
+               writel(irq_ack, mmio + HOST_IRQ_STAT);
+               handled = 1;
+       }
+
+        spin_unlock(&host_set->lock);
+
+       VPRINTK("EXIT\n");
+
+       return IRQ_RETVAL(handled);
+}
+
+static int ahci_qc_issue(struct ata_queued_cmd *qc)
+{
+       struct ata_port *ap = qc->ap;
+       void *port_mmio = (void *) ap->ioaddr.cmd_addr;
+
+       writel(1, port_mmio + PORT_SCR_ACT);
+       readl(port_mmio + PORT_SCR_ACT);        /* flush */
+
+       writel(1, port_mmio + PORT_CMD_ISSUE);
+       readl(port_mmio + PORT_CMD_ISSUE);      /* flush */
+
+       return 0;
+}
+
+static void ahci_setup_port(struct ata_ioports *port, unsigned long base,
+                           unsigned int port_idx)
+{
+       VPRINTK("ENTER, base==0x%lx, port_idx %u\n", base, port_idx);
+       base = ahci_port_base_ul(base, port_idx);
+       VPRINTK("base now==0x%lx\n", base);
+
+       port->cmd_addr          = base;
+       port->scr_addr          = base + PORT_SCR;
+
+       VPRINTK("EXIT\n");
+}
+
+static int ahci_host_init(struct ata_probe_ent *probe_ent)
+{
+       struct ahci_host_priv *hpriv = probe_ent->private_data;
+       struct pci_dev *pdev = probe_ent->pdev;
+       void __iomem *mmio = probe_ent->mmio_base;
+       u32 tmp, cap_save;
+       u16 tmp16;
+       unsigned int i, j, using_dac;
+       int rc;
+       void __iomem *port_mmio;
+
+       cap_save = readl(mmio + HOST_CAP);
+       cap_save &= ( (1<<28) | (1<<17) );
+       cap_save |= (1 << 27);
+
+       /* global controller reset */
+       tmp = readl(mmio + HOST_CTL);
+       if ((tmp & HOST_RESET) == 0) {
+               writel(tmp | HOST_RESET, mmio + HOST_CTL);
+               readl(mmio + HOST_CTL); /* flush */
+       }
+
+       /* reset must complete within 1 second, or
+        * the hardware should be considered fried.
+        */
+       ssleep(1);
+
+       tmp = readl(mmio + HOST_CTL);
+       if (tmp & HOST_RESET) {
+               printk(KERN_ERR DRV_NAME "(%s): controller reset failed (0x%x)\n",
+                       pci_name(pdev), tmp);
+               return -EIO;
+       }
+
+       writel(HOST_AHCI_EN, mmio + HOST_CTL);
+       (void) readl(mmio + HOST_CTL);  /* flush */
+       writel(cap_save, mmio + HOST_CAP);
+       writel(0xf, mmio + HOST_PORTS_IMPL);
+       (void) readl(mmio + HOST_PORTS_IMPL);   /* flush */
+
+       pci_read_config_word(pdev, 0x92, &tmp16);
+       tmp16 |= 0xf;
+       pci_write_config_word(pdev, 0x92, tmp16);
+
+       hpriv->cap = readl(mmio + HOST_CAP);
+       hpriv->port_map = readl(mmio + HOST_PORTS_IMPL);
+       probe_ent->n_ports = (hpriv->cap & 0x1f) + 1;
+
+       VPRINTK("cap 0x%x  port_map 0x%x  n_ports %d\n",
+               hpriv->cap, hpriv->port_map, probe_ent->n_ports);
+
+       using_dac = hpriv->cap & HOST_CAP_64;
+       if (using_dac &&
+           !pci_set_dma_mask(pdev, 0xffffffffffffffffULL)) {
+               rc = pci_set_consistent_dma_mask(pdev, 0xffffffffffffffffULL);
+               if (rc) {
+                       rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+                       if (rc) {
+                               printk(KERN_ERR DRV_NAME "(%s): 64-bit DMA enable failed\n",
+                                       pci_name(pdev));
+                               return rc;
+                       }
+               }
+
+               hpriv->flags |= HOST_CAP_64;
+       } else {
+               rc = pci_set_dma_mask(pdev, 0xffffffffULL);
+               if (rc) {
+                       printk(KERN_ERR DRV_NAME "(%s): 32-bit DMA enable failed\n",
+                               pci_name(pdev));
+                       return rc;
+               }
+               rc = pci_set_consistent_dma_mask(pdev, 0xffffffffULL);
+               if (rc) {
+                       printk(KERN_ERR DRV_NAME "(%s): 32-bit consistent DMA enable failed\n",
+                               pci_name(pdev));
+                       return rc;
+               }
+       }
+
+       for (i = 0; i < probe_ent->n_ports; i++) {
+#if 0 /* BIOSen initialize this incorrectly */
+               if (!(hpriv->port_map & (1 << i)))
+                       continue;
+#endif
+
+               port_mmio = ahci_port_base(mmio, i);
+               VPRINTK("mmio %p  port_mmio %p\n", mmio, port_mmio);
+
+               ahci_setup_port(&probe_ent->port[i],
+                               (unsigned long) mmio, i);
+
+               /* make sure port is not active */
+               tmp = readl(port_mmio + PORT_CMD);
+               VPRINTK("PORT_CMD 0x%x\n", tmp);
+               if (tmp & (PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
+                          PORT_CMD_FIS_RX | PORT_CMD_START)) {
+                       tmp &= ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON |
+                                PORT_CMD_FIS_RX | PORT_CMD_START);
+                       writel(tmp, port_mmio + PORT_CMD);
+                       readl(port_mmio + PORT_CMD); /* flush */
+
+                       /* spec says 500 msecs for each bit, so
+                        * this is slightly incorrect.
+                        */
+                       msleep(500);
+               }
+
+               writel(PORT_CMD_SPIN_UP, port_mmio + PORT_CMD);
+
+               j = 0;
+               while (j < 100) {
+                       msleep(10);
+                       tmp = readl(port_mmio + PORT_SCR_STAT);
+                       if ((tmp & 0xf) == 0x3)
+                               break;
+                       j++;
+               }
+
+               tmp = readl(port_mmio + PORT_SCR_ERR);
+               VPRINTK("PORT_SCR_ERR 0x%x\n", tmp);
+               writel(tmp, port_mmio + PORT_SCR_ERR);
+
+               /* ack any pending irq events for this port */
+               tmp = readl(port_mmio + PORT_IRQ_STAT);
+               VPRINTK("PORT_IRQ_STAT 0x%x\n", tmp);
+               if (tmp)
+                       writel(tmp, port_mmio + PORT_IRQ_STAT);
+
+               writel(1 << i, mmio + HOST_IRQ_STAT);
+
+               /* set irq mask (enables interrupts) */
+               writel(DEF_PORT_IRQ, port_mmio + PORT_IRQ_MASK);
+       }
+
+       tmp = readl(mmio + HOST_CTL);
+       VPRINTK("HOST_CTL 0x%x\n", tmp);
+       writel(tmp | HOST_IRQ_EN, mmio + HOST_CTL);
+       tmp = readl(mmio + HOST_CTL);
+       VPRINTK("HOST_CTL 0x%x\n", tmp);
+
+       pci_set_master(pdev);
+
+       return 0;
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+       u16 pci_command;
+
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+       if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+               pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+       }
+}
+
+static void ahci_print_info(struct ata_probe_ent *probe_ent)
+{
+       struct ahci_host_priv *hpriv = probe_ent->private_data;
+       struct pci_dev *pdev = probe_ent->pdev;
+       void *mmio = probe_ent->mmio_base;
+       u32 vers, cap, impl, speed;
+       const char *speed_s;
+       u16 cc;
+       const char *scc_s;
+
+       vers = readl(mmio + HOST_VERSION);
+       cap = hpriv->cap;
+       impl = hpriv->port_map;
+
+       speed = (cap >> 20) & 0xf;
+       if (speed == 1)
+               speed_s = "1.5";
+       else if (speed == 2)
+               speed_s = "3";
+       else
+               speed_s = "?";
+
+       pci_read_config_word(pdev, 0x0a, &cc);
+       if (cc == 0x0101)
+               scc_s = "IDE";
+       else if (cc == 0x0106)
+               scc_s = "SATA";
+       else if (cc == 0x0104)
+               scc_s = "RAID";
+       else
+               scc_s = "unknown";
+
+       printk(KERN_INFO DRV_NAME "(%s) AHCI %02x%02x.%02x%02x "
+               "%u slots %u ports %s Gbps 0x%x impl %s mode\n"
+               ,
+               pci_name(pdev),
+
+               (vers >> 24) & 0xff,
+               (vers >> 16) & 0xff,
+               (vers >> 8) & 0xff,
+               vers & 0xff,
+
+               ((cap >> 8) & 0x1f) + 1,
+               (cap & 0x1f) + 1,
+               speed_s,
+               impl,
+               scc_s);
+
+       printk(KERN_INFO DRV_NAME "(%s) flags: "
+               "%s%s%s%s%s%s"
+               "%s%s%s%s%s%s%s\n"
+               ,
+               pci_name(pdev),
+
+               cap & (1 << 31) ? "64bit " : "",
+               cap & (1 << 30) ? "ncq " : "",
+               cap & (1 << 28) ? "ilck " : "",
+               cap & (1 << 27) ? "stag " : "",
+               cap & (1 << 26) ? "pm " : "",
+               cap & (1 << 25) ? "led " : "",
+
+               cap & (1 << 24) ? "clo " : "",
+               cap & (1 << 19) ? "nz " : "",
+               cap & (1 << 18) ? "only " : "",
+               cap & (1 << 17) ? "pmp " : "",
+               cap & (1 << 15) ? "pio " : "",
+               cap & (1 << 14) ? "slum " : "",
+               cap & (1 << 13) ? "part " : ""
+               );
+}
+
+static int ahci_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       static int printed_version;
+       struct ata_probe_ent *probe_ent = NULL;
+       struct ahci_host_priv *hpriv;
+       unsigned long base;
+       void *mmio_base;
+       unsigned int board_idx = (unsigned int) ent->driver_data;
+       int rc;
+
+       VPRINTK("ENTER\n");
+
+       if (!printed_version++)
+               printk(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n");
+
+       /*
+        * If this driver happens to only be useful on Apple's K2, then
+        * we should check that here as it has a normal Serverworks ID
+        */
+       rc = pci_enable_device(pdev);
+       if (rc)
+               return rc;
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out;
+
+       pci_enable_intx(pdev);
+
+       probe_ent = kmalloc(sizeof(*probe_ent), GFP_KERNEL);
+       if (probe_ent == NULL) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       memset(probe_ent, 0, sizeof(*probe_ent));
+       probe_ent->pdev = pdev;
+       INIT_LIST_HEAD(&probe_ent->node);
+
+       mmio_base = ioremap(pci_resource_start(pdev, AHCI_PCI_BAR),
+                           pci_resource_len(pdev, AHCI_PCI_BAR));
+       if (mmio_base == NULL) {
+               rc = -ENOMEM;
+               goto err_out_free_ent;
+       }
+       base = (unsigned long) mmio_base;
+
+       hpriv = kmalloc(sizeof(*hpriv), GFP_KERNEL);
+       if (!hpriv) {
+               rc = -ENOMEM;
+               goto err_out_iounmap;
+       }
+       memset(hpriv, 0, sizeof(*hpriv));
+
+       probe_ent->sht          = ahci_port_info[board_idx].sht;
+       probe_ent->host_flags   = ahci_port_info[board_idx].host_flags;
+       probe_ent->pio_mask     = ahci_port_info[board_idx].pio_mask;
+       probe_ent->udma_mask    = ahci_port_info[board_idx].udma_mask;
+       probe_ent->port_ops     = ahci_port_info[board_idx].port_ops;
+
+               probe_ent->irq = pdev->irq;
+               probe_ent->irq_flags = SA_SHIRQ;
+       probe_ent->mmio_base = mmio_base;
+       probe_ent->private_data = hpriv;
+
+       /* initialize adapter */
+       rc = ahci_host_init(probe_ent);
+       if (rc)
+               goto err_out_hpriv;
+
+       ahci_print_info(probe_ent);
+
+       /* FIXME: check ata_device_add return value */
+       ata_device_add(probe_ent);
+       kfree(probe_ent);
+
+       return 0;
+
+err_out_hpriv:
+       kfree(hpriv);
+err_out_iounmap:
+       iounmap(mmio_base);
+err_out_free_ent:
+       kfree(probe_ent);
+err_out_regions:
+       pci_release_regions(pdev);
+err_out:
+       pci_disable_device(pdev);
+       return rc;
+}
+
+
+static int __init ahci_init(void)
+{
+       return pci_module_init(&ahci_pci_driver);
+}
+
+
+static void __exit ahci_exit(void)
+{
+       pci_unregister_driver(&ahci_pci_driver);
+}
+
+
+MODULE_AUTHOR("Jeff Garzik");
+MODULE_DESCRIPTION("AHCI SATA low-level driver");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, ahci_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+module_init(ahci_init);
+module_exit(ahci_exit);
diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c
new file mode 100644 (file)
index 0000000..1becb36
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ *  sata_uli.c - ULi Electronics SATA
+ *
+ *  The contents of this file are subject to the Open
+ *  Software License version 1.1 that can be found at
+ *  http://www.opensource.org/licenses/osl-1.1.txt and is included herein
+ *  by reference.
+ *
+ *  Alternatively, the contents of this file may be used under the terms
+ *  of the GNU General Public License version 2 (the "GPL") as distributed
+ *  in the kernel source COPYING file, in which case the provisions of
+ *  the GPL are applicable instead of the above.  If you wish to allow
+ *  the use of your version of this file only under the terms of the
+ *  GPL and not to allow others to use your version of this file under
+ *  the OSL, indicate your decision by deleting the provisions above and
+ *  replace them with the notice and other provisions required by the GPL.
+ *  If you do not delete the provisions above, a recipient may use your
+ *  version of this file under either the OSL or the GPL.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include "scsi.h"
+#include <scsi/scsi_host.h>
+#include <linux/libata.h>
+
+#define DRV_NAME       "sata_uli"
+#define DRV_VERSION    "0.11"
+
+enum {
+       uli_5289                = 0,
+       uli_5287                = 1,
+
+       /* PCI configuration registers */
+       ULI_SCR_BASE            = 0x90, /* sata0 phy SCR registers */
+       ULI_SATA1_OFS           = 0x10, /* offset from sata0->sata1 phy regs */
+
+};
+
+static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent);
+static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg);
+static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val);
+
+static struct pci_device_id uli_pci_tbl[] = {
+       { PCI_VENDOR_ID_AL, 0x5289, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5289 },
+       { PCI_VENDOR_ID_AL, 0x5287, PCI_ANY_ID, PCI_ANY_ID, 0, 0, uli_5287 },
+       { }     /* terminate list */
+};
+
+
+static struct pci_driver uli_pci_driver = {
+       .name                   = DRV_NAME,
+       .id_table               = uli_pci_tbl,
+       .probe                  = uli_init_one,
+       .remove                 = ata_pci_remove_one,
+};
+
+static Scsi_Host_Template uli_sht = {
+       .module                 = THIS_MODULE,
+       .name                   = DRV_NAME,
+       .ioctl                  = ata_scsi_ioctl,
+       .queuecommand           = ata_scsi_queuecmd,
+       .eh_strategy_handler    = ata_scsi_error,
+       .can_queue              = ATA_DEF_QUEUE,
+       .this_id                = ATA_SHT_THIS_ID,
+       .sg_tablesize           = LIBATA_MAX_PRD,
+       .max_sectors            = ATA_MAX_SECTORS,
+       .cmd_per_lun            = ATA_SHT_CMD_PER_LUN,
+       .emulated               = ATA_SHT_EMULATED,
+       .use_clustering         = ATA_SHT_USE_CLUSTERING,
+       .proc_name              = DRV_NAME,
+       .dma_boundary           = ATA_DMA_BOUNDARY,
+       .slave_configure        = ata_scsi_slave_config,
+       .bios_param             = ata_std_bios_param,
+};
+
+static struct ata_port_operations uli_ops = {
+       .port_disable           = ata_port_disable,
+
+       .tf_load                = ata_tf_load,
+       .tf_read                = ata_tf_read,
+       .check_status           = ata_check_status,
+       .exec_command           = ata_exec_command,
+       .dev_select             = ata_std_dev_select,
+
+       .phy_reset              = sata_phy_reset,
+
+       .bmdma_setup            = ata_bmdma_setup,
+       .bmdma_start            = ata_bmdma_start,
+       .qc_prep                = ata_qc_prep,
+       .qc_issue               = ata_qc_issue_prot,
+
+       .eng_timeout            = ata_eng_timeout,
+
+       .irq_handler            = ata_interrupt,
+       .irq_clear              = ata_bmdma_irq_clear,
+
+       .scr_read               = uli_scr_read,
+       .scr_write              = uli_scr_write,
+
+       .port_start             = ata_port_start,
+       .port_stop              = ata_port_stop,
+};
+
+static struct ata_port_info uli_port_info = {
+       .sht            = &uli_sht,
+       .host_flags     = ATA_FLAG_SATA | ATA_FLAG_SATA_RESET |
+                         ATA_FLAG_NO_LEGACY,
+       .pio_mask       = 0x03,         //support pio mode 4 (FIXME)
+       .udma_mask      = 0x7f,         //support udma mode 6
+       .port_ops       = &uli_ops,
+};
+
+
+MODULE_AUTHOR("Peer Chen");
+MODULE_DESCRIPTION("low-level driver for ULi Electronics SATA controller");
+MODULE_LICENSE("GPL");
+MODULE_DEVICE_TABLE(pci, uli_pci_tbl);
+MODULE_VERSION(DRV_VERSION);
+
+static unsigned int get_scr_cfg_addr(unsigned int port_no, unsigned int sc_reg)
+{
+       unsigned int addr = ULI_SCR_BASE + (4 * sc_reg);
+
+       switch (port_no) {
+       case 0:
+               break;
+       case 1:
+               addr += ULI_SATA1_OFS;
+               break;
+       case 2:
+               addr += ULI_SATA1_OFS*4;
+               break;
+       case 3:
+               addr += ULI_SATA1_OFS*5;
+               break;
+       default:
+               BUG();
+               break;
+       }
+       return addr;
+}
+
+static u32 uli_scr_cfg_read (struct ata_port *ap, unsigned int sc_reg)
+{
+       unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, sc_reg);
+       u32 val;
+
+       pci_read_config_dword(ap->host_set->pdev, cfg_addr, &val);
+       return val;
+}
+
+static void uli_scr_cfg_write (struct ata_port *ap, unsigned int scr, u32 val)
+{
+       unsigned int cfg_addr = get_scr_cfg_addr(ap->port_no, scr);
+
+       pci_write_config_dword(ap->host_set->pdev, cfg_addr, val);
+}
+
+static u32 uli_scr_read (struct ata_port *ap, unsigned int sc_reg)
+{
+       if (sc_reg > SCR_CONTROL)
+               return 0xffffffffU;
+
+       return uli_scr_cfg_read(ap, sc_reg);
+}
+
+static void uli_scr_write (struct ata_port *ap, unsigned int sc_reg, u32 val)
+{
+       if (sc_reg > SCR_CONTROL)       //SCR_CONTROL=2, SCR_ERROR=1, SCR_STATUS=0
+               return;
+
+       uli_scr_cfg_write(ap, sc_reg, val);
+}
+
+/* move to PCI layer, integrate w/ MSI stuff */
+static void pci_enable_intx(struct pci_dev *pdev)
+{
+       u16 pci_command;
+
+       pci_read_config_word(pdev, PCI_COMMAND, &pci_command);
+       if (pci_command & PCI_COMMAND_INTX_DISABLE) {
+               pci_command &= ~PCI_COMMAND_INTX_DISABLE;
+               pci_write_config_word(pdev, PCI_COMMAND, pci_command);
+       }
+}
+
+static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       struct ata_probe_ent *probe_ent;
+       struct ata_port_info *ppi;
+       int rc;
+       unsigned int board_idx = (unsigned int) ent->driver_data;
+
+       rc = pci_enable_device(pdev);
+       if (rc)
+               return rc;
+
+       rc = pci_request_regions(pdev, DRV_NAME);
+       if (rc)
+               goto err_out;
+
+       rc = pci_set_dma_mask(pdev, ATA_DMA_MASK);
+       if (rc)
+               goto err_out_regions;
+       rc = pci_set_consistent_dma_mask(pdev, ATA_DMA_MASK);
+       if (rc)
+               goto err_out_regions;
+
+       ppi = &uli_port_info;
+       probe_ent = ata_pci_init_native_mode(pdev, &ppi);
+       if (!probe_ent) {
+               rc = -ENOMEM;
+               goto err_out_regions;
+       }
+
+       switch (board_idx) {
+       case uli_5287:
+                       probe_ent->n_ports = 4;
+
+                       probe_ent->port[2].cmd_addr = pci_resource_start(pdev, 0) + 8;
+               probe_ent->port[2].altstatus_addr =
+               probe_ent->port[2].ctl_addr =
+                       (pci_resource_start(pdev, 1) | ATA_PCI_CTL_OFS) + 4;
+               probe_ent->port[2].bmdma_addr = pci_resource_start(pdev, 4) + 16;
+
+               probe_ent->port[3].cmd_addr = pci_resource_start(pdev, 2) + 8;
+               probe_ent->port[3].altstatus_addr =
+               probe_ent->port[3].ctl_addr =
+                       (pci_resource_start(pdev, 3) | ATA_PCI_CTL_OFS) + 4;
+               probe_ent->port[3].bmdma_addr = pci_resource_start(pdev, 4) + 24;
+
+               ata_std_ports(&probe_ent->port[2]);
+               ata_std_ports(&probe_ent->port[3]);
+               break;
+
+       case uli_5289:
+               /* do nothing; ata_pci_init_native_mode did it all */
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+
+       pci_set_master(pdev);
+       pci_enable_intx(pdev);
+
+       /* FIXME: check ata_device_add return value */
+       ata_device_add(probe_ent);
+       kfree(probe_ent);
+
+       return 0;
+
+err_out_regions:
+       pci_release_regions(pdev);
+
+err_out:
+       pci_disable_device(pdev);
+       return rc;
+
+}
+
+static int __init uli_init(void)
+{
+       return pci_module_init(&uli_pci_driver);
+}
+
+static void __exit uli_exit(void)
+{
+       pci_unregister_driver(&uli_pci_driver);
+}
+
+
+module_init(uli_init);
+module_exit(uli_exit);
diff --git a/drivers/usb/atm/Makefile b/drivers/usb/atm/Makefile
new file mode 100644 (file)
index 0000000..9213b8b
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the rest of the USB drivers
+# (the ones that don't fit into any other categories)
+#
+
+obj-$(CONFIG_USB_ATM)          += usb_atm.o
+obj-$(CONFIG_USB_SPEEDTOUCH)   += speedtch.o
diff --git a/drivers/usb/atm/speedtch.c b/drivers/usb/atm/speedtch.c
new file mode 100644 (file)
index 0000000..c06a3c1
--- /dev/null
@@ -0,0 +1,863 @@
+/******************************************************************************
+ *  speedtch.c  -  Alcatel SpeedTouch USB xDSL modem driver
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ *
+ *  You should have received a copy of the GNU General Public License along with
+ *  this program; if not, write to the Free Software Foundation, Inc., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/gfp.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#      define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#if defined(CONFIG_FW_LOADER) || defined(CONFIG_FW_LOADER_MODULE)
+#      define USE_FW_LOADER
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...)    udsl_print_packet (arg)
+#define vdbg(arg...)           dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC    "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static const char speedtch_driver_name[] = "speedtch";
+
+#define SPEEDTOUCH_VENDORID            0x06b9
+#define SPEEDTOUCH_PRODUCTID           0x4061
+
+/* Timeout in jiffies */
+#define CTRL_TIMEOUT (2*HZ)
+#define DATA_TIMEOUT (2*HZ)
+
+#define OFFSET_7  0            /* size 1 */
+#define OFFSET_b  1            /* size 8 */
+#define OFFSET_d  9            /* size 4 */
+#define OFFSET_e 13            /* size 1 */
+#define OFFSET_f 14            /* size 1 */
+#define TOTAL    15
+
+#define SIZE_7 1
+#define SIZE_b 8
+#define SIZE_d 4
+#define SIZE_e 1
+#define SIZE_f 1
+
+static int dl_512_first = 0;
+static int sw_buffering = 0;
+
+module_param(dl_512_first, bool, 0444);
+MODULE_PARM_DESC(dl_512_first, "Read 512 bytes before sending firmware");
+
+module_param(sw_buffering, uint, 0444);
+MODULE_PARM_DESC(sw_buffering, "Enable software buffering");
+
+#define UDSL_IOCTL_LINE_UP             1
+#define UDSL_IOCTL_LINE_DOWN           2
+
+#define SPEEDTCH_ENDPOINT_INT          0x81
+#define SPEEDTCH_ENDPOINT_DATA         0x07
+#define SPEEDTCH_ENDPOINT_FIRMWARE     0x05
+
+#define hex2int(c) ( (c >= '0') && (c <= '9') ? (c - '0') : ((c & 0xf) + 9) )
+
+static struct usb_device_id speedtch_usb_ids[] = {
+       {USB_DEVICE(SPEEDTOUCH_VENDORID, SPEEDTOUCH_PRODUCTID)},
+       {}
+};
+
+MODULE_DEVICE_TABLE(usb, speedtch_usb_ids);
+
+struct speedtch_instance_data {
+       struct udsl_instance_data u;
+
+       /* Status */
+       struct urb *int_urb;
+       unsigned char int_data[16];
+       struct work_struct poll_work;
+       struct timer_list poll_timer;
+};
+/* USB */
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+                             const struct usb_device_id *id);
+static void speedtch_usb_disconnect(struct usb_interface *intf);
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+                             void *user_data);
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs);
+static void speedtch_poll_status(struct speedtch_instance_data *instance);
+
+static struct usb_driver speedtch_usb_driver = {
+       .owner          = THIS_MODULE,
+       .name           = speedtch_driver_name,
+       .probe          = speedtch_usb_probe,
+       .disconnect     = speedtch_usb_disconnect,
+       .ioctl          = speedtch_usb_ioctl,
+       .id_table       = speedtch_usb_ids,
+};
+
+/***************
+**  firmware  **
+***************/
+
+static void speedtch_got_firmware(struct speedtch_instance_data *instance,
+                                 int got_it)
+{
+       int err;
+       struct usb_interface *intf;
+
+       down(&instance->u.serialize);   /* vs self, speedtch_firmware_start */
+       if (instance->u.status == UDSL_LOADED_FIRMWARE)
+               goto out;
+       if (!got_it) {
+               instance->u.status = UDSL_NO_FIRMWARE;
+               goto out;
+       }
+       if ((err = usb_set_interface(instance->u.usb_dev, 1, 1)) < 0) {
+               dbg("speedtch_got_firmware: usb_set_interface returned %d!", err);
+               instance->u.status = UDSL_NO_FIRMWARE;
+               goto out;
+       }
+
+       /* Set up interrupt endpoint */
+       intf = usb_ifnum_to_if(instance->u.usb_dev, 0);
+       if (intf && !usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) {
+
+               instance->int_urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (instance->int_urb) {
+
+                       usb_fill_int_urb(instance->int_urb, instance->u.usb_dev,
+                                        usb_rcvintpipe(instance->u.usb_dev, SPEEDTCH_ENDPOINT_INT),
+                                        instance->int_data,
+                                        sizeof(instance->int_data),
+                                        speedtch_handle_int, instance, 50);
+                       err = usb_submit_urb(instance->int_urb, GFP_KERNEL);
+                       if (err) {
+                               /* Doesn't matter; we'll poll anyway */
+                               dbg("speedtch_got_firmware: Submission of interrupt URB failed %d", err);
+                               usb_free_urb(instance->int_urb);
+                               instance->int_urb = NULL;
+                               usb_driver_release_interface(&speedtch_usb_driver, intf);
+                       }
+               }
+       }
+       /* Start status polling */
+       mod_timer(&instance->poll_timer, jiffies + (1 * HZ));
+
+       instance->u.status = UDSL_LOADED_FIRMWARE;
+       tasklet_schedule(&instance->u.receive_tasklet);
+ out:
+       up(&instance->u.serialize);
+       wake_up_interruptible(&instance->u.firmware_waiters);
+}
+
+static int speedtch_set_swbuff(struct speedtch_instance_data *instance,
+                              int state)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       int ret;
+
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x32, 0x40, state ? 0x01 : 0x00,
+                             0x00, NULL, 0, 100);
+       if (ret < 0) {
+               printk("Warning: %sabling SW buffering: usb_control_msg returned %d\n",
+                    state ? "En" : "Dis", ret);
+               return ret;
+       }
+
+       dbg("speedtch_set_swbuff: %sbled SW buffering", state ? "En" : "Dis");
+       return 0;
+}
+
+static void speedtch_test_sequence(struct speedtch_instance_data *instance)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       unsigned char buf[10];
+       int ret;
+
+       /* URB 147 */
+       buf[0] = 0x1c;
+       buf[1] = 0x50;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x0b, 0x00, buf, 2, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB147: %d\n", __func__, ret);
+
+       /* URB 148 */
+       buf[0] = 0x32;
+       buf[1] = 0x00;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x02, 0x00, buf, 2, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB148: %d\n", __func__, ret);
+
+       /* URB 149 */
+       buf[0] = 0x01;
+       buf[1] = 0x00;
+       buf[2] = 0x01;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x03, 0x00, buf, 3, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB149: %d\n", __func__, ret);
+
+       /* URB 150 */
+       buf[0] = 0x01;
+       buf[1] = 0x00;
+       buf[2] = 0x01;
+       ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+                             0x01, 0x40, 0x04, 0x00, buf, 3, 100);
+       if (ret < 0)
+               printk(KERN_WARNING "%s failed on URB150: %d\n", __func__, ret);
+}
+
+static int speedtch_start_synchro(struct speedtch_instance_data *instance)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       unsigned char buf[2];
+       int ret;
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x04, 0x00,
+                             buf, sizeof(buf), CTRL_TIMEOUT);
+       if (ret < 0) {
+               printk(KERN_WARNING "SpeedTouch: Failed to start ADSL synchronisation: %d\n", ret);
+               return ret;
+       }
+
+       dbg("speedtch_start_synchro: modem prodded. %d Bytes returned: %02x %02x", ret, buf[0], buf[1]);
+       return 0;
+}
+
+static void speedtch_handle_int(struct urb *urb, struct pt_regs *regs)
+{
+       struct speedtch_instance_data *instance = urb->context;
+       unsigned int count = urb->actual_length;
+       int ret;
+
+       /* The magic interrupt for "up state" */
+       const static unsigned char up_int[6]   = { 0xa1, 0x00, 0x01, 0x00, 0x00, 0x00 };
+       /* The magic interrupt for "down state" */
+       const static unsigned char down_int[6] = { 0xa1, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+       switch (urb->status) {
+       case 0:
+               /* success */
+               break;
+       case -ECONNRESET:
+       case -ENOENT:
+       case -ESHUTDOWN:
+               /* this urb is terminated; clean up */
+               dbg("%s - urb shutting down with status: %d", __func__, urb->status);
+               return;
+       default:
+               dbg("%s - nonzero urb status received: %d", __func__, urb->status);
+               goto exit;
+       }
+
+       if (count < 6) {
+               dbg("%s - int packet too short", __func__);
+               goto exit;
+       }
+
+       if (!memcmp(up_int, instance->int_data, 6)) {
+               del_timer(&instance->poll_timer);
+               printk(KERN_NOTICE "DSL line goes up\n");
+       } else if (!memcmp(down_int, instance->int_data, 6)) {
+               printk(KERN_NOTICE "DSL line goes down\n");
+       } else {
+               int i;
+
+               printk(KERN_DEBUG "Unknown interrupt packet of %d bytes:", count);
+               for (i = 0; i < count; i++)
+                       printk(" %02x", instance->int_data[i]);
+               printk("\n");
+       }
+       schedule_work(&instance->poll_work);
+
+ exit:
+       rmb();
+       if (!instance->int_urb)
+               return;
+
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret)
+               err("%s - usb_submit_urb failed with result %d", __func__, ret);
+}
+
+static int speedtch_get_status(struct speedtch_instance_data *instance,
+                              unsigned char *buf)
+{
+       struct usb_device *dev = instance->u.usb_dev;
+       int ret;
+
+       memset(buf, 0, TOTAL);
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x07, 0x00, buf + OFFSET_7, SIZE_7,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG 7 failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x0b, 0x00, buf + OFFSET_b, SIZE_b,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG B failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x0d, 0x00, buf + OFFSET_d, SIZE_d,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG D failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x01, 0xc0, 0x0e, 0x00, buf + OFFSET_e, SIZE_e,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG E failed");
+               return ret;
+       }
+
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x01, 0xc0, 0x0f, 0x00, buf + OFFSET_f, SIZE_f,
+                             CTRL_TIMEOUT);
+       if (ret < 0) {
+               dbg("MSG F failed");
+               return ret;
+       }
+
+       return 0;
+}
+
+static void speedtch_poll_status(struct speedtch_instance_data *instance)
+{
+       unsigned char buf[TOTAL];
+       int ret;
+
+       ret = speedtch_get_status(instance, buf);
+       if (ret) {
+               printk(KERN_WARNING
+                      "SpeedTouch: Error %d fetching device status\n", ret);
+               return;
+       }
+
+       dbg("Line state %02x", buf[OFFSET_7]);
+
+       switch (buf[OFFSET_7]) {
+       case 0:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+                       printk(KERN_NOTICE "ADSL line is down\n");
+               }
+               break;
+
+       case 0x08:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+                       printk(KERN_NOTICE "ADSL line is blocked?\n");
+               }
+               break;
+
+       case 0x10:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_LOST) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+                       printk(KERN_NOTICE "ADSL line is synchronising\n");
+               }
+               break;
+
+       case 0x20:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_FOUND) {
+                       int down_speed = buf[OFFSET_b] | (buf[OFFSET_b + 1] << 8)
+                               | (buf[OFFSET_b + 2] << 16) | (buf[OFFSET_b + 3] << 24);
+                       int up_speed = buf[OFFSET_b + 4] | (buf[OFFSET_b + 5] << 8)
+                               | (buf[OFFSET_b + 6] << 16) | (buf[OFFSET_b + 7] << 24);
+
+                       if (!(down_speed & 0x0000ffff) &&
+                           !(up_speed & 0x0000ffff)) {
+                               down_speed >>= 16;
+                               up_speed >>= 16;
+                       }
+                       instance->u.atm_dev->link_rate = down_speed * 1000 / 424;
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+
+                       printk(KERN_NOTICE
+                              "ADSL line is up (%d Kib/s down | %d Kib/s up)\n",
+                              down_speed, up_speed);
+               }
+               break;
+
+       default:
+               if (instance->u.atm_dev->signal != ATM_PHY_SIG_UNKNOWN) {
+                       instance->u.atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+                       printk(KERN_NOTICE "Unknown line state %02x\n", buf[OFFSET_7]);
+               }
+               break;
+       }
+}
+
+static void speedtch_timer_poll(unsigned long data)
+{
+       struct speedtch_instance_data *instance = (void *)data;
+
+       schedule_work(&instance->poll_work);
+       mod_timer(&instance->poll_timer, jiffies + (5 * HZ));
+}
+
+#ifdef USE_FW_LOADER
+static void speedtch_upload_firmware(struct speedtch_instance_data *instance,
+                                    const struct firmware *fw1,
+                                    const struct firmware *fw2)
+{
+       unsigned char *buffer;
+       struct usb_device *usb_dev = instance->u.usb_dev;
+       struct usb_interface *intf;
+       int actual_length, ret;
+       int offset;
+
+       dbg("speedtch_upload_firmware");
+
+       if (!(intf = usb_ifnum_to_if(usb_dev, 2))) {
+               dbg("speedtch_upload_firmware: interface not found!");
+               goto fail;
+       }
+
+       if (!(buffer = (unsigned char *)__get_free_page(GFP_KERNEL))) {
+               dbg("speedtch_upload_firmware: no memory for buffer!");
+               goto fail;
+       }
+
+       /* A user-space firmware loader may already have claimed interface #2 */
+       if ((ret =
+            usb_driver_claim_interface(&speedtch_usb_driver, intf, NULL)) < 0) {
+               dbg("speedtch_upload_firmware: interface in use (%d)!", ret);
+               goto fail_free;
+       }
+
+       /* URB 7 */
+       if (dl_512_first) {     /* some modems need a read before writing the firmware */
+               ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                                  buffer, 0x200, &actual_length, 2 * HZ);
+
+               if (ret < 0 && ret != -ETIMEDOUT)
+                       dbg("speedtch_upload_firmware: read BLOCK0 from modem failed (%d)!", ret);
+               else
+                       dbg("speedtch_upload_firmware: BLOCK0 downloaded (%d bytes)", ret);
+       }
+
+       /* URB 8 : both leds are static green */
+       for (offset = 0; offset < fw1->size; offset += PAGE_SIZE) {
+               int thislen = min_t(int, PAGE_SIZE, fw1->size - offset);
+               memcpy(buffer, fw1->data + offset, thislen);
+
+               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+               if (ret < 0) {
+                       dbg("speedtch_upload_firmware: write BLOCK1 to modem failed (%d)!", ret);
+                       goto fail_release;
+               }
+               dbg("speedtch_upload_firmware: BLOCK1 uploaded (%d bytes)", fw1->size);
+       }
+
+       /* USB led blinking green, ADSL led off */
+
+       /* URB 11 */
+       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+       if (ret < 0) {
+               dbg("speedtch_upload_firmware: read BLOCK2 from modem failed (%d)!", ret);
+               goto fail_release;
+       }
+       dbg("speedtch_upload_firmware: BLOCK2 downloaded (%d bytes)", actual_length);
+
+       /* URBs 12 to 139 - USB led blinking green, ADSL led off */
+       for (offset = 0; offset < fw2->size; offset += PAGE_SIZE) {
+               int thislen = min_t(int, PAGE_SIZE, fw2->size - offset);
+               memcpy(buffer, fw2->data + offset, thislen);
+
+               ret = usb_bulk_msg(usb_dev, usb_sndbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                                  buffer, thislen, &actual_length, DATA_TIMEOUT);
+
+               if (ret < 0) {
+                       dbg("speedtch_upload_firmware: write BLOCK3 to modem failed (%d)!", ret);
+                       goto fail_release;
+               }
+       }
+       dbg("speedtch_upload_firmware: BLOCK3 uploaded (%d bytes)", fw2->size);
+
+       /* USB led static green, ADSL led static red */
+
+       /* URB 142 */
+       ret = usb_bulk_msg(usb_dev, usb_rcvbulkpipe(usb_dev, SPEEDTCH_ENDPOINT_FIRMWARE),
+                          buffer, 0x200, &actual_length, DATA_TIMEOUT);
+
+       if (ret < 0) {
+               dbg("speedtch_upload_firmware: read BLOCK4 from modem failed (%d)!", ret);
+               goto fail_release;
+       }
+
+       /* success */
+       dbg("speedtch_upload_firmware: BLOCK4 downloaded (%d bytes)", actual_length);
+
+       /* Delay to allow firmware to start up. We can do this here
+          because we're in our own kernel thread anyway. */
+       msleep(1000);
+
+       /* Enable software buffering, if requested */
+       if (sw_buffering)
+               speedtch_set_swbuff(instance, 1);
+
+       /* Magic spell; don't ask us what this does */
+       speedtch_test_sequence(instance);
+
+       /* Start modem synchronisation */
+       if (speedtch_start_synchro(instance))
+               dbg("speedtch_start_synchro: failed");
+
+       speedtch_got_firmware(instance, 1);
+
+       free_page((unsigned long)buffer);
+       return;
+
+ fail_release:
+       /* Only release interface #2 if uploading failed; we don't release it
+          we succeeded.  This prevents the userspace tools from trying to load
+          the firmware themselves */
+       usb_driver_release_interface(&speedtch_usb_driver, intf);
+ fail_free:
+       free_page((unsigned long)buffer);
+ fail:
+       speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_find_firmware(struct speedtch_instance_data
+                                 *instance, int phase,
+                                 const struct firmware **fw_p)
+{
+       char buf[24];
+       const u16 bcdDevice = instance->u.usb_dev->descriptor.bcdDevice;
+       const u8 major_revision = bcdDevice >> 8;
+       const u8 minor_revision = bcdDevice & 0xff;
+
+       sprintf(buf, "speedtch-%d.bin.%x.%02x", phase, major_revision, minor_revision);
+       dbg("speedtch_find_firmware: looking for %s", buf);
+
+       if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+               sprintf(buf, "speedtch-%d.bin.%x", phase, major_revision);
+               dbg("speedtch_find_firmware: looking for %s", buf);
+
+               if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+                       sprintf(buf, "speedtch-%d.bin", phase);
+                       dbg("speedtch_find_firmware: looking for %s", buf);
+
+                       if (request_firmware(fw_p, buf, &instance->u.usb_dev->dev)) {
+                               dev_warn(&instance->u.usb_dev->dev, "no stage %d firmware found!", phase);
+                               return -ENOENT;
+                       }
+               }
+       }
+
+       dev_info(&instance->u.usb_dev->dev, "found stage %d firmware %s\n", phase, buf);
+
+       return 0;
+}
+
+static int speedtch_load_firmware(void *arg)
+{
+       const struct firmware *fw1, *fw2;
+       struct speedtch_instance_data *instance = arg;
+
+       BUG_ON(!instance);
+
+       daemonize("firmware/speedtch");
+
+       if (!speedtch_find_firmware(instance, 1, &fw1)) {
+               if (!speedtch_find_firmware(instance, 2, &fw2)) {
+                       speedtch_upload_firmware(instance, fw1, fw2);
+                       release_firmware(fw2);
+               }
+               release_firmware(fw1);
+       }
+
+       /* In case we failed, set state back to NO_FIRMWARE so that
+          another later attempt may work. Otherwise, we never actually
+          manage to recover if, for example, the firmware is on /usr and
+          we look for it too early. */
+       speedtch_got_firmware(instance, 0);
+
+       module_put(THIS_MODULE);
+       udsl_put_instance(&instance->u);
+       return 0;
+}
+#endif /* USE_FW_LOADER */
+
+static void speedtch_firmware_start(struct speedtch_instance_data *instance)
+{
+#ifdef USE_FW_LOADER
+       int ret;
+#endif
+
+       dbg("speedtch_firmware_start");
+
+       down(&instance->u.serialize);   /* vs self, speedtch_got_firmware */
+
+       if (instance->u.status >= UDSL_LOADING_FIRMWARE) {
+               up(&instance->u.serialize);
+               return;
+       }
+
+       instance->u.status = UDSL_LOADING_FIRMWARE;
+       up(&instance->u.serialize);
+
+#ifdef USE_FW_LOADER
+       udsl_get_instance(&instance->u);
+       try_module_get(THIS_MODULE);
+
+       ret = kernel_thread(speedtch_load_firmware, instance,
+                           CLONE_FS | CLONE_FILES);
+
+       if (ret >= 0)
+               return;         /* OK */
+
+       dbg("speedtch_firmware_start: kernel_thread failed (%d)!", ret);
+
+       module_put(THIS_MODULE);
+       udsl_put_instance(&instance->u);
+       /* Just pretend it never happened... hope modem_run happens */
+#endif                         /* USE_FW_LOADER */
+
+       speedtch_got_firmware(instance, 0);
+}
+
+static int speedtch_firmware_wait(struct udsl_instance_data *instance)
+{
+       speedtch_firmware_start((void *)instance);
+
+       if (wait_event_interruptible(instance->firmware_waiters, instance->status != UDSL_LOADING_FIRMWARE) < 0)
+               return -ERESTARTSYS;
+
+       return (instance->status == UDSL_LOADED_FIRMWARE) ? 0 : -EAGAIN;
+}
+
+/**********
+**  USB  **
+**********/
+
+static int speedtch_usb_ioctl(struct usb_interface *intf, unsigned int code,
+                             void *user_data)
+{
+       struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+       dbg("speedtch_usb_ioctl entered");
+
+       if (!instance) {
+               dbg("speedtch_usb_ioctl: NULL instance!");
+               return -ENODEV;
+       }
+
+       switch (code) {
+       case UDSL_IOCTL_LINE_UP:
+               instance->u.atm_dev->signal = ATM_PHY_SIG_FOUND;
+               speedtch_got_firmware(instance, 1);
+               return (instance->u.status == UDSL_LOADED_FIRMWARE) ? 0 : -EIO;
+       case UDSL_IOCTL_LINE_DOWN:
+               instance->u.atm_dev->signal = ATM_PHY_SIG_LOST;
+               return 0;
+       default:
+               return -ENOTTY;
+       }
+}
+
+static int speedtch_usb_probe(struct usb_interface *intf,
+                             const struct usb_device_id *id)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       int ifnum = intf->altsetting->desc.bInterfaceNumber;
+       struct speedtch_instance_data *instance;
+       unsigned char mac_str[13];
+       int ret, i;
+       char buf7[SIZE_7];
+
+       dbg("speedtch_usb_probe: trying device with vendor=0x%x, product=0x%x, ifnum %d", dev->descriptor.idVendor, dev->descriptor.idProduct, ifnum);
+
+       if ((dev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC) ||
+           (dev->descriptor.idVendor != SPEEDTOUCH_VENDORID) ||
+           (dev->descriptor.idProduct != SPEEDTOUCH_PRODUCTID) || (ifnum != 1))
+               return -ENODEV;
+
+       dbg("speedtch_usb_probe: device accepted");
+
+       /* instance init */
+       instance = kmalloc(sizeof(*instance), GFP_KERNEL);
+       if (!instance) {
+               dbg("speedtch_usb_probe: no memory for instance data!");
+               return -ENOMEM;
+       }
+
+       memset(instance, 0, sizeof(struct speedtch_instance_data));
+
+       if ((ret = usb_set_interface(dev, 0, 0)) < 0)
+               goto fail;
+
+       if ((ret = usb_set_interface(dev, 2, 0)) < 0)
+               goto fail;
+
+       instance->u.data_endpoint = SPEEDTCH_ENDPOINT_DATA;
+       instance->u.firmware_wait = speedtch_firmware_wait;
+       instance->u.driver_name = speedtch_driver_name;
+
+       ret = udsl_instance_setup(dev, &instance->u);
+       if (ret)
+               goto fail;
+
+       init_timer(&instance->poll_timer);
+       instance->poll_timer.function = speedtch_timer_poll;
+       instance->poll_timer.data = (unsigned long)instance;
+
+       INIT_WORK(&instance->poll_work, (void *)speedtch_poll_status, instance);
+
+       /* set MAC address, it is stored in the serial number */
+       memset(instance->u.atm_dev->esi, 0, sizeof(instance->u.atm_dev->esi));
+       if (usb_string(dev, dev->descriptor.iSerialNumber, mac_str, sizeof(mac_str)) == 12) {
+               for (i = 0; i < 6; i++)
+                       instance->u.atm_dev->esi[i] =
+                               (hex2int(mac_str[i * 2]) * 16) + (hex2int(mac_str[i * 2 + 1]));
+       }
+
+       /* First check whether the modem already seems to be alive */
+       ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+                             0x12, 0xc0, 0x07, 0x00, buf7, SIZE_7, HZ / 2);
+
+       if (ret == SIZE_7) {
+               dbg("firmware appears to be already loaded");
+               speedtch_got_firmware(instance, 1);
+               speedtch_poll_status(instance);
+       } else {
+               speedtch_firmware_start(instance);
+       }
+
+       usb_set_intfdata(intf, instance);
+
+       return 0;
+
+ fail:
+       kfree(instance);
+
+       return -ENOMEM;
+}
+
+static void speedtch_usb_disconnect(struct usb_interface *intf)
+{
+       struct speedtch_instance_data *instance = usb_get_intfdata(intf);
+
+       dbg("speedtch_usb_disconnect entered");
+
+       if (!instance) {
+               dbg("speedtch_usb_disconnect: NULL instance!");
+               return;
+       }
+
+       if (instance->int_urb) {
+               struct urb *int_urb = instance->int_urb;
+               instance->int_urb = NULL;
+               wmb();
+               usb_unlink_urb(int_urb);
+               usb_free_urb(int_urb);
+       }
+
+       instance->int_data[0] = 1;
+       del_timer_sync(&instance->poll_timer);
+       wmb();
+       flush_scheduled_work();
+
+       udsl_instance_disconnect(&instance->u);
+
+       /* clean up */
+       usb_set_intfdata(intf, NULL);
+       udsl_put_instance(&instance->u);
+}
+
+/***********
+**  init  **
+***********/
+
+static int __init speedtch_usb_init(void)
+{
+       dbg("speedtch_usb_init: driver version " DRIVER_VERSION);
+
+       return usb_register(&speedtch_usb_driver);
+}
+
+static void __exit speedtch_usb_cleanup(void)
+{
+       dbg("speedtch_usb_cleanup entered");
+
+       usb_deregister(&speedtch_usb_driver);
+}
+
+module_init(speedtch_usb_init);
+module_exit(speedtch_usb_cleanup);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
diff --git a/drivers/usb/atm/usb_atm.c b/drivers/usb/atm/usb_atm.c
new file mode 100644 (file)
index 0000000..9180dda
--- /dev/null
@@ -0,0 +1,1201 @@
+/******************************************************************************
+ *  usb_atm.c - Generic USB xDSL driver core
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ *
+ *  You should have received a copy of the GNU General Public License along with
+ *  this program; if not, write to the Free Software Foundation, Inc., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+/*
+ *  Written by Johan Verrept, maintained by Duncan Sands (duncan.sands@free.fr)
+ *
+ *  1.7+:      - See the check-in logs
+ *
+ *  1.6:       - No longer opens a connection if the firmware is not loaded
+ *             - Added support for the speedtouch 330
+ *             - Removed the limit on the number of devices
+ *             - Module now autoloads on device plugin
+ *             - Merged relevant parts of sarlib
+ *             - Replaced the kernel thread with a tasklet
+ *             - New packet transmission code
+ *             - Changed proc file contents
+ *             - Fixed all known SMP races
+ *             - Many fixes and cleanups
+ *             - Various fixes by Oliver Neukum (oliver@neukum.name)
+ *
+ *  1.5A:      - Version for inclusion in 2.5 series kernel
+ *             - Modifications by Richard Purdie (rpurdie@rpsys.net)
+ *             - made compatible with kernel 2.5.6 onwards by changing
+ *             udsl_usb_send_data_context->urb to a pointer and adding code
+ *             to alloc and free it
+ *             - remove_wait_queue() added to udsl_atm_processqueue_thread()
+ *
+ *  1.5:       - fixed memory leak when atmsar_decode_aal5 returned NULL.
+ *             (reported by stephen.robinson@zen.co.uk)
+ *
+ *  1.4:       - changed the spin_lock() under interrupt to spin_lock_irqsave()
+ *             - unlink all active send urbs of a vcc that is being closed.
+ *
+ *  1.3.1:     - added the version number
+ *
+ *  1.3:       - Added multiple send urb support
+ *             - fixed memory leak and vcc->tx_inuse starvation bug
+ *               when not enough memory left in vcc.
+ *
+ *  1.2:       - Fixed race condition in udsl_usb_send_data()
+ *  1.1:       - Turned off packet debugging
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/proc_fs.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <asm/uaccess.h>
+#include <linux/smp_lock.h>
+#include <linux/interrupt.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <linux/crc32.h>
+#include <linux/init.h>
+#include <linux/firmware.h>
+
+#include "usb_atm.h"
+
+/*
+#define DEBUG
+#define VERBOSE_DEBUG
+*/
+
+#if !defined (DEBUG) && defined (CONFIG_USB_DEBUG)
+#      define DEBUG
+#endif
+
+#include <linux/usb.h>
+
+#ifdef DEBUG
+#define UDSL_ASSERT(x) BUG_ON(!(x))
+#else
+#define UDSL_ASSERT(x) do { if (!(x)) warn("failed assertion '" #x "' at line %d", __LINE__); } while(0)
+#endif
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len);
+#define PACKETDEBUG(arg...)    udsl_print_packet (arg)
+#define vdbg(arg...)           dbg (arg)
+#else
+#define PACKETDEBUG(arg...)
+#define vdbg(arg...)
+#endif
+
+#define DRIVER_AUTHOR  "Johan Verrept, Duncan Sands <duncan.sands@free.fr>"
+#define DRIVER_VERSION "1.8"
+#define DRIVER_DESC    "Alcatel SpeedTouch USB driver version " DRIVER_VERSION
+
+static unsigned int num_rcv_urbs = UDSL_DEFAULT_RCV_URBS;
+static unsigned int num_snd_urbs = UDSL_DEFAULT_SND_URBS;
+static unsigned int num_rcv_bufs = UDSL_DEFAULT_RCV_BUFS;
+static unsigned int num_snd_bufs = UDSL_DEFAULT_SND_BUFS;
+static unsigned int rcv_buf_size = UDSL_DEFAULT_RCV_BUF_SIZE;
+static unsigned int snd_buf_size = UDSL_DEFAULT_SND_BUF_SIZE;
+
+module_param(num_rcv_urbs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_urbs,
+                "Number of urbs used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_URBS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_URBS) ")");
+
+module_param(num_snd_urbs, uint, 0444);
+MODULE_PARM_DESC(num_snd_urbs,
+                "Number of urbs used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_URBS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_URBS) ")");
+
+module_param(num_rcv_bufs, uint, 0444);
+MODULE_PARM_DESC(num_rcv_bufs,
+                "Number of buffers used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_BUFS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_BUFS) ")");
+
+module_param(num_snd_bufs, uint, 0444);
+MODULE_PARM_DESC(num_snd_bufs,
+                "Number of buffers used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_BUFS) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_BUFS) ")");
+
+module_param(rcv_buf_size, uint, 0444);
+MODULE_PARM_DESC(rcv_buf_size,
+                "Size of the buffers used for reception (range: 0-"
+                __MODULE_STRING(UDSL_MAX_RCV_BUF_SIZE) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_RCV_BUF_SIZE) ")");
+
+module_param(snd_buf_size, uint, 0444);
+MODULE_PARM_DESC(snd_buf_size,
+                "Size of the buffers used for transmission (range: 0-"
+                __MODULE_STRING(UDSL_MAX_SND_BUF_SIZE) ", default: "
+                __MODULE_STRING(UDSL_DEFAULT_SND_BUF_SIZE) ")");
+
+/* ATM */
+
+static void udsl_atm_dev_close(struct atm_dev *dev);
+static int udsl_atm_open(struct atm_vcc *vcc);
+static void udsl_atm_close(struct atm_vcc *vcc);
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd, void __user * arg);
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb);
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page);
+
+static struct atmdev_ops udsl_atm_devops = {
+       .dev_close      = udsl_atm_dev_close,
+       .open           = udsl_atm_open,
+       .close          = udsl_atm_close,
+       .ioctl          = udsl_atm_ioctl,
+       .send           = udsl_atm_send,
+       .proc_read      = udsl_atm_proc_read,
+       .owner          = THIS_MODULE,
+};
+
+/***********
+**  misc  **
+***********/
+
+static inline void udsl_pop(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       if (vcc->pop)
+               vcc->pop(vcc, skb);
+       else
+               dev_kfree_skb(skb);
+}
+
+/*************
+**  decode  **
+*************/
+
+static inline struct udsl_vcc_data *udsl_find_vcc(struct udsl_instance_data *instance,
+                                                 short vpi, int vci)
+{
+       struct udsl_vcc_data *vcc;
+
+       list_for_each_entry(vcc, &instance->vcc_list, list)
+               if ((vcc->vci == vci) && (vcc->vpi == vpi))
+                       return vcc;
+       return NULL;
+}
+
+static void udsl_extract_cells(struct udsl_instance_data *instance,
+                              unsigned char *source, unsigned int howmany)
+{
+       struct udsl_vcc_data *cached_vcc = NULL;
+       struct atm_vcc *vcc;
+       struct sk_buff *sarb;
+       struct udsl_vcc_data *vcc_data;
+       int cached_vci = 0;
+       unsigned int i;
+       int pti;
+       int vci;
+       short cached_vpi = 0;
+       short vpi;
+
+       for (i = 0; i < howmany;
+            i++, source += ATM_CELL_SIZE + instance->rcv_padding) {
+               vpi = ((source[0] & 0x0f) << 4) | (source[1] >> 4);
+               vci = ((source[1] & 0x0f) << 12) | (source[2] << 4) | (source[3] >> 4);
+               pti = (source[3] & 0x2) != 0;
+
+               vdbg("udsl_extract_cells: vpi %hd, vci %d, pti %d", vpi, vci, pti);
+
+               if (cached_vcc && (vci == cached_vci) && (vpi == cached_vpi))
+                       vcc_data = cached_vcc;
+               else if ((vcc_data = udsl_find_vcc(instance, vpi, vci))) {
+                       cached_vcc = vcc_data;
+                       cached_vpi = vpi;
+                       cached_vci = vci;
+               } else {
+                       dbg("udsl_extract_cells: unknown vpi/vci (%hd/%d)!", vpi, vci);
+                       continue;
+               }
+
+               vcc = vcc_data->vcc;
+               sarb = vcc_data->sarb;
+
+               if (sarb->tail + ATM_CELL_PAYLOAD > sarb->end) {
+                       dbg("udsl_extract_cells: buffer overrun (sarb->len %u, vcc: 0x%p)!", sarb->len, vcc);
+                       /* discard cells already received */
+                       skb_trim(sarb, 0);
+               }
+
+               memcpy(sarb->tail, source + ATM_CELL_HEADER, ATM_CELL_PAYLOAD);
+               __skb_put(sarb, ATM_CELL_PAYLOAD);
+
+               if (pti) {
+                       struct sk_buff *skb;
+                       unsigned int length;
+                       unsigned int pdu_length;
+
+                       length = (source[ATM_CELL_SIZE - 6] << 8) + source[ATM_CELL_SIZE - 5];
+
+                       /* guard against overflow */
+                       if (length > ATM_MAX_AAL5_PDU) {
+                               dbg("udsl_extract_cells: bogus length %u (vcc: 0x%p)!", length, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       pdu_length = UDSL_NUM_CELLS(length) * ATM_CELL_PAYLOAD;
+
+                       if (sarb->len < pdu_length) {
+                               dbg("udsl_extract_cells: bogus pdu_length %u (sarb->len: %u, vcc: 0x%p)!", pdu_length, sarb->len, vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       if (crc32_be(~0, sarb->tail - pdu_length, pdu_length) != 0xc704dd7b) {
+                               dbg("udsl_extract_cells: packet failed crc check (vcc: 0x%p)!", vcc);
+                               atomic_inc(&vcc->stats->rx_err);
+                               goto out;
+                       }
+
+                       vdbg("udsl_extract_cells: got packet (length: %u, pdu_length: %u, vcc: 0x%p)", length, pdu_length, vcc);
+
+                       if (!(skb = dev_alloc_skb(length))) {
+                               dbg("udsl_extract_cells: no memory for skb (length: %u)!", length);
+                               atomic_inc(&vcc->stats->rx_drop);
+                               goto out;
+                       }
+
+                       vdbg("udsl_extract_cells: allocated new sk_buff (skb: 0x%p, skb->truesize: %u)", skb, skb->truesize);
+
+                       if (!atm_charge(vcc, skb->truesize)) {
+                               dbg("udsl_extract_cells: failed atm_charge (skb->truesize: %u)!", skb->truesize);
+                               dev_kfree_skb(skb);
+                               goto out;       /* atm_charge increments rx_drop */
+                       }
+
+                       memcpy(skb->data, sarb->tail - pdu_length, length);
+                       __skb_put(skb, length);
+
+                       vdbg("udsl_extract_cells: sending skb 0x%p, skb->len %u, skb->truesize %u", skb, skb->len, skb->truesize);
+
+                       PACKETDEBUG(skb->data, skb->len);
+
+                       vcc->push(vcc, skb);
+
+                       atomic_inc(&vcc->stats->rx);
+               out:
+                       skb_trim(sarb, 0);
+               }
+       }
+}
+
+/*************
+**  encode  **
+*************/
+
+static const unsigned char zeros[ATM_CELL_PAYLOAD];
+
+static void udsl_groom_skb(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       struct udsl_control *ctrl = UDSL_SKB(skb);
+       unsigned int zero_padding;
+       u32 crc;
+
+       ctrl->atm_data.vcc = vcc;
+       ctrl->cell_header[0] = vcc->vpi >> 4;
+       ctrl->cell_header[1] = (vcc->vpi << 4) | (vcc->vci >> 12);
+       ctrl->cell_header[2] = vcc->vci >> 4;
+       ctrl->cell_header[3] = vcc->vci << 4;
+       ctrl->cell_header[4] = 0xec;
+
+       ctrl->num_cells = UDSL_NUM_CELLS(skb->len);
+       ctrl->num_entire = skb->len / ATM_CELL_PAYLOAD;
+
+       zero_padding = ctrl->num_cells * ATM_CELL_PAYLOAD - skb->len - ATM_AAL5_TRAILER;
+
+       if (ctrl->num_entire + 1 < ctrl->num_cells)
+               ctrl->pdu_padding = zero_padding - (ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+       else
+               ctrl->pdu_padding = zero_padding;
+
+       ctrl->aal5_trailer[0] = 0;      /* UU = 0 */
+       ctrl->aal5_trailer[1] = 0;      /* CPI = 0 */
+       ctrl->aal5_trailer[2] = skb->len >> 8;
+       ctrl->aal5_trailer[3] = skb->len;
+
+       crc = crc32_be(~0, skb->data, skb->len);
+       crc = crc32_be(crc, zeros, zero_padding);
+       crc = crc32_be(crc, ctrl->aal5_trailer, 4);
+       crc = ~crc;
+
+       ctrl->aal5_trailer[4] = crc >> 24;
+       ctrl->aal5_trailer[5] = crc >> 16;
+       ctrl->aal5_trailer[6] = crc >> 8;
+       ctrl->aal5_trailer[7] = crc;
+}
+
+static unsigned int udsl_write_cells(struct udsl_instance_data *instance,
+                                    unsigned int howmany, struct sk_buff *skb,
+                                    unsigned char **target_p)
+{
+       struct udsl_control *ctrl = UDSL_SKB(skb);
+       unsigned char *target = *target_p;
+       unsigned int nc, ne, i;
+
+       vdbg("udsl_write_cells: howmany=%u, skb->len=%d, num_cells=%u, num_entire=%u, pdu_padding=%u", howmany, skb->len, ctrl->num_cells, ctrl->num_entire, ctrl->pdu_padding);
+
+       nc = ctrl->num_cells;
+       ne = min(howmany, ctrl->num_entire);
+
+       for (i = 0; i < ne; i++) {
+               memcpy(target, ctrl->cell_header, ATM_CELL_HEADER);
+               target += ATM_CELL_HEADER;
+               memcpy(target, skb->data, ATM_CELL_PAYLOAD);
+               target += ATM_CELL_PAYLOAD;
+               if (instance->snd_padding) {
+                       memset(target, 0, instance->snd_padding);
+                       target += instance->snd_padding;
+               }
+               __skb_pull(skb, ATM_CELL_PAYLOAD);
+       }
+
+       ctrl->num_entire -= ne;
+
+       if (!(ctrl->num_cells -= ne) || !(howmany -= ne))
+               goto out;
+
+       if (instance->snd_padding) {
+               memset(target, 0, instance->snd_padding);
+               target += instance->snd_padding;
+       }
+       memcpy(target, ctrl->cell_header, ATM_CELL_HEADER);
+       target += ATM_CELL_HEADER;
+       memcpy(target, skb->data, skb->len);
+       target += skb->len;
+       __skb_pull(skb, skb->len);
+       memset(target, 0, ctrl->pdu_padding);
+       target += ctrl->pdu_padding;
+
+       if (--ctrl->num_cells) {
+               if (!--howmany) {
+                       ctrl->pdu_padding = ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+                       goto out;
+               }
+
+               memcpy(target, ctrl->cell_header, ATM_CELL_HEADER);
+               target += ATM_CELL_HEADER;
+               memset(target, 0, ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER);
+               target += ATM_CELL_PAYLOAD - ATM_AAL5_TRAILER;
+
+               --ctrl->num_cells;
+               UDSL_ASSERT(!ctrl->num_cells);
+       }
+
+       memcpy(target, ctrl->aal5_trailer, ATM_AAL5_TRAILER);
+       target += ATM_AAL5_TRAILER;
+       /* set pti bit in last cell */
+       *(target + 3 - ATM_CELL_SIZE) |= 0x2;
+       if (instance->snd_padding) {
+               memset(target, 0, instance->snd_padding);
+               target += instance->snd_padding;
+       }
+ out:
+       *target_p = target;
+       return nc - ctrl->num_cells;
+}
+
+/**************
+**  receive  **
+**************/
+
+static void udsl_complete_receive(struct urb *urb, struct pt_regs *regs)
+{
+       struct udsl_receive_buffer *buf;
+       struct udsl_instance_data *instance;
+       struct udsl_receiver *rcv;
+       unsigned long flags;
+
+       if (!urb || !(rcv = urb->context)) {
+               dbg("udsl_complete_receive: bad urb!");
+               return;
+       }
+
+       instance = rcv->instance;
+       buf = rcv->buffer;
+
+       buf->filled_cells = urb->actual_length / (ATM_CELL_SIZE + instance->rcv_padding);
+
+       vdbg("udsl_complete_receive: urb 0x%p, status %d, actual_length %d, filled_cells %u, rcv 0x%p, buf 0x%p", urb, urb->status, urb->actual_length, buf->filled_cells, rcv, buf);
+
+       UDSL_ASSERT(buf->filled_cells <= rcv_buf_size);
+
+       /* may not be in_interrupt() */
+       spin_lock_irqsave(&instance->receive_lock, flags);
+       list_add(&rcv->list, &instance->spare_receivers);
+       list_add_tail(&buf->list, &instance->filled_receive_buffers);
+       if (likely(!urb->status))
+               tasklet_schedule(&instance->receive_tasklet);
+       spin_unlock_irqrestore(&instance->receive_lock, flags);
+}
+
+static void udsl_process_receive(unsigned long data)
+{
+       struct udsl_receive_buffer *buf;
+       struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+       struct udsl_receiver *rcv;
+       int err;
+
+ made_progress:
+       while (!list_empty(&instance->spare_receive_buffers)) {
+               spin_lock_irq(&instance->receive_lock);
+               if (list_empty(&instance->spare_receivers)) {
+                       spin_unlock_irq(&instance->receive_lock);
+                       break;
+               }
+               rcv = list_entry(instance->spare_receivers.next,
+                                struct udsl_receiver, list);
+               list_del(&rcv->list);
+               spin_unlock_irq(&instance->receive_lock);
+
+               buf = list_entry(instance->spare_receive_buffers.next,
+                                struct udsl_receive_buffer, list);
+               list_del(&buf->list);
+
+               rcv->buffer = buf;
+
+               usb_fill_bulk_urb(rcv->urb, instance->usb_dev,
+                                 usb_rcvbulkpipe(instance->usb_dev, instance->data_endpoint),
+                                 buf->base,
+                                 rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+                                 udsl_complete_receive, rcv);
+
+               vdbg("udsl_process_receive: sending urb 0x%p, rcv 0x%p, buf 0x%p",
+                    rcv->urb, rcv, buf);
+
+               if ((err = usb_submit_urb(rcv->urb, GFP_ATOMIC)) < 0) {
+                       dbg("udsl_process_receive: urb submission failed (%d)!", err);
+                       list_add(&buf->list, &instance->spare_receive_buffers);
+                       spin_lock_irq(&instance->receive_lock);
+                       list_add(&rcv->list, &instance->spare_receivers);
+                       spin_unlock_irq(&instance->receive_lock);
+                       break;
+               }
+       }
+
+       spin_lock_irq(&instance->receive_lock);
+       if (list_empty(&instance->filled_receive_buffers)) {
+               spin_unlock_irq(&instance->receive_lock);
+               return;         /* done - no more buffers */
+       }
+       buf = list_entry(instance->filled_receive_buffers.next,
+                        struct udsl_receive_buffer, list);
+       list_del(&buf->list);
+       spin_unlock_irq(&instance->receive_lock);
+
+       vdbg("udsl_process_receive: processing buf 0x%p", buf);
+       udsl_extract_cells(instance, buf->base, buf->filled_cells);
+       list_add(&buf->list, &instance->spare_receive_buffers);
+       goto made_progress;
+}
+
+/***********
+**  send  **
+***********/
+
+static void udsl_complete_send(struct urb *urb, struct pt_regs *regs)
+{
+       struct udsl_instance_data *instance;
+       struct udsl_sender *snd;
+       unsigned long flags;
+
+       if (!urb || !(snd = urb->context) || !(instance = snd->instance)) {
+               dbg("udsl_complete_send: bad urb!");
+               return;
+       }
+
+       vdbg("udsl_complete_send: urb 0x%p, status %d, snd 0x%p, buf 0x%p", urb,
+            urb->status, snd, snd->buffer);
+
+       /* may not be in_interrupt() */
+       spin_lock_irqsave(&instance->send_lock, flags);
+       list_add(&snd->list, &instance->spare_senders);
+       list_add(&snd->buffer->list, &instance->spare_send_buffers);
+       tasklet_schedule(&instance->send_tasklet);
+       spin_unlock_irqrestore(&instance->send_lock, flags);
+}
+
+static void udsl_process_send(unsigned long data)
+{
+       struct udsl_send_buffer *buf;
+       struct udsl_instance_data *instance = (struct udsl_instance_data *)data;
+       struct sk_buff *skb;
+       struct udsl_sender *snd;
+       int err;
+       unsigned int num_written;
+
+ made_progress:
+       spin_lock_irq(&instance->send_lock);
+       while (!list_empty(&instance->spare_senders)) {
+               if (!list_empty(&instance->filled_send_buffers)) {
+                       buf = list_entry(instance->filled_send_buffers.next,
+                                        struct udsl_send_buffer, list);
+                       list_del(&buf->list);
+               } else if ((buf = instance->current_buffer)) {
+                       instance->current_buffer = NULL;
+               } else          /* all buffers empty */
+                       break;
+
+               snd = list_entry(instance->spare_senders.next,
+                                struct udsl_sender, list);
+               list_del(&snd->list);
+               spin_unlock_irq(&instance->send_lock);
+
+               snd->buffer = buf;
+               usb_fill_bulk_urb(snd->urb, instance->usb_dev,
+                                 usb_sndbulkpipe(instance->usb_dev, instance->data_endpoint),
+                                 buf->base,
+                                 (snd_buf_size - buf->free_cells) * (ATM_CELL_SIZE + instance->snd_padding),
+                                 udsl_complete_send, snd);
+
+               vdbg("udsl_process_send: submitting urb 0x%p (%d cells), snd 0x%p, buf 0x%p",
+                    snd->urb, snd_buf_size - buf->free_cells, snd, buf);
+
+               if ((err = usb_submit_urb(snd->urb, GFP_ATOMIC)) < 0) {
+                       dbg("udsl_process_send: urb submission failed (%d)!", err);
+                       spin_lock_irq(&instance->send_lock);
+                       list_add(&snd->list, &instance->spare_senders);
+                       spin_unlock_irq(&instance->send_lock);
+                       list_add(&buf->list, &instance->filled_send_buffers);
+                       return; /* bail out */
+               }
+
+               spin_lock_irq(&instance->send_lock);
+       }                       /* while */
+       spin_unlock_irq(&instance->send_lock);
+
+       if (!instance->current_skb)
+               instance->current_skb = skb_dequeue(&instance->sndqueue);
+       if (!instance->current_skb)
+               return;         /* done - no more skbs */
+
+       skb = instance->current_skb;
+
+       if (!(buf = instance->current_buffer)) {
+               spin_lock_irq(&instance->send_lock);
+               if (list_empty(&instance->spare_send_buffers)) {
+                       instance->current_buffer = NULL;
+                       spin_unlock_irq(&instance->send_lock);
+                       return; /* done - no more buffers */
+               }
+               buf = list_entry(instance->spare_send_buffers.next,
+                              struct udsl_send_buffer, list);
+               list_del(&buf->list);
+               spin_unlock_irq(&instance->send_lock);
+
+               buf->free_start = buf->base;
+               buf->free_cells = snd_buf_size;
+
+               instance->current_buffer = buf;
+       }
+
+       num_written = udsl_write_cells(instance, buf->free_cells, skb, &buf->free_start);
+
+       vdbg("udsl_process_send: wrote %u cells from skb 0x%p to buffer 0x%p",
+            num_written, skb, buf);
+
+       if (!(buf->free_cells -= num_written)) {
+               list_add_tail(&buf->list, &instance->filled_send_buffers);
+               instance->current_buffer = NULL;
+       }
+
+       vdbg("udsl_process_send: buffer contains %d cells, %d left",
+            snd_buf_size - buf->free_cells, buf->free_cells);
+
+       if (!UDSL_SKB(skb)->num_cells) {
+               struct atm_vcc *vcc = UDSL_SKB(skb)->atm_data.vcc;
+
+               udsl_pop(vcc, skb);
+               instance->current_skb = NULL;
+
+               atomic_inc(&vcc->stats->tx);
+       }
+
+       goto made_progress;
+}
+
+static void udsl_cancel_send(struct udsl_instance_data *instance,
+                            struct atm_vcc *vcc)
+{
+       struct sk_buff *skb, *n;
+
+       dbg("udsl_cancel_send entered");
+       spin_lock_irq(&instance->sndqueue.lock);
+       for (skb = instance->sndqueue.next, n = skb->next;
+            skb != (struct sk_buff *)&instance->sndqueue;
+            skb = n, n = skb->next)
+               if (UDSL_SKB(skb)->atm_data.vcc == vcc) {
+                       dbg("udsl_cancel_send: popping skb 0x%p", skb);
+                       __skb_unlink(skb, &instance->sndqueue);
+                       udsl_pop(vcc, skb);
+               }
+       spin_unlock_irq(&instance->sndqueue.lock);
+
+       tasklet_disable(&instance->send_tasklet);
+       if ((skb = instance->current_skb) && (UDSL_SKB(skb)->atm_data.vcc == vcc)) {
+               dbg("udsl_cancel_send: popping current skb (0x%p)", skb);
+               instance->current_skb = NULL;
+               udsl_pop(vcc, skb);
+       }
+       tasklet_enable(&instance->send_tasklet);
+       dbg("udsl_cancel_send done");
+}
+
+static int udsl_atm_send(struct atm_vcc *vcc, struct sk_buff *skb)
+{
+       struct udsl_instance_data *instance = vcc->dev->dev_data;
+       int err;
+
+       vdbg("udsl_atm_send called (skb 0x%p, len %u)", skb, skb->len);
+
+       if (!instance) {
+               dbg("udsl_atm_send: NULL data!");
+               err = -ENODEV;
+               goto fail;
+       }
+
+       if (vcc->qos.aal != ATM_AAL5) {
+               dbg("udsl_atm_send: unsupported ATM type %d!", vcc->qos.aal);
+               err = -EINVAL;
+               goto fail;
+       }
+
+       if (skb->len > ATM_MAX_AAL5_PDU) {
+               dbg("udsl_atm_send: packet too long (%d vs %d)!", skb->len,
+                   ATM_MAX_AAL5_PDU);
+               err = -EINVAL;
+               goto fail;
+       }
+
+       PACKETDEBUG(skb->data, skb->len);
+
+       udsl_groom_skb(vcc, skb);
+       skb_queue_tail(&instance->sndqueue, skb);
+       tasklet_schedule(&instance->send_tasklet);
+
+       return 0;
+
+ fail:
+       udsl_pop(vcc, skb);
+       return err;
+}
+
+/********************
+**  bean counting  **
+********************/
+
+static void udsl_destroy_instance(struct kref *kref)
+{
+       struct udsl_instance_data *instance =
+           container_of(kref, struct udsl_instance_data, refcount);
+
+       tasklet_kill(&instance->receive_tasklet);
+       tasklet_kill(&instance->send_tasklet);
+       usb_put_dev(instance->usb_dev);
+       kfree(instance);
+}
+
+void udsl_get_instance(struct udsl_instance_data *instance)
+{
+       kref_get(&instance->refcount);
+}
+
+void udsl_put_instance(struct udsl_instance_data *instance)
+{
+       kref_put(&instance->refcount, udsl_destroy_instance);
+}
+
+/**********
+**  ATM  **
+**********/
+
+static void udsl_atm_dev_close(struct atm_dev *dev)
+{
+       struct udsl_instance_data *instance = dev->dev_data;
+
+       dev->dev_data = NULL;
+       udsl_put_instance(instance);
+}
+
+static int udsl_atm_proc_read(struct atm_dev *atm_dev, loff_t * pos, char *page)
+{
+       struct udsl_instance_data *instance = atm_dev->dev_data;
+       int left = *pos;
+
+       if (!instance) {
+               dbg("udsl_atm_proc_read: NULL instance!");
+               return -ENODEV;
+       }
+
+       if (!left--)
+               return sprintf(page, "%s\n", instance->description);
+
+       if (!left--)
+               return sprintf(page, "MAC: %02x:%02x:%02x:%02x:%02x:%02x\n",
+                              atm_dev->esi[0], atm_dev->esi[1],
+                              atm_dev->esi[2], atm_dev->esi[3],
+                              atm_dev->esi[4], atm_dev->esi[5]);
+
+       if (!left--)
+               return sprintf(page,
+                              "AAL5: tx %d ( %d err ), rx %d ( %d err, %d drop )\n",
+                              atomic_read(&atm_dev->stats.aal5.tx),
+                              atomic_read(&atm_dev->stats.aal5.tx_err),
+                              atomic_read(&atm_dev->stats.aal5.rx),
+                              atomic_read(&atm_dev->stats.aal5.rx_err),
+                              atomic_read(&atm_dev->stats.aal5.rx_drop));
+
+       if (!left--) {
+               switch (atm_dev->signal) {
+               case ATM_PHY_SIG_FOUND:
+                       sprintf(page, "Line up");
+                       break;
+               case ATM_PHY_SIG_LOST:
+                       sprintf(page, "Line down");
+                       break;
+               default:
+                       sprintf(page, "Line state unknown");
+                       break;
+               }
+
+               if (instance->usb_dev->state == USB_STATE_NOTATTACHED)
+                       strcat(page, ", disconnected\n");
+               else {
+                       if (instance->status == UDSL_LOADED_FIRMWARE)
+                               strcat(page, ", firmware loaded\n");
+                       else if (instance->status == UDSL_LOADING_FIRMWARE)
+                               strcat(page, ", firmware loading\n");
+                       else
+                               strcat(page, ", no firmware\n");
+               }
+
+               return strlen(page);
+       }
+
+       return 0;
+}
+
+static int udsl_atm_open(struct atm_vcc *vcc)
+{
+       struct udsl_instance_data *instance = vcc->dev->dev_data;
+       struct udsl_vcc_data *new;
+       unsigned int max_pdu;
+       int vci = vcc->vci;
+       short vpi = vcc->vpi;
+       int err;
+
+       dbg("udsl_atm_open: vpi %hd, vci %d", vpi, vci);
+
+       if (!instance) {
+               dbg("udsl_atm_open: NULL data!");
+               return -ENODEV;
+       }
+
+       /* only support AAL5 */
+       if ((vcc->qos.aal != ATM_AAL5) || (vcc->qos.rxtp.max_sdu < 0)
+           || (vcc->qos.rxtp.max_sdu > ATM_MAX_AAL5_PDU)) {
+               dbg("udsl_atm_open: unsupported ATM type %d!", vcc->qos.aal);
+               return -EINVAL;
+       }
+
+       if (instance->firmware_wait &&
+           (err = instance->firmware_wait(instance)) < 0) {
+               dbg("udsl_atm_open: firmware not loaded (%d)!", err);
+               return err;
+       }
+
+       down(&instance->serialize);     /* vs self, udsl_atm_close */
+
+       if (udsl_find_vcc(instance, vpi, vci)) {
+               dbg("udsl_atm_open: %hd/%d already in use!", vpi, vci);
+               up(&instance->serialize);
+               return -EADDRINUSE;
+       }
+
+       if (!(new = kmalloc(sizeof(struct udsl_vcc_data), GFP_KERNEL))) {
+               dbg("udsl_atm_open: no memory for vcc_data!");
+               up(&instance->serialize);
+               return -ENOMEM;
+       }
+
+       memset(new, 0, sizeof(struct udsl_vcc_data));
+       new->vcc = vcc;
+       new->vpi = vpi;
+       new->vci = vci;
+
+       /* udsl_extract_cells requires at least one cell */
+       max_pdu = max(1, UDSL_NUM_CELLS(vcc->qos.rxtp.max_sdu)) * ATM_CELL_PAYLOAD;
+       if (!(new->sarb = alloc_skb(max_pdu, GFP_KERNEL))) {
+               dbg("udsl_atm_open: no memory for SAR buffer!");
+               kfree(new);
+               up(&instance->serialize);
+               return -ENOMEM;
+       }
+
+       vcc->dev_data = new;
+
+       tasklet_disable(&instance->receive_tasklet);
+       list_add(&new->list, &instance->vcc_list);
+       tasklet_enable(&instance->receive_tasklet);
+
+       set_bit(ATM_VF_ADDR, &vcc->flags);
+       set_bit(ATM_VF_PARTIAL, &vcc->flags);
+       set_bit(ATM_VF_READY, &vcc->flags);
+
+       up(&instance->serialize);
+
+       tasklet_schedule(&instance->receive_tasklet);
+
+       dbg("udsl_atm_open: allocated vcc data 0x%p (max_pdu: %u)", new, max_pdu);
+
+       return 0;
+}
+
+static void udsl_atm_close(struct atm_vcc *vcc)
+{
+       struct udsl_instance_data *instance = vcc->dev->dev_data;
+       struct udsl_vcc_data *vcc_data = vcc->dev_data;
+
+       dbg("udsl_atm_close called");
+
+       if (!instance || !vcc_data) {
+               dbg("udsl_atm_close: NULL data!");
+               return;
+       }
+
+       dbg("udsl_atm_close: deallocating vcc 0x%p with vpi %d vci %d",
+           vcc_data, vcc_data->vpi, vcc_data->vci);
+
+       udsl_cancel_send(instance, vcc);
+
+       down(&instance->serialize);     /* vs self, udsl_atm_open */
+
+       tasklet_disable(&instance->receive_tasklet);
+       list_del(&vcc_data->list);
+       tasklet_enable(&instance->receive_tasklet);
+
+       kfree_skb(vcc_data->sarb);
+       vcc_data->sarb = NULL;
+
+       kfree(vcc_data);
+       vcc->dev_data = NULL;
+
+       vcc->vpi = ATM_VPI_UNSPEC;
+       vcc->vci = ATM_VCI_UNSPEC;
+       clear_bit(ATM_VF_READY, &vcc->flags);
+       clear_bit(ATM_VF_PARTIAL, &vcc->flags);
+       clear_bit(ATM_VF_ADDR, &vcc->flags);
+
+       up(&instance->serialize);
+
+       dbg("udsl_atm_close successful");
+}
+
+static int udsl_atm_ioctl(struct atm_dev *dev, unsigned int cmd,
+                         void __user * arg)
+{
+       switch (cmd) {
+       case ATM_QUERYLOOP:
+               return put_user(ATM_LM_NONE, (int __user *)arg) ? -EFAULT : 0;
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+/**********
+**  USB  **
+**********/
+
+int udsl_instance_setup(struct usb_device *dev,
+                       struct udsl_instance_data *instance)
+{
+       char *buf;
+       int i, length;
+
+       kref_init(&instance->refcount); /* one for USB */
+       udsl_get_instance(instance);    /* one for ATM */
+
+       init_MUTEX(&instance->serialize);
+
+       instance->usb_dev = dev;
+
+       INIT_LIST_HEAD(&instance->vcc_list);
+
+       instance->status = UDSL_NO_FIRMWARE;
+       init_waitqueue_head(&instance->firmware_waiters);
+
+       spin_lock_init(&instance->receive_lock);
+       INIT_LIST_HEAD(&instance->spare_receivers);
+       INIT_LIST_HEAD(&instance->filled_receive_buffers);
+
+       tasklet_init(&instance->receive_tasklet, udsl_process_receive, (unsigned long)instance);
+       INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+       skb_queue_head_init(&instance->sndqueue);
+
+       spin_lock_init(&instance->send_lock);
+       INIT_LIST_HEAD(&instance->spare_senders);
+       INIT_LIST_HEAD(&instance->spare_send_buffers);
+
+       tasklet_init(&instance->send_tasklet, udsl_process_send,
+                    (unsigned long)instance);
+       INIT_LIST_HEAD(&instance->filled_send_buffers);
+
+       /* receive init */
+       for (i = 0; i < num_rcv_urbs; i++) {
+               struct udsl_receiver *rcv = &(instance->receivers[i]);
+
+               if (!(rcv->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+                       dbg("udsl_usb_probe: no memory for receive urb %d!", i);
+                       goto fail;
+               }
+
+               rcv->instance = instance;
+
+               list_add(&rcv->list, &instance->spare_receivers);
+       }
+
+       for (i = 0; i < num_rcv_bufs; i++) {
+               struct udsl_receive_buffer *buf =
+                   &(instance->receive_buffers[i]);
+
+               buf->base = kmalloc(rcv_buf_size * (ATM_CELL_SIZE + instance->rcv_padding),
+                                   GFP_KERNEL);
+               if (!buf->base) {
+                       dbg("udsl_usb_probe: no memory for receive buffer %d!", i);
+                       goto fail;
+               }
+
+               list_add(&buf->list, &instance->spare_receive_buffers);
+       }
+
+       /* send init */
+       for (i = 0; i < num_snd_urbs; i++) {
+               struct udsl_sender *snd = &(instance->senders[i]);
+
+               if (!(snd->urb = usb_alloc_urb(0, GFP_KERNEL))) {
+                       dbg("udsl_usb_probe: no memory for send urb %d!", i);
+                       goto fail;
+               }
+
+               snd->instance = instance;
+
+               list_add(&snd->list, &instance->spare_senders);
+       }
+
+       for (i = 0; i < num_snd_bufs; i++) {
+               struct udsl_send_buffer *buf = &(instance->send_buffers[i]);
+
+               buf->base = kmalloc(snd_buf_size * (ATM_CELL_SIZE + instance->snd_padding),
+                                   GFP_KERNEL);
+               if (!buf->base) {
+                       dbg("udsl_usb_probe: no memory for send buffer %d!", i);
+                       goto fail;
+               }
+
+               list_add(&buf->list, &instance->spare_send_buffers);
+       }
+
+       /* ATM init */
+       instance->atm_dev = atm_dev_register(instance->driver_name,
+                                            &udsl_atm_devops, -1, NULL);
+       if (!instance->atm_dev) {
+               dbg("udsl_usb_probe: failed to register ATM device!");
+               goto fail;
+       }
+
+       instance->atm_dev->ci_range.vpi_bits = ATM_CI_MAX;
+       instance->atm_dev->ci_range.vci_bits = ATM_CI_MAX;
+       instance->atm_dev->signal = ATM_PHY_SIG_UNKNOWN;
+
+       /* temp init ATM device, set to 128kbit */
+       instance->atm_dev->link_rate = 128 * 1000 / 424;
+
+       /* device description */
+       buf = instance->description;
+       length = sizeof(instance->description);
+
+       if ((i = usb_string(dev, dev->descriptor.iProduct, buf, length)) < 0)
+               goto finish;
+
+       buf += i;
+       length -= i;
+
+       i = scnprintf(buf, length, " (");
+       buf += i;
+       length -= i;
+
+       if (length <= 0 || (i = usb_make_path(dev, buf, length)) < 0)
+               goto finish;
+
+       buf += i;
+       length -= i;
+
+       snprintf(buf, length, ")");
+
+ finish:
+       /* ready for ATM callbacks */
+       wmb();
+       instance->atm_dev->dev_data = instance;
+
+       usb_get_dev(dev);
+
+       return 0;
+
+ fail:
+       for (i = 0; i < num_snd_bufs; i++)
+               kfree(instance->send_buffers[i].base);
+
+       for (i = 0; i < num_snd_urbs; i++)
+               usb_free_urb(instance->senders[i].urb);
+
+       for (i = 0; i < num_rcv_bufs; i++)
+               kfree(instance->receive_buffers[i].base);
+
+       for (i = 0; i < num_rcv_urbs; i++)
+               usb_free_urb(instance->receivers[i].urb);
+
+       return -ENOMEM;
+}
+
+void udsl_instance_disconnect(struct udsl_instance_data *instance)
+{
+       int i;
+
+       dbg("udsl_instance_disconnect entered");
+
+       if (!instance) {
+               dbg("udsl_instance_disconnect: NULL instance!");
+               return;
+       }
+
+       /* receive finalize */
+       tasklet_disable(&instance->receive_tasklet);
+
+       for (i = 0; i < num_rcv_urbs; i++)
+               usb_kill_urb(instance->receivers[i].urb);
+
+       /* no need to take the spinlock */
+       INIT_LIST_HEAD(&instance->filled_receive_buffers);
+       INIT_LIST_HEAD(&instance->spare_receive_buffers);
+
+       tasklet_enable(&instance->receive_tasklet);
+
+       for (i = 0; i < num_rcv_urbs; i++)
+               usb_free_urb(instance->receivers[i].urb);
+
+       for (i = 0; i < num_rcv_bufs; i++)
+               kfree(instance->receive_buffers[i].base);
+
+       /* send finalize */
+       tasklet_disable(&instance->send_tasklet);
+
+       for (i = 0; i < num_snd_urbs; i++)
+               usb_kill_urb(instance->senders[i].urb);
+
+       /* no need to take the spinlock */
+       INIT_LIST_HEAD(&instance->spare_senders);
+       INIT_LIST_HEAD(&instance->spare_send_buffers);
+       instance->current_buffer = NULL;
+
+       tasklet_enable(&instance->send_tasklet);
+
+       for (i = 0; i < num_snd_urbs; i++)
+               usb_free_urb(instance->senders[i].urb);
+
+       for (i = 0; i < num_snd_bufs; i++)
+               kfree(instance->send_buffers[i].base);
+
+       /* ATM finalize */
+       shutdown_atm_dev(instance->atm_dev);
+}
+
+EXPORT_SYMBOL_GPL(udsl_get_instance);
+EXPORT_SYMBOL_GPL(udsl_put_instance);
+EXPORT_SYMBOL_GPL(udsl_instance_setup);
+EXPORT_SYMBOL_GPL(udsl_instance_disconnect);
+
+/***********
+**  init  **
+***********/
+
+static int __init udsl_usb_init(void)
+{
+       dbg("udsl_usb_init: driver version " DRIVER_VERSION);
+
+       if (sizeof(struct udsl_control) > sizeof(((struct sk_buff *) 0)->cb)) {
+               printk(KERN_ERR __FILE__ ": unusable with this kernel!\n");
+               return -EIO;
+       }
+
+       if ((num_rcv_urbs > UDSL_MAX_RCV_URBS)
+           || (num_snd_urbs > UDSL_MAX_SND_URBS)
+           || (num_rcv_bufs > UDSL_MAX_RCV_BUFS)
+           || (num_snd_bufs > UDSL_MAX_SND_BUFS)
+           || (rcv_buf_size > UDSL_MAX_RCV_BUF_SIZE)
+           || (snd_buf_size > UDSL_MAX_SND_BUF_SIZE))
+               return -EINVAL;
+
+       return 0;
+}
+
+static void __exit udsl_usb_exit(void)
+{
+}
+
+module_init(udsl_usb_init);
+module_exit(udsl_usb_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+/************
+**  debug  **
+************/
+
+#ifdef VERBOSE_DEBUG
+static int udsl_print_packet(const unsigned char *data, int len)
+{
+       unsigned char buffer[256];
+       int i = 0, j = 0;
+
+       for (i = 0; i < len;) {
+               buffer[0] = '\0';
+               sprintf(buffer, "%.3d :", i);
+               for (j = 0; (j < 16) && (i < len); j++, i++) {
+                       sprintf(buffer, "%s %2.2x", buffer, data[i]);
+               }
+               dbg("%s", buffer);
+       }
+       return i;
+}
+#endif
diff --git a/drivers/usb/atm/usb_atm.h b/drivers/usb/atm/usb_atm.h
new file mode 100644 (file)
index 0000000..188e917
--- /dev/null
@@ -0,0 +1,160 @@
+/******************************************************************************
+ *  usb_atm.h - Generic USB xDSL driver core
+ *
+ *  Copyright (C) 2001, Alcatel
+ *  Copyright (C) 2003, Duncan Sands, SolNegro, Josep Comas
+ *  Copyright (C) 2004, David Woodhouse
+ *
+ *  This program is free software; you can redistribute it and/or modify it
+ *  under the terms of the GNU General Public License as published by the Free
+ *  Software Foundation; either version 2 of the License, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but WITHOUT
+ *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ *  more details.
+ *
+ *  You should have received a copy of the GNU General Public License along with
+ *  this program; if not, write to the Free Software Foundation, Inc., 59
+ *  Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ ******************************************************************************/
+
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/kref.h>
+#include <linux/atm.h>
+#include <linux/atmdev.h>
+#include <asm/semaphore.h>
+
+#define UDSL_MAX_RCV_URBS              4
+#define UDSL_MAX_SND_URBS              4
+#define UDSL_MAX_RCV_BUFS              8
+#define UDSL_MAX_SND_BUFS              8
+#define UDSL_MAX_RCV_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_MAX_SND_BUF_SIZE          1024    /* ATM cells */
+#define UDSL_DEFAULT_RCV_URBS          2
+#define UDSL_DEFAULT_SND_URBS          2
+#define UDSL_DEFAULT_RCV_BUFS          4
+#define UDSL_DEFAULT_SND_BUFS          4
+#define UDSL_DEFAULT_RCV_BUF_SIZE      64      /* ATM cells */
+#define UDSL_DEFAULT_SND_BUF_SIZE      64      /* ATM cells */
+
+#define ATM_CELL_HEADER                        (ATM_CELL_SIZE - ATM_CELL_PAYLOAD)
+#define UDSL_NUM_CELLS(x)              (((x) + ATM_AAL5_TRAILER + ATM_CELL_PAYLOAD - 1) / ATM_CELL_PAYLOAD)
+
+/* receive */
+
+struct udsl_receive_buffer {
+       struct list_head list;
+       unsigned char *base;
+       unsigned int filled_cells;
+};
+
+struct udsl_receiver {
+       struct list_head list;
+       struct udsl_receive_buffer *buffer;
+       struct urb *urb;
+       struct udsl_instance_data *instance;
+};
+
+struct udsl_vcc_data {
+       /* vpi/vci lookup */
+       struct list_head list;
+       short vpi;
+       int vci;
+       struct atm_vcc *vcc;
+
+       /* raw cell reassembly */
+       struct sk_buff *sarb;
+};
+
+/* send */
+
+struct udsl_send_buffer {
+       struct list_head list;
+       unsigned char *base;
+       unsigned char *free_start;
+       unsigned int free_cells;
+};
+
+struct udsl_sender {
+       struct list_head list;
+       struct udsl_send_buffer *buffer;
+       struct urb *urb;
+       struct udsl_instance_data *instance;
+};
+
+struct udsl_control {
+       struct atm_skb_data atm_data;
+       unsigned int num_cells;
+       unsigned int num_entire;
+       unsigned int pdu_padding;
+       unsigned char cell_header[ATM_CELL_HEADER];
+       unsigned char aal5_trailer[ATM_AAL5_TRAILER];
+};
+
+#define UDSL_SKB(x)            ((struct udsl_control *)(x)->cb)
+
+/* main driver data */
+
+enum udsl_status {
+       UDSL_NO_FIRMWARE,
+       UDSL_LOADING_FIRMWARE,
+       UDSL_LOADED_FIRMWARE
+};
+
+struct udsl_instance_data {
+       struct kref refcount;
+       struct semaphore serialize;
+
+       /* USB device part */
+       struct usb_device *usb_dev;
+       char description[64];
+       int data_endpoint;
+       int snd_padding;
+       int rcv_padding;
+       const char *driver_name;
+
+       /* ATM device part */
+       struct atm_dev *atm_dev;
+       struct list_head vcc_list;
+
+       /* firmware */
+       int (*firmware_wait) (struct udsl_instance_data *);
+       enum udsl_status status;
+       wait_queue_head_t firmware_waiters;
+
+       /* receive */
+       struct udsl_receiver receivers[UDSL_MAX_RCV_URBS];
+       struct udsl_receive_buffer receive_buffers[UDSL_MAX_RCV_BUFS];
+
+       spinlock_t receive_lock;
+       struct list_head spare_receivers;
+       struct list_head filled_receive_buffers;
+
+       struct tasklet_struct receive_tasklet;
+       struct list_head spare_receive_buffers;
+
+       /* send */
+       struct udsl_sender senders[UDSL_MAX_SND_URBS];
+       struct udsl_send_buffer send_buffers[UDSL_MAX_SND_BUFS];
+
+       struct sk_buff_head sndqueue;
+
+       spinlock_t send_lock;
+       struct list_head spare_senders;
+       struct list_head spare_send_buffers;
+
+       struct tasklet_struct send_tasklet;
+       struct sk_buff *current_skb;                    /* being emptied */
+       struct udsl_send_buffer *current_buffer;        /* being filled */
+       struct list_head filled_send_buffers;
+};
+
+extern int udsl_instance_setup(struct usb_device *dev,
+                              struct udsl_instance_data *instance);
+extern void udsl_instance_disconnect(struct udsl_instance_data *instance);
+extern void udsl_get_instance(struct udsl_instance_data *instance);
+extern void udsl_put_instance(struct udsl_instance_data *instance);
diff --git a/include/asm-ia64/sn/shub_mmr.h b/include/asm-ia64/sn/shub_mmr.h
new file mode 100644 (file)
index 0000000..430c50f
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (c) 2001-2004 Silicon Graphics, Inc.  All rights reserved.
+ */
+
+#ifndef _ASM_IA64_SN_SHUB_MMR_H
+#define _ASM_IA64_SN_SHUB_MMR_H
+
+/* ==================================================================== */
+/*                        Register "SH_IPI_INT"                         */
+/*               SHub Inter-Processor Interrupt Registers               */
+/* ==================================================================== */
+#define SH_IPI_INT                               0x0000000110000380UL
+#define SH_IPI_INT_MASK                          0x8ff3ffffffefffffUL
+#define SH_IPI_INT_INIT                          0x0000000000000000UL
+
+/*   SH_IPI_INT_TYPE                                                    */
+/*   Description:  Type of Interrupt: 0=INT, 2=PMI, 4=NMI, 5=INIT       */
+#define SH_IPI_INT_TYPE_SHFT                     0
+#define SH_IPI_INT_TYPE_MASK                     0x0000000000000007UL
+
+/*   SH_IPI_INT_AGT                                                     */
+/*   Description:  Agent, must be 0 for SHub                            */
+#define SH_IPI_INT_AGT_SHFT                      3
+#define SH_IPI_INT_AGT_MASK                      0x0000000000000008UL
+
+/*   SH_IPI_INT_PID                                                     */
+/*   Description:  Processor ID, same setting as on targeted McKinley  */
+#define SH_IPI_INT_PID_SHFT                      4
+#define SH_IPI_INT_PID_MASK                      0x00000000000ffff0UL
+
+/*   SH_IPI_INT_BASE                                                    */
+/*   Description:  Optional interrupt vector area, 2MB aligned          */
+#define SH_IPI_INT_BASE_SHFT                     21
+#define SH_IPI_INT_BASE_MASK                     0x0003ffffffe00000UL
+
+/*   SH_IPI_INT_IDX                                                     */
+/*   Description:  Targeted McKinley interrupt vector                   */
+#define SH_IPI_INT_IDX_SHFT                      52
+#define SH_IPI_INT_IDX_MASK                      0x0ff0000000000000UL
+
+/*   SH_IPI_INT_SEND                                                    */
+/*   Description:  Send Interrupt Message to PI, This generates a puls  */
+#define SH_IPI_INT_SEND_SHFT                     63
+#define SH_IPI_INT_SEND_MASK                     0x8000000000000000UL
+
+/* ==================================================================== */
+/*                     Register "SH_EVENT_OCCURRED"                     */
+/*                    SHub Interrupt Event Occurred                     */
+/* ==================================================================== */
+#define SH_EVENT_OCCURRED                        0x0000000110010000UL
+#define SH_EVENT_OCCURRED_ALIAS                  0x0000000110010008UL
+
+/* ==================================================================== */
+/*                     Register "SH_PI_CAM_CONTROL"                     */
+/*                      CRB CAM MMR Access Control                      */
+/* ==================================================================== */
+#ifndef __ASSEMBLY__
+#define SH_PI_CAM_CONTROL                        0x0000000120050300UL
+#else
+#define SH_PI_CAM_CONTROL                        0x0000000120050300
+#endif
+
+/* ==================================================================== */
+/*                        Register "SH_SHUB_ID"                         */
+/*                            SHub ID Number                            */
+/* ==================================================================== */
+#define SH_SHUB_ID                               0x0000000110060580UL
+#define SH_SHUB_ID_REVISION_SHFT                 28
+#define SH_SHUB_ID_REVISION_MASK                 0x00000000f0000000
+
+/* ==================================================================== */
+/*                         Register "SH_PTC_0"                          */
+/*       Puge Translation Cache Message Configuration Information       */
+/* ==================================================================== */
+#define SH_PTC_0                                 0x00000001101a0000UL
+#define SH_PTC_1                                 0x00000001101a0080UL
+
+/* ==================================================================== */
+/*                          Register "SH_RTC"                           */
+/*                           Real-time Clock                            */
+/* ==================================================================== */
+#define SH_RTC                                   0x00000001101c0000UL
+#define SH_RTC_MASK                              0x007fffffffffffffUL
+
+/* ==================================================================== */
+/*                 Register "SH_MEMORY_WRITE_STATUS_0|1"                */
+/*                    Memory Write Status for CPU 0 & 1                 */
+/* ==================================================================== */
+#define SH_MEMORY_WRITE_STATUS_0                 0x0000000120070000UL
+#define SH_MEMORY_WRITE_STATUS_1                 0x0000000120070080UL
+
+/* ==================================================================== */
+/*                   Register "SH_PIO_WRITE_STATUS_0|1"                 */
+/*                      PIO Write Status for CPU 0 & 1                  */
+/* ==================================================================== */
+#ifndef __ASSEMBLY__
+#define SH_PIO_WRITE_STATUS_0                    0x0000000120070200UL
+#define SH_PIO_WRITE_STATUS_1                    0x0000000120070280UL
+
+/*   SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK                               */
+/*   Description:  Deadlock response detected                           */
+#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT 1
+#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_MASK 0x0000000000000002
+
+/*   SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT                          */
+/*   Description:  Count of currently pending PIO writes                */
+#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT 56
+#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_MASK 0x3f00000000000000UL
+#else
+#define SH_PIO_WRITE_STATUS_0                    0x0000000120070200
+#define SH_PIO_WRITE_STATUS_0_PENDING_WRITE_COUNT_SHFT 56
+#define SH_PIO_WRITE_STATUS_0_WRITE_DEADLOCK_SHFT 1
+#endif
+
+/* ==================================================================== */
+/*                Register "SH_PIO_WRITE_STATUS_0_ALIAS"                */
+/* ==================================================================== */
+#ifndef __ASSEMBLY__
+#define SH_PIO_WRITE_STATUS_0_ALIAS              0x0000000120070208UL
+#else
+#define SH_PIO_WRITE_STATUS_0_ALIAS              0x0000000120070208
+#endif
+
+/* ==================================================================== */
+/*                     Register "SH_EVENT_OCCURRED"                     */
+/*                    SHub Interrupt Event Occurred                     */
+/* ==================================================================== */
+/*   SH_EVENT_OCCURRED_UART_INT                                         */
+/*   Description:  Pending Junk Bus UART Interrupt                      */
+#define SH_EVENT_OCCURRED_UART_INT_SHFT          20
+#define SH_EVENT_OCCURRED_UART_INT_MASK          0x0000000000100000
+
+/*   SH_EVENT_OCCURRED_IPI_INT                                          */
+/*   Description:  Pending IPI Interrupt                                */
+#define SH_EVENT_OCCURRED_IPI_INT_SHFT           28
+#define SH_EVENT_OCCURRED_IPI_INT_MASK           0x0000000010000000
+
+/*   SH_EVENT_OCCURRED_II_INT0                                          */
+/*   Description:  Pending II 0 Interrupt                               */
+#define SH_EVENT_OCCURRED_II_INT0_SHFT           29
+#define SH_EVENT_OCCURRED_II_INT0_MASK           0x0000000020000000
+
+/*   SH_EVENT_OCCURRED_II_INT1                                          */
+/*   Description:  Pending II 1 Interrupt                               */
+#define SH_EVENT_OCCURRED_II_INT1_SHFT           30
+#define SH_EVENT_OCCURRED_II_INT1_MASK           0x0000000040000000
+
+/* ==================================================================== */
+/*                         Register "SH_PTC_0"                          */
+/*       Puge Translation Cache Message Configuration Information       */
+/* ==================================================================== */
+#define SH_PTC_0                                 0x00000001101a0000UL
+#define SH_PTC_0_MASK                            0x80000000fffffffd
+#define SH_PTC_0_INIT                            0x0000000000000000
+
+/*   SH_PTC_0_A                                                         */
+/*   Description:  Type                                                 */
+#define SH_PTC_0_A_SHFT                          0
+#define SH_PTC_0_A_MASK                          0x0000000000000001
+
+/*   SH_PTC_0_PS                                                        */
+/*   Description:  Page Size                                            */
+#define SH_PTC_0_PS_SHFT                         2
+#define SH_PTC_0_PS_MASK                         0x00000000000000fc
+
+/*   SH_PTC_0_RID                                                       */
+/*   Description:  Region ID                                            */
+#define SH_PTC_0_RID_SHFT                        8
+#define SH_PTC_0_RID_MASK                        0x00000000ffffff00
+
+/*   SH_PTC_0_START                                                     */
+/*   Description:  Start                                                */
+#define SH_PTC_0_START_SHFT                      63
+#define SH_PTC_0_START_MASK                      0x8000000000000000
+
+/* ==================================================================== */
+/*                         Register "SH_PTC_1"                          */
+/*       Puge Translation Cache Message Configuration Information       */
+/* ==================================================================== */
+#define SH_PTC_1                                 0x00000001101a0080UL
+#define SH_PTC_1_MASK                            0x9ffffffffffff000
+#define SH_PTC_1_INIT                            0x0000000000000000
+
+/*   SH_PTC_1_VPN                                                       */
+/*   Description:  Virtual page number                                  */
+#define SH_PTC_1_VPN_SHFT                        12
+#define SH_PTC_1_VPN_MASK                        0x1ffffffffffff000
+
+/*   SH_PTC_1_START                                                     */
+/*   Description:  PTC_1 Start                                          */
+#define SH_PTC_1_START_SHFT                      63
+#define SH_PTC_1_START_MASK                      0x8000000000000000
+
+#endif /* _ASM_IA64_SN_SHUB_MMR_H */