Add support for RTL8188EE.
authorjmatthew <jmatthew@openbsd.org>
Fri, 21 Sep 2018 01:45:53 +0000 (01:45 +0000)
committerjmatthew <jmatthew@openbsd.org>
Fri, 21 Sep 2018 01:45:53 +0000 (01:45 +0000)
This needs a new firmware image, which should be added to the rtwn
firmware package shortly.

testing and lots of help from kevlo@
ok kevlo@ stsp@

sys/dev/ic/r92creg.h
sys/dev/ic/rtwn.c
sys/dev/ic/rtwnvar.h
sys/dev/pci/if_rtwn.c

index c2be8db..8dbf64c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: r92creg.h,v 1.17 2018/09/13 09:28:07 kevlo Exp $      */
+/*     $OpenBSD: r92creg.h,v 1.18 2018/09/21 01:45:53 jmatthew Exp $   */
 
 /*-
  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -58,6 +58,8 @@
 #define R92C_FSISR                     0x054
 #define R92C_HSIMR                     0x058
 #define R92C_HSISR                     0x05c
+#define R92C_AFE_XTAL_CTRL_EXT         0x078
+#define R88E_XCK_OUT_CTRL              0x07c
 #define R92C_MCUFWDL                   0x080
 #define R92C_HMEBOX_EXT(idx)           (0x088 + (idx) * 2)
 #define R88E_HIMR                      0x0b0
@@ -96,6 +98,7 @@
 #define R92C_MBIST_START               0x174
 #define R92C_MBIST_DONE                        0x178
 #define R92C_MBIST_FAIL                        0x17c
+#define R88E_32K_CTRL                  0x194
 #define R92C_C2HEVT_MSG                        0x1a0
 #define R92C_C2HEVT_CLEAR              0x1af
 #define R92C_C2HEVT_MSG_TEST           0x1b8
 /* Rx DMA Configuration. */
 #define R92C_RXDMA_AGG_PG_TH           0x280
 #define R92C_RXPKT_NUM                 0x284
+#define R88E_RXDMA_CTRL                        0x286
 #define R92C_RXDMA_STATUS              0x288
 
 #define R92C_PCIE_CTRL_REG             0x300
 #define R92C_RD_RESP_PKT_TH            0x463
 #define R92C_INIRTS_RATE_SEL           0x480
 #define R92C_INIDATA_RATE_SEL(macid)   (0x484 + (macid))
-#define R88E_TX_RPT_CTRL               0x4ec
 #define R92C_MAX_AGGR_NUM              0x4ca
+#define R88E_TX_RPT_CTRL               0x4ec
+#define R88E_TX_RPT_TIME               0x4f0
 /* EDCA Configuration. */
 #define R92C_EDCA_VO_PARAM             0x500
 #define R92C_EDCA_VI_PARAM             0x504
 #define R92C_AFE_XTAL_CTRL_ADDR_M      0x007ff800
 #define R92C_AFE_XTAL_CTRL_ADDR_S      11
 
+/* Bits for R88E_XCK_OUT_CTRL. */
+#define R88E_XCK_OUT_CTRL_EN           1
+
 /* Bits for R92C_EFUSE_CTRL. */
 #define R92C_EFUSE_CTRL_DATA_M 0x000000ff
 #define R92C_EFUSE_CTRL_DATA_S 0
 /* Bits for R92C_GPIO_MUXCFG. */
 #define R92C_GPIO_MUXCFG_RFKILL        0x0008
 #define R92C_GPIO_MUXCFG_ENBT  0x0020
+#define R92C_GPIO_MUXCFG_ENSIC 0x1000
 
 /* Bits for R92C_GPIO_IO_SEL. */
 #define R92C_GPIO_IO_SEL_RFKILL        0x0008
 #define R92C_MCUFWDL_CPRST             0x00800000
 
 /* Bits for R88E_HIMR. */
+#define R88E_HIMR_ROK                  0x00000001
+#define R88E_HIMR_RDU                  0x00000002
+#define R88E_HIMR_VODOK                        0x00000004
+#define R88E_HIMR_VIDOK                        0x00000008
+#define R88E_HIMR_BEDOK                        0x00000010
+#define R88E_HIMR_BKDOK                        0x00000020
+#define R88E_HIMR_MGNTDOK              0x00000040
+#define R88E_HIMR_HIGHDOK              0x00000080
 #define R88E_HIMR_CPWM                 0x00000100
 #define R88E_HIMR_CPWM2                        0x00000200
+#define R88E_HIMR_C2HCMD               0x00000400
+#define R88E_HIMR_HISR1_IND_INT                0x00000800
+#define R88E_HIMR_ATIMEND              0x00001000
+#define R88E_HIMR_BCNDMAINT_E          0x00004000
+#define R88E_HIMR_HSISR_IND_ON_INT     0x00008000
+#define R88E_HIMR_BCNDOK0              0x00010000
+#define R88E_HIMR_BCNDMAINT0           0x00100000
+#define R88E_HIMR_TSF_BIT32_TOGGLE     0x01000000
+#define R88E_HIMR_TBDOK                        0x02000000
 #define R88E_HIMR_TBDER                        0x04000000
+#define R88E_HIMR_GTINT3               0x08000000
+#define R88E_HIMR_GTINT4               0x10000000
 #define R88E_HIMR_PSTIMEOUT            0x20000000
+#define R88E_HIMR_TXCCK                        0x40000000
 
 /* Bits for R88E_HIMRE.*/
 #define R88E_HIMRE_RXFOVW              0x00000100
 #define R88E_HIMRE_RXERR               0x00000400
 #define R88E_HIMRE_TXERR               0x00000800
 
+/* Bits for R88E_HSIMR */
+#define R88E_HSIMR_GPIO12_0_INT_EN     0x00000001
+#define R88E_HSIMR_SPS_OCP_INT_EN      0x00000020
+#define R88E_HSIMR_RON_INT_EN          0x00000040
+#define R88E_HSIMR_PDN_INT_EN          0x00000080
+#define R88E_HSIMR_GPIO9_INT_EN                0x02000000
+
 /* Bits for R92C_EFUSE_ACCESS. */
 #define R92C_EFUSE_ACCESS_OFF          0x00
 #define R92C_EFUSE_ACCESS_ON           0x69
@@ -1100,7 +1136,7 @@ struct r88e_tx_pwr {
 } __packed;
 
 /*
- * RTL8188EU ROM image.
+ * RTL8188E ROM images.
  */
 struct r88e_rom {
        uint16_t                id;
@@ -1119,6 +1155,18 @@ struct r88e_rom {
        uint8_t                 reserved4[3];
        uint8_t                 rf_ant_opt;
        uint8_t                 reserved5[6];
+} __packed;
+
+struct r88e_pci_rom {
+       uint8_t                 macaddr[IEEE80211_ADDR_LEN];
+       uint16_t                vid;
+       uint16_t                did;
+       uint16_t                svid;
+       uint16_t                smid;
+       uint8_t                 reserved[290];
+} __packed;
+
+struct r88e_usb_rom {
        uint16_t                vid;
        uint16_t                pid;
        uint8_t                 usb_opt;
index 01aca68..8ede639 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rtwn.c,v 1.37 2018/09/13 09:28:07 kevlo Exp $ */
+/*     $OpenBSD: rtwn.c,v 1.38 2018/09/21 01:45:53 jmatthew Exp $      */
 
 /*-
  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -637,18 +637,29 @@ rtwn_r88e_read_rom(struct rtwn_softc *sc)
 {
        struct ieee80211com *ic = &sc->sc_ic;
        struct r88e_rom *rom = &sc->sc_r88e_rom;
+       struct r88e_pci_rom *pcirom = &sc->sc_r88e_pci_rom;
+       struct r88e_usb_rom *usbrom = &sc->sc_r88e_usb_rom;
+       int romsize;
+
+       if (sc->chip & RTWN_CHIP_PCI)
+               romsize = sizeof(struct r88e_pci_rom);
+       else
+               romsize = sizeof(struct r88e_usb_rom);
 
        /* Read full ROM image. */
        rtwn_efuse_read(sc, (uint8_t *)&sc->sc_r88e_rom,
-           sizeof(sc->sc_r88e_rom));
+           sizeof(sc->sc_r88e_rom) + romsize);
 
-       sc->crystal_cap = rom->xtal;
+       sc->crystal_cap = (sc->chip & RTWN_CHIP_PCI) ? 0x20 : rom->xtal;
        DPRINTF(("Crystal cap=0x%x\n", sc->crystal_cap));
 
        sc->regulatory = MS(rom->rf_board_opt, R92C_ROM_RF1_REGULATORY);
        DPRINTF(("regulatory type=%d\n", sc->regulatory));
 
-       IEEE80211_ADDR_COPY(ic->ic_myaddr, rom->macaddr);
+       if (sc->chip & RTWN_CHIP_PCI)
+               IEEE80211_ADDR_COPY(ic->ic_myaddr, pcirom->macaddr);
+       else
+               IEEE80211_ADDR_COPY(ic->ic_myaddr, usbrom->macaddr);
 }
 
 int
@@ -1729,6 +1740,13 @@ rtwn_rf_init(struct rtwn_softc *sc)
                sc->rf_chnlbw[i] = rtwn_rf_read(sc, i, R92C_RF_CHNLBW);
        }
 
+       /* magic value for HP 8188EEs */
+       if (sc->chip == (RTWN_CHIP_88E | RTWN_CHIP_PCI)) {
+               struct r88e_pci_rom *pcirom = &sc->sc_r88e_pci_rom;
+               if ((pcirom->svid == 0x103c) && (pcirom->smid == 0x197d))
+                       rtwn_rf_write(sc, 0, 0x52, 0x7e4bd);
+       }
+
        if ((sc->chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) ==
            RTWN_CHIP_UMC_A_CUT) {
                rtwn_rf_write(sc, 0, R92C_RF_RX_G1, 0x30255);
@@ -2186,6 +2204,9 @@ rtwn_set_chan(struct rtwn_softc *sc, struct ieee80211_channel *c,
                    ((sc->chip & RTWN_CHIP_88E) ? R88E_RF_CHNLBW_BW20 :
                    R92C_RF_CHNLBW_BW20));
        }
+
+       if (sc->chip == (RTWN_CHIP_88E | RTWN_CHIP_PCI))
+               DELAY(25000);
 }
 
 int
@@ -2609,12 +2630,23 @@ rtwn_enable_intr(struct rtwn_softc *sc)
 {
        if (sc->chip & RTWN_CHIP_88E) {
                rtwn_write_4(sc, R88E_HISR, 0xffffffff);
-               rtwn_write_4(sc, R88E_HIMR, R88E_HIMR_CPWM |
-               R88E_HIMR_CPWM2 | R88E_HIMR_TBDER |
-               R88E_HIMR_PSTIMEOUT);
-               rtwn_write_4(sc, R88E_HIMRE, R88E_HIMRE_RXFOVW |
-                   R88E_HIMRE_TXFOVW | R88E_HIMRE_RXERR |
-                   R88E_HIMRE_TXERR);
+               if (sc->chip & RTWN_CHIP_USB) {
+                       rtwn_write_4(sc, R88E_HIMR, R88E_HIMR_CPWM |
+                       R88E_HIMR_CPWM2 | R88E_HIMR_TBDER |
+                       R88E_HIMR_PSTIMEOUT);
+                       rtwn_write_4(sc, R88E_HIMRE, R88E_HIMRE_RXFOVW |
+                           R88E_HIMRE_TXFOVW | R88E_HIMRE_RXERR |
+                           R88E_HIMRE_TXERR);
+               } else {
+                       rtwn_write_4(sc, R88E_HIMR,
+                           RTWN_88E_INT_ENABLE);
+                       rtwn_write_4(sc, R88E_HIMRE,
+                           R88E_HIMRE_RXFOVW);
+                       rtwn_write_1(sc, R92C_C2HEVT_CLEAR, 0);
+                       rtwn_write_4(sc, R92C_HSIMR,
+                           R88E_HSIMR_PDN_INT_EN | R88E_HSIMR_RON_INT_EN);
+               }
+
                if (sc->chip & RTWN_CHIP_USB) {
                        rtwn_write_1(sc, R92C_USB_SPECIAL_OPTION,
                            rtwn_read_1(sc, R92C_USB_SPECIAL_OPTION) |
@@ -2626,7 +2658,7 @@ rtwn_enable_intr(struct rtwn_softc *sc)
                if (sc->chip & RTWN_CHIP_USB)
                        imask = 0xffffffff;
                else if (sc->chip & RTWN_CHIP_PCI)
-                       imask = RTWN_INT_ENABLE;
+                       imask = RTWN_92C_INT_ENABLE;
                else
                        panic("unknown chip type 0x%x", sc->chip);
 
index a5a3c67..698c76a 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: rtwnvar.h,v 1.10 2017/07/08 14:26:23 kevlo Exp $      */
+/*     $OpenBSD: rtwnvar.h,v 1.11 2018/09/21 01:45:53 jmatthew Exp $   */
 
 /*-
  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -48,10 +48,14 @@ struct rtwn_ops {
 #define RTWN_LED_LINK  0
 #define RTWN_LED_DATA  1
 
-#define RTWN_INT_ENABLE        (R92C_IMR_ROK | R92C_IMR_VODOK | R92C_IMR_VIDOK | \
+#define RTWN_92C_INT_ENABLE (R92C_IMR_ROK | R92C_IMR_VODOK | R92C_IMR_VIDOK | \
                        R92C_IMR_BEDOK | R92C_IMR_BKDOK | R92C_IMR_MGNTDOK | \
                        R92C_IMR_HIGHDOK | R92C_IMR_BDOK | R92C_IMR_RDU | \
                        R92C_IMR_RXFOVW)
+#define RTWN_88E_INT_ENABLE (R88E_HIMR_PSTIMEOUT | R88E_HIMR_HSISR_IND_ON_INT | \
+                       R88E_HIMR_C2HCMD | R88E_HIMR_ROK | R88E_HIMR_VODOK | \
+                       R88E_HIMR_VIDOK | R88E_HIMR_BEDOK | R88E_HIMR_BKDOK | \
+                       R88E_HIMR_MGNTDOK | R88E_HIMR_HIGHDOK | R88E_HIMR_RDU)
 
 struct rtwn_softc {
        /* sc_ops must be initialized by the attachment driver! */
@@ -95,10 +99,18 @@ struct rtwn_softc {
        int                             fwcur;
        union {
                struct r92c_rom         r92c_rom;
-               struct r88e_rom         r88e_rom;
+               struct {
+                       struct r88e_rom r88e_rom;
+                       union {
+                               struct r88e_pci_rom pci;
+                               struct r88e_usb_rom usb;
+                       } u;
+               } __packed _88e;
        } u;
 #define sc_r92c_rom    u.r92c_rom
-#define sc_r88e_rom    u.r88e_rom
+#define sc_r88e_rom    u._88e.r88e_rom
+#define sc_r88e_pci_rom        u._88e.u.pci
+#define sc_r88e_usb_rom        u._88e.u.usb
 
        uint32_t                        rf_chnlbw[R92C_MAX_CHAINS];
 };
index c146cee..f9a713b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_rtwn.c,v 1.32 2018/09/13 09:28:07 kevlo Exp $      */
+/*     $OpenBSD: if_rtwn.c,v 1.33 2018/09/21 01:45:53 jmatthew Exp $   */
 
 /*-
  * Copyright (c) 2010 Damien Bergamini <damien.bergamini@free.fr>
@@ -19,7 +19,7 @@
  */
 
 /*
- * PCI front-end for Realtek RTL8188CE/RTL8192CE driver.
+ * PCI front-end for Realtek RTL8188CE/RTL8188EE/RTL8192CE driver.
  */
 
 #include "bpfilter.h"
@@ -64,6 +64,7 @@
  * Driver definitions.
  */
 
+#define R92C_NPQ_NPAGES                0
 #define R92C_PUBQ_NPAGES       176
 #define R92C_HPQ_NPAGES                41
 #define R92C_LPQ_NPAGES                28
 #define R92C_TX_PAGE_COUNT     \
        (R92C_PUBQ_NPAGES + R92C_HPQ_NPAGES + R92C_LPQ_NPAGES)
 #define R92C_TX_PAGE_BOUNDARY  (R92C_TX_PAGE_COUNT + 1)
+#define R92C_MAX_RX_DMA_SIZE   0x2800
+
+#define R88E_NPQ_NPAGES                0
+#define R88E_PUBQ_NPAGES       116
+#define R88E_HPQ_NPAGES                41
+#define R88E_LPQ_NPAGES                13
+#define R88E_TXPKTBUF_COUNT    176
+#define R88E_TX_PAGE_COUNT     \
+       (R88E_PUBQ_NPAGES + R88E_HPQ_NPAGES + R88E_LPQ_NPAGES)
+#define R88E_TX_PAGE_BOUNDARY  (R88E_TX_PAGE_COUNT + 1)
+#define R88E_MAX_RX_DMA_SIZE   0x2600
 
 #define RTWN_NTXQUEUES                 9
 #define RTWN_RX_LIST_COUNT             256
@@ -202,6 +214,7 @@ extern int rtwn_debug;
 
 static const struct pci_matchid rtwn_pci_devices[] = {
        { PCI_VENDOR_REALTEK,   PCI_PRODUCT_REALTEK_RTL8188CE },
+       { PCI_VENDOR_REALTEK,   PCI_PRODUCT_REALTEK_RTL8188EE },
        { PCI_VENDOR_REALTEK,   PCI_PRODUCT_REALTEK_RTL8192CE }
 };
 
@@ -229,12 +242,15 @@ int               rtwn_tx(void *, struct mbuf *, struct ieee80211_node *);
 void           rtwn_tx_done(struct rtwn_pci_softc *, int);
 int            rtwn_alloc_buffers(void *);
 int            rtwn_pci_init(void *);
+void           rtwn_pci_88e_stop(struct rtwn_pci_softc *);
 void           rtwn_pci_stop(void *);
 int            rtwn_intr(void *);
 int            rtwn_is_oactive(void *);
+int            rtwn_92c_power_on(struct rtwn_pci_softc *);
+int            rtwn_88e_power_on(struct rtwn_pci_softc *);
 int            rtwn_power_on(void *);
 int            rtwn_llt_write(struct rtwn_pci_softc *, uint32_t, uint32_t);
-int            rtwn_llt_init(struct rtwn_pci_softc *);
+int            rtwn_llt_init(struct rtwn_pci_softc *, int);
 int            rtwn_dma_init(void *);
 int            rtwn_fw_loadpage(void *, int, uint8_t *, int);
 int            rtwn_pci_load_firmware(void *, u_char **, size_t *);
@@ -373,7 +389,16 @@ rtwn_pci_attach(struct device *parent, struct device *self, void *aux)
        sc->sc_sc.sc_ops.cancel_scan = rtwn_cancel_scan;
        sc->sc_sc.sc_ops.wait_async = rtwn_wait_async;
 
-       sc->sc_sc.chip = RTWN_CHIP_88C | RTWN_CHIP_92C | RTWN_CHIP_PCI;
+       sc->sc_sc.chip = RTWN_CHIP_PCI;
+       switch (PCI_PRODUCT(pa->pa_id)) {
+       case PCI_PRODUCT_REALTEK_RTL8188CE:
+       case PCI_PRODUCT_REALTEK_RTL8192CE:
+               sc->sc_sc.chip |= RTWN_CHIP_88C | RTWN_CHIP_92C;
+               break;
+       case PCI_PRODUCT_REALTEK_RTL8188EE:
+               sc->sc_sc.chip |= RTWN_CHIP_88E;
+               break;
+       }
 
        error = rtwn_attach(&sc->sc_dev, &sc->sc_sc);
        if (error != 0) {
@@ -790,6 +815,30 @@ rtwn_rx_frame(struct rtwn_pci_softc *sc, struct r92c_rx_desc_pci *rx_desc,
        rxdw0 = letoh32(rx_desc->rxdw0);
        rxdw3 = letoh32(rx_desc->rxdw3);
 
+       if (sc->sc_sc.chip & RTWN_CHIP_88E) {
+               int ntries, type;
+               struct r88e_tx_rpt_ccx *rxstat;
+
+               type = MS(rxdw3, R88E_RXDW3_RPT);
+               if (type == R88E_RXDW3_RPT_TX1) {
+                       uint32_t rptb1, rptb2;
+
+                       rxstat = mtod(rx_data->m, struct r88e_tx_rpt_ccx *);
+                       rptb1 = letoh32(rxstat->rptb1);
+                       rptb2 = letoh32(rxstat->rptb2);
+                       ntries = MS(rptb2, R88E_RPTB2_RETRY_CNT);
+                       if (rptb1 & R88E_RPTB1_PKT_OK)
+                               sc->amn.amn_txcnt++;
+                       if (ntries > 0)
+                               sc->amn.amn_retrycnt++;
+
+                       rtwn_setup_rx_desc(sc, rx_desc,
+                           rx_data->map->dm_segs[0].ds_addr, MCLBYTES,
+                           desc_idx);
+                       return;
+               }
+       }
+
        if (__predict_false(rxdw0 & (R92C_RXDW0_CRCERR | R92C_RXDW0_ICVERR))) {
                /*
                 * This should not happen since we setup our Rx filter
@@ -996,11 +1045,20 @@ rtwn_tx(void *cookie, struct mbuf *m, struct ieee80211_node *ni)
                        raid = R92C_RAID_11B;
                else
                        raid = R92C_RAID_11BG;
-               txd->txdw1 |= htole32(
-                   SM(R92C_TXDW1_MACID, R92C_MACID_BSS) |
-                   SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
-                   SM(R92C_TXDW1_RAID, raid) |
-                   R92C_TXDW1_AGGBK);
+
+               if (sc->sc_sc.chip & RTWN_CHIP_88E) {
+                       txd->txdw1 |= htole32(
+                           SM(R88E_TXDW1_MACID, R92C_MACID_BSS) |
+                           SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
+                           SM(R92C_TXDW1_RAID, raid));
+                       txd->txdw2 |= htole32(R88E_TXDW2_AGGBK);
+               } else {
+                       txd->txdw1 |= htole32(
+                           SM(R92C_TXDW1_MACID, R92C_MACID_BSS) |
+                           SM(R92C_TXDW1_QSEL, R92C_TXDW1_QSEL_BE) |
+                           SM(R92C_TXDW1_RAID, raid) |
+                           R92C_TXDW1_AGGBK);
+               }
 
                /* Request TX status report for AMRR. */
                txd->txdw2 |= htole32(R92C_TXDW2_CCX_RPT);
@@ -1178,9 +1236,114 @@ rtwn_pci_init(void *cookie)
 {
        struct rtwn_pci_softc *sc = cookie;
        ieee80211_amrr_node_init(&sc->amrr, &sc->amn);
+
+       /* Enable TX reports for AMRR */
+       if (sc->sc_sc.chip & RTWN_CHIP_88E) {
+               rtwn_pci_write_1(sc, R88E_TX_RPT_CTRL,
+                   (rtwn_pci_read_1(sc, R88E_TX_RPT_CTRL) & ~0) |
+                   R88E_TX_RPT_CTRL_EN);
+               rtwn_pci_write_1(sc, R88E_TX_RPT_CTRL + 1, 0x02);
+
+               rtwn_pci_write_2(sc, R88E_TX_RPT_TIME, 0xcdf0);
+       }
+
        return (0);
 }
 
+void
+rtwn_pci_88e_stop(struct rtwn_pci_softc *sc)
+{
+       int i, s;
+       uint16_t reg;
+
+       s = splnet();
+
+       /* Disable interrupts. */
+       rtwn_pci_write_4(sc, R88E_HIMR, 0x00000000);
+
+       /* Stop hardware. */
+       rtwn_pci_write_1(sc, R88E_TX_RPT_CTRL,
+           rtwn_pci_read_1(sc, R88E_TX_RPT_CTRL) &
+           ~(R88E_TX_RPT_CTRL_EN));
+
+       for (i = 0; i < 100; i++) {
+               if (rtwn_pci_read_1(sc, R88E_RXDMA_CTRL) & 0x02)
+                       break;
+               DELAY(10);
+       }
+       if (i == 100)
+               DPRINTF(("rxdma ctrl didn't go off, %x\n", rtwn_pci_read_1(sc, R88E_RXDMA_CTRL)));
+
+       rtwn_pci_write_1(sc, R92C_PCIE_CTRL_REG + 1, 0xff);
+
+       rtwn_pci_write_1(sc, R92C_TXPAUSE, R92C_TXPAUSE_ALL);
+
+       /* ensure transmission has stopped */
+       for (i = 0; i < 100; i++) {
+               if (rtwn_pci_read_4(sc, 0x5f8) == 0)
+                       break;
+               DELAY(10);
+       }
+       if (i == 100)
+               DPRINTF(("tx didn't stop\n"));
+
+       rtwn_pci_write_1(sc, R92C_SYS_FUNC_EN,
+           rtwn_pci_read_1(sc, R92C_SYS_FUNC_EN) &
+           ~(R92C_SYS_FUNC_EN_BBRSTB));
+       DELAY(1);
+       reg = rtwn_pci_read_2(sc, R92C_CR);
+       reg &= ~(R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
+           R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
+           R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN |
+           R92C_CR_ENSEC);
+       rtwn_pci_write_2(sc, R92C_CR, reg);
+       rtwn_pci_write_1(sc, R92C_DUAL_TSF_RST,
+           rtwn_pci_read_1(sc, R92C_DUAL_TSF_RST) | 0x20);
+
+       rtwn_pci_write_1(sc, R92C_RF_CTRL, 0x00);
+       if (rtwn_pci_read_1(sc, R92C_MCUFWDL) & R92C_MCUFWDL_RAM_DL_SEL)
+               rtwn_fw_reset(&sc->sc_sc);
+
+       rtwn_pci_write_1(sc, R92C_SYS_FUNC_EN + 1,
+           rtwn_pci_read_1(sc, R92C_SYS_FUNC_EN + 1) & ~0x02);
+       rtwn_pci_write_1(sc, R92C_MCUFWDL, 0);
+
+       rtwn_pci_write_1(sc, R88E_32K_CTRL,
+           rtwn_pci_read_1(sc, R88E_32K_CTRL) & ~(0x01));
+
+       /* transition to cardemu state */
+       rtwn_pci_write_1(sc, R92C_RF_CTRL, 0);
+       rtwn_pci_write_1(sc, R92C_LPLDO_CTRL,
+           rtwn_pci_read_1(sc, R92C_LPLDO_CTRL) | 0x10);
+       rtwn_pci_write_2(sc, R92C_APS_FSMCO,
+           rtwn_pci_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_OFF);
+       for (i = 0; i < 100; i++) {
+               if ((rtwn_pci_read_2(sc, R92C_APS_FSMCO) &
+                   R92C_APS_FSMCO_APFM_OFF) == 0)
+                       break;
+               DELAY(10);
+       }
+       if (i == 100)
+               DPRINTF(("apfm off didn't go off\n"));
+
+       /* transition to card disabled state */
+       rtwn_pci_write_1(sc, R92C_AFE_XTAL_CTRL + 2,
+           rtwn_pci_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80);
+
+       rtwn_pci_write_1(sc, R92C_RSV_CTRL + 1,
+           rtwn_pci_read_1(sc, R92C_RSV_CTRL + 1) & ~(0x08));
+       rtwn_pci_write_1(sc, R92C_RSV_CTRL + 1,
+           rtwn_pci_read_1(sc, R92C_RSV_CTRL + 1) | 0x08);
+
+       rtwn_pci_write_1(sc, R92C_RSV_CTRL, 0x0e);
+
+       for (i = 0; i < RTWN_NTXQUEUES; i++)
+               rtwn_reset_tx_list(sc, i);
+       rtwn_reset_rx_list(sc);
+
+       splx(s);
+}
+
 void
 rtwn_pci_stop(void *cookie)
 {
@@ -1188,13 +1351,18 @@ rtwn_pci_stop(void *cookie)
        uint16_t reg;
        int i, s;
 
+       if (sc->sc_sc.chip & RTWN_CHIP_88E) {
+               rtwn_pci_88e_stop(sc);
+               return;
+       }
+
        s = splnet();
 
        /* Disable interrupts. */
        rtwn_pci_write_4(sc, R92C_HIMR, 0x00000000);
 
        /* Stop hardware. */
-       rtwn_pci_write_1(sc, R92C_TXPAUSE, 0xff);
+       rtwn_pci_write_1(sc, R92C_TXPAUSE, R92C_TXPAUSE_ALL);
        rtwn_pci_write_1(sc, R92C_RF_CTRL, 0x00);
        reg = rtwn_pci_read_1(sc, R92C_SYS_FUNC_EN);
        reg |= R92C_SYS_FUNC_EN_BB_GLB_RST;
@@ -1223,6 +1391,68 @@ rtwn_pci_stop(void *cookie)
        splx(s);
 }
 
+int
+rtwn_88e_intr(struct rtwn_pci_softc *sc)
+{
+       u_int32_t status, estatus;
+       int i;
+
+       status = rtwn_pci_read_4(sc, R88E_HISR);
+       if (status == 0 || status == 0xffffffff)
+               return (0);
+
+       estatus = rtwn_pci_read_4(sc, R88E_HISRE);
+
+       status &= RTWN_88E_INT_ENABLE;
+       estatus &= R88E_HIMRE_RXFOVW;
+
+       rtwn_pci_write_4(sc, R88E_HIMR, 0);
+       rtwn_pci_write_4(sc, R88E_HIMRE, 0);
+       rtwn_pci_write_4(sc, R88E_HISR, status);
+       rtwn_pci_write_4(sc, R88E_HISRE, estatus);
+
+       if (status & R88E_HIMR_HIGHDOK)
+               rtwn_tx_done(sc, RTWN_HIGH_QUEUE);
+       if (status & R88E_HIMR_MGNTDOK)
+               rtwn_tx_done(sc, RTWN_MGNT_QUEUE);
+       if (status & R88E_HIMR_BKDOK)
+               rtwn_tx_done(sc, RTWN_BK_QUEUE);
+       if (status & R88E_HIMR_BEDOK)
+               rtwn_tx_done(sc, RTWN_BE_QUEUE);
+       if (status & R88E_HIMR_VIDOK)
+               rtwn_tx_done(sc, RTWN_VI_QUEUE);
+       if (status & R88E_HIMR_VODOK)
+               rtwn_tx_done(sc, RTWN_VO_QUEUE);
+       if ((status & (R88E_HIMR_ROK | R88E_HIMR_RDU)) ||
+           (estatus & R88E_HIMRE_RXFOVW)) {
+               bus_dmamap_sync(sc->sc_dmat, sc->rx_ring.map, 0,
+                   sizeof(struct r92c_rx_desc_pci) * RTWN_RX_LIST_COUNT,
+                   BUS_DMASYNC_POSTREAD);
+
+               for (i = 0; i < RTWN_RX_LIST_COUNT; i++) {
+                       struct r92c_rx_desc_pci *rx_desc = &sc->rx_ring.desc[i];
+                       struct rtwn_rx_data *rx_data = &sc->rx_ring.rx_data[i];
+
+                       if (letoh32(rx_desc->rxdw0) & R92C_RXDW0_OWN)
+                               continue;
+
+                       rtwn_rx_frame(sc, rx_desc, rx_data, i);
+               }
+       }
+
+       if (status & R88E_HIMR_HSISR_IND_ON_INT) {
+               rtwn_pci_write_1(sc, R92C_HSISR,
+                   rtwn_pci_read_1(sc, R92C_HSISR) |
+                   R88E_HSIMR_PDN_INT_EN | R88E_HSIMR_RON_INT_EN);
+       }
+
+       /* Enable interrupts. */
+       rtwn_pci_write_4(sc, R88E_HIMR, RTWN_88E_INT_ENABLE);
+       rtwn_pci_write_4(sc, R88E_HIMRE, R88E_HIMRE_RXFOVW);
+
+       return (1);
+}
+
 int
 rtwn_intr(void *xsc)
 {
@@ -1230,6 +1460,9 @@ rtwn_intr(void *xsc)
        u_int32_t status;
        int i;
 
+       if (sc->sc_sc.chip & RTWN_CHIP_88E)
+               return (rtwn_88e_intr(sc));
+
        status = rtwn_pci_read_4(sc, R92C_HISR);
        if (status == 0 || status == 0xffffffff)
                return (0);
@@ -1273,7 +1506,7 @@ rtwn_intr(void *xsc)
                rtwn_tx_done(sc, RTWN_VO_QUEUE);
 
        /* Enable interrupts. */
-       rtwn_pci_write_4(sc, R92C_HIMR, RTWN_INT_ENABLE);
+       rtwn_pci_write_4(sc, R92C_HIMR, RTWN_92C_INT_ENABLE);
 
        return (1);
 }
@@ -1306,12 +1539,15 @@ rtwn_llt_write(struct rtwn_pci_softc *sc, uint32_t addr, uint32_t data)
 }
 
 int
-rtwn_llt_init(struct rtwn_pci_softc *sc)
+rtwn_llt_init(struct rtwn_pci_softc *sc, int page_count)
 {
-       int i, error;
+       int i, error, pktbuf_count;
 
-       /* Reserve pages [0; R92C_TX_PAGE_COUNT]. */
-       for (i = 0; i < R92C_TX_PAGE_COUNT; i++) {
+       pktbuf_count = (sc->sc_sc.chip & RTWN_CHIP_88E) ?
+           R88E_TXPKTBUF_COUNT : R92C_TXPKTBUF_COUNT;
+
+       /* Reserve pages [0; page_count]. */
+       for (i = 0; i < page_count; i++) {
                if ((error = rtwn_llt_write(sc, i, i + 1)) != 0)
                        return (error);
        }
@@ -1319,22 +1555,21 @@ rtwn_llt_init(struct rtwn_pci_softc *sc)
        if ((error = rtwn_llt_write(sc, i, 0xff)) != 0)
                return (error);
        /*
-        * Use pages [R92C_TX_PAGE_COUNT + 1; R92C_TXPKTBUF_COUNT - 1]
+        * Use pages [page_count + 1; pktbuf_count - 1]
         * as ring buffer.
         */
-       for (++i; i < R92C_TXPKTBUF_COUNT - 1; i++) {
+       for (++i; i < pktbuf_count - 1; i++) {
                if ((error = rtwn_llt_write(sc, i, i + 1)) != 0)
                        return (error);
        }
        /* Make the last page point to the beginning of the ring buffer. */
-       error = rtwn_llt_write(sc, i, R92C_TX_PAGE_COUNT + 1);
+       error = rtwn_llt_write(sc, i, pktbuf_count + 1);
        return (error);
 }
 
 int
-rtwn_power_on(void *cookie)
+rtwn_92c_power_on(struct rtwn_pci_softc *sc)
 {
-       struct rtwn_pci_softc *sc = cookie;
        uint32_t reg;
        int ntries;
 
@@ -1463,44 +1698,165 @@ rtwn_power_on(void *cookie)
        return (0);
 }
 
+int
+rtwn_88e_power_on(struct rtwn_pci_softc *sc)
+{
+       uint32_t reg;
+       int ntries;
+
+       /* Disable XTAL output for power saving. */
+       rtwn_pci_write_1(sc, R88E_XCK_OUT_CTRL,
+           rtwn_pci_read_1(sc, R88E_XCK_OUT_CTRL) & ~R88E_XCK_OUT_CTRL_EN);
+
+       rtwn_pci_write_2(sc, R92C_APS_FSMCO,
+           rtwn_pci_read_2(sc, R92C_APS_FSMCO) & (~R92C_APS_FSMCO_APDM_HPDN));
+       rtwn_pci_write_1(sc, R92C_RSV_CTRL, 0);
+
+       /* Wait for power ready bit. */
+       for (ntries = 0; ntries < 5000; ntries++) {
+               if (rtwn_pci_read_4(sc, R92C_APS_FSMCO) & R92C_APS_FSMCO_SUS_HOST)
+                       break;
+               DELAY(10);
+       }
+       if (ntries == 5000) {
+               printf("%s: timeout waiting for chip power up\n",
+                   sc->sc_dev.dv_xname);
+               return (ETIMEDOUT);
+       }
+
+       /* Reset BB. */
+       rtwn_pci_write_1(sc, R92C_SYS_FUNC_EN,
+           rtwn_pci_read_1(sc, R92C_SYS_FUNC_EN) & ~(R92C_SYS_FUNC_EN_BBRSTB |
+           R92C_SYS_FUNC_EN_BB_GLB_RST));
+
+       rtwn_pci_write_1(sc, R92C_AFE_XTAL_CTRL + 2,
+           rtwn_pci_read_1(sc, R92C_AFE_XTAL_CTRL + 2) | 0x80);
+
+       /* Disable HWPDN. */
+       rtwn_pci_write_2(sc, R92C_APS_FSMCO,
+           rtwn_pci_read_2(sc, R92C_APS_FSMCO) & ~R92C_APS_FSMCO_APDM_HPDN);
+       /* Disable WL suspend. */
+       rtwn_pci_write_2(sc, R92C_APS_FSMCO,
+           rtwn_pci_read_2(sc, R92C_APS_FSMCO) &
+           ~(R92C_APS_FSMCO_AFSM_HSUS | R92C_APS_FSMCO_AFSM_PCIE));
+
+       /* Auto enable WLAN. */
+       rtwn_pci_write_2(sc, R92C_APS_FSMCO,
+           rtwn_pci_read_2(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_APFM_ONMAC);
+       for (ntries = 0; ntries < 5000; ntries++) {
+               if (!(rtwn_pci_read_2(sc, R92C_APS_FSMCO) &
+                   R92C_APS_FSMCO_APFM_ONMAC))
+                       break;
+               DELAY(10);
+       }
+       if (ntries == 5000) {
+               printf("%s: timeout waiting for MAC auto ON\n",
+                   sc->sc_dev.dv_xname);
+               return (ETIMEDOUT);
+       }
+
+       /* Enable LDO normal mode. */
+       rtwn_pci_write_1(sc, R92C_LPLDO_CTRL,
+           rtwn_pci_read_1(sc, R92C_LPLDO_CTRL) & ~0x10);
+
+       rtwn_pci_write_1(sc, R92C_APS_FSMCO,
+           rtwn_pci_read_1(sc, R92C_APS_FSMCO) | R92C_APS_FSMCO_PDN_EN);
+       rtwn_pci_write_1(sc, R92C_PCIE_CTRL_REG + 2,
+           rtwn_pci_read_1(sc, R92C_PCIE_CTRL_REG + 2) | 0x04);
+
+       rtwn_pci_write_1(sc, R92C_AFE_XTAL_CTRL_EXT + 1,
+           rtwn_pci_read_1(sc, R92C_AFE_XTAL_CTRL_EXT + 1) | 0x02);
+
+       rtwn_pci_write_1(sc, R92C_SYS_CLKR,
+           rtwn_pci_read_1(sc, R92C_SYS_CLKR) | 0x08);
+
+       rtwn_pci_write_2(sc, R92C_GPIO_MUXCFG,
+           rtwn_pci_read_2(sc, R92C_GPIO_MUXCFG) & ~R92C_GPIO_MUXCFG_ENSIC);
+
+       /* Enable MAC DMA/WMAC/SCHEDULE/SEC blocks. */
+       rtwn_pci_write_2(sc, R92C_CR, 0);
+       reg = rtwn_pci_read_2(sc, R92C_CR);
+       reg |= R92C_CR_HCI_TXDMA_EN | R92C_CR_HCI_RXDMA_EN |
+           R92C_CR_TXDMA_EN | R92C_CR_RXDMA_EN | R92C_CR_PROTOCOL_EN |
+           R92C_CR_SCHEDULE_EN | R92C_CR_MACTXEN | R92C_CR_MACRXEN |
+           R92C_CR_ENSEC | R92C_CR_CALTMR_EN;
+       rtwn_pci_write_2(sc, R92C_CR, reg);
+
+       rtwn_pci_write_1(sc, R92C_MSR, 0);
+       return (0);
+}
+
+int
+rtwn_power_on(void *cookie)
+{
+       struct rtwn_pci_softc *sc = cookie;
+
+       if (sc->sc_sc.chip & RTWN_CHIP_88E)
+               return (rtwn_88e_power_on(sc));
+       else
+               return (rtwn_92c_power_on(sc));
+}
+
 int
 rtwn_dma_init(void *cookie)
 {
        struct rtwn_pci_softc *sc = cookie;
        uint32_t reg;
+       uint16_t dmasize;
+       int hqpages, lqpages, nqpages, pagecnt, boundary, trxdma, tcr;
        int error;
 
+       if (sc->sc_sc.chip & RTWN_CHIP_88E) {
+               nqpages = R88E_NPQ_NPAGES;
+               hqpages = R88E_HPQ_NPAGES;
+               lqpages = R88E_LPQ_NPAGES;
+               pagecnt = R88E_TX_PAGE_COUNT;
+               boundary = R88E_TX_PAGE_BOUNDARY;
+               dmasize = R88E_MAX_RX_DMA_SIZE;
+               tcr = R92C_TCR_CFENDFORM | R92C_TCR_ERRSTEN3;
+               trxdma = 0xe771;
+       } else {
+               nqpages = R92C_NPQ_NPAGES;
+               hqpages = R92C_HPQ_NPAGES;
+               lqpages = R92C_LPQ_NPAGES;
+               pagecnt = R92C_TX_PAGE_COUNT;
+               boundary = R92C_TX_PAGE_BOUNDARY;
+               dmasize = R92C_MAX_RX_DMA_SIZE;
+               tcr = R92C_TCR_CFENDFORM | R92C_TCR_ERRSTEN0 |
+                   R92C_TCR_ERRSTEN1;
+               trxdma = 0xf771;
+       }
+
        /* Initialize LLT table. */
-       error = rtwn_llt_init(sc);
+       error = rtwn_llt_init(sc, pagecnt);
        if (error != 0)
                return error;
 
        /* Set number of pages for normal priority queue. */
-       rtwn_pci_write_2(sc, R92C_RQPN_NPQ, 0);
+       rtwn_pci_write_2(sc, R92C_RQPN_NPQ, nqpages);
        rtwn_pci_write_4(sc, R92C_RQPN,
            /* Set number of pages for public queue. */
-           SM(R92C_RQPN_PUBQ, R92C_PUBQ_NPAGES) |
+           SM(R92C_RQPN_PUBQ, pagecnt) |
            /* Set number of pages for high priority queue. */
-           SM(R92C_RQPN_HPQ, R92C_HPQ_NPAGES) |
+           SM(R92C_RQPN_HPQ, hqpages) |
            /* Set number of pages for low priority queue. */
-           SM(R92C_RQPN_LPQ, R92C_LPQ_NPAGES) |
+           SM(R92C_RQPN_LPQ, lqpages) |
            /* Load values. */
            R92C_RQPN_LD);
 
-       rtwn_pci_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, R92C_TX_PAGE_BOUNDARY);
-       rtwn_pci_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, R92C_TX_PAGE_BOUNDARY);
+       rtwn_pci_write_1(sc, R92C_TXPKTBUF_BCNQ_BDNY, boundary);
+       rtwn_pci_write_1(sc, R92C_TXPKTBUF_MGQ_BDNY, boundary);
        rtwn_pci_write_1(sc, R92C_TXPKTBUF_WMAC_LBK_BF_HD,
-           R92C_TX_PAGE_BOUNDARY);
-       rtwn_pci_write_1(sc, R92C_TRXFF_BNDY, R92C_TX_PAGE_BOUNDARY);
-       rtwn_pci_write_1(sc, R92C_TDECTRL + 1, R92C_TX_PAGE_BOUNDARY);
+           boundary);
+       rtwn_pci_write_1(sc, R92C_TRXFF_BNDY, boundary);
+       rtwn_pci_write_1(sc, R92C_TDECTRL + 1, boundary);
 
        reg = rtwn_pci_read_2(sc, R92C_TRXDMA_CTRL);
        reg &= ~R92C_TRXDMA_CTRL_QMAP_M;
-       reg |= 0xF771; 
+       reg |= trxdma;
        rtwn_pci_write_2(sc, R92C_TRXDMA_CTRL, reg);
 
-       rtwn_pci_write_4(sc, R92C_TCR,
-           R92C_TCR_CFENDFORM | R92C_TCR_ERRSTEN0 | R92C_TCR_ERRSTEN1);
+       rtwn_pci_write_4(sc, R92C_TCR, tcr);
 
        /* Configure Tx DMA. */
        rtwn_pci_write_4(sc, R92C_BKQ_DESA,
@@ -1520,9 +1876,10 @@ rtwn_dma_init(void *cookie)
 
        /* Configure Rx DMA. */
        rtwn_pci_write_4(sc, R92C_RX_DESA, sc->rx_ring.map->dm_segs[0].ds_addr);
+       rtwn_pci_write_1(sc, R92C_PCIE_CTRL_REG+1, 0);
 
        /* Set Tx/Rx transfer page boundary. */
-       rtwn_pci_write_2(sc, R92C_TRXFF_BNDY + 2, 0x27ff);
+       rtwn_pci_write_2(sc, R92C_TRXFF_BNDY + 2, dmasize - 1);
 
        /* Set Tx/Rx transfer page size. */
        rtwn_pci_write_1(sc, R92C_PBP,
@@ -1569,7 +1926,9 @@ rtwn_pci_load_firmware(void *cookie, u_char **fw, size_t *len)
        const char *name;
        int error;
 
-       if ((sc->sc_sc.chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) ==
+       if (sc->sc_sc.chip & RTWN_CHIP_88E)
+               name = "rtwn-rtl8188efw";
+       else if ((sc->sc_sc.chip & (RTWN_CHIP_UMC_A_CUT | RTWN_CHIP_92C)) ==
            RTWN_CHIP_UMC_A_CUT)
                name = "rtwn-rtl8192cfwU";
        else
@@ -1589,9 +1948,19 @@ rtwn_mac_init(void *cookie)
        int i;
 
        /* Write MAC initialization values. */
-       for (i = 0; i < nitems(rtl8192ce_mac); i++)
-               rtwn_pci_write_1(sc, rtl8192ce_mac[i].reg,
-                   rtl8192ce_mac[i].val);
+       if (sc->sc_sc.chip & RTWN_CHIP_88E) {
+               for (i = 0; i < nitems(rtl8188eu_mac); i++) {
+                       if (rtl8188eu_mac[i].reg == R92C_GPIO_MUXCFG)
+                               continue;
+                       rtwn_pci_write_1(sc, rtl8188eu_mac[i].reg,
+                           rtl8188eu_mac[i].val);
+               }
+               rtwn_pci_write_1(sc, R92C_MAX_AGGR_NUM, 0x07);
+       } else {
+               for (i = 0; i < nitems(rtl8192ce_mac); i++)
+                       rtwn_pci_write_1(sc, rtl8192ce_mac[i].reg,
+                           rtl8192ce_mac[i].val);
+       }
 }
 
 void
@@ -1608,7 +1977,8 @@ rtwn_bb_init(void *cookie)
            R92C_SYS_FUNC_EN_BBRSTB | R92C_SYS_FUNC_EN_BB_GLB_RST |
            R92C_SYS_FUNC_EN_DIO_RF);
 
-       rtwn_pci_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83);
+       if (!(sc->sc_sc.chip & RTWN_CHIP_88E))
+               rtwn_pci_write_2(sc, R92C_AFE_PLL_CTRL, 0xdb83);
 
        rtwn_pci_write_1(sc, R92C_RF_CTRL,
            R92C_RF_CTRL_EN | R92C_RF_CTRL_RSTB | R92C_RF_CTRL_SDMRSTB);
@@ -1618,14 +1988,20 @@ rtwn_bb_init(void *cookie)
            R92C_SYS_FUNC_EN_PPLL | R92C_SYS_FUNC_EN_BB_GLB_RST |
            R92C_SYS_FUNC_EN_BBRSTB);
 
-       rtwn_pci_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
+       if (!(sc->sc_sc.chip & RTWN_CHIP_88E)) {
+               rtwn_pci_write_1(sc, R92C_AFE_XTAL_CTRL + 1, 0x80);
+       }
 
        rtwn_pci_write_4(sc, R92C_LEDCFG0,
            rtwn_pci_read_4(sc, R92C_LEDCFG0) | 0x00800000);
 
-       /* Select BB programming. */ 
-       prog = (sc->sc_sc.chip & RTWN_CHIP_92C) ?
-           &rtl8192ce_bb_prog_2t : &rtl8192ce_bb_prog_1t;
+       /* Select BB programming. */
+       if (sc->sc_sc.chip & RTWN_CHIP_88E)
+               prog = &rtl8188eu_bb_prog;
+       else if (!(sc->sc_sc.chip & RTWN_CHIP_92C))
+               prog = &rtl8192ce_bb_prog_1t;
+       else
+               prog = &rtl8192ce_bb_prog_2t;
 
        /* Write BB initialization values. */
        for (i = 0; i < prog->count; i++) {