mwifiex: add support for Marvell SD8897 chipset

Some of the key differences between SD8897 and older chipsets
are as follows:

a) sdio mpa_rx and mpa_tx ports have been increased from 16 to 32
b) Same is the case with read/write bitmap that one receives from
   mpa_reg read
c) aggregation packet count doubled from 8 to 16
d) Most of key reg addresses are changed
e) There is a separate command or control port
f) Now command rx/tx_done have new interrupts

1. 'supports_sdio_new_mode' flag is added to handle (a) and (b).
2. (c) and (d) are taken care of by filling chip specific
   information in global structurei (mwifiex_sdio_sd8897).
3. For older chipsets, port 0 was cmd port and port 1->15 were
   data port. Therefore we had CTRL_PORT_MASK to differentiate
   port type. Now these changes are under 'has_control_mask' flag.

Signed-off-by: Yogesh Ashok Powar <yogeshp@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Nishant Sarmukadam <nishants@marvell.com>
Signed-off-by: Bing Zhao <bzhao@marvell.com>
Signed-off-by: Frank Huang <frankh@marvell.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c
index 4b196dc9..5ee5ed0 100644
--- a/drivers/net/wireless/mwifiex/sdio.c
+++ b/drivers/net/wireless/mwifiex/sdio.c
@@ -84,6 +84,8 @@
 		card->reg = data->reg;
 		card->max_ports = data->max_ports;
 		card->mp_agg_pkt_limit = data->mp_agg_pkt_limit;
+		card->supports_sdio_new_mode = data->supports_sdio_new_mode;
+		card->has_control_mask = data->has_control_mask;
 	}
 
 	sdio_claim_host(func);
@@ -260,6 +262,8 @@
 #define SDIO_DEVICE_ID_MARVELL_8787   (0x9119)
 /* Device ID for SD8797 */
 #define SDIO_DEVICE_ID_MARVELL_8797   (0x9129)
+/* Device ID for SD8897 */
+#define SDIO_DEVICE_ID_MARVELL_8897   (0x912d)
 
 /* WLAN IDs */
 static const struct sdio_device_id mwifiex_ids[] = {
@@ -269,6 +273,8 @@
 		.driver_data = (unsigned long) &mwifiex_sdio_sd8787},
 	{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8797),
 		.driver_data = (unsigned long) &mwifiex_sdio_sd8797},
+	{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, SDIO_DEVICE_ID_MARVELL_8897),
+		.driver_data = (unsigned long) &mwifiex_sdio_sd8897},
 	{},
 };
 
@@ -412,7 +418,40 @@
 }
 
 /*
- * This function initializes the IO ports.
+ * This function is used to initialize IO ports for the
+ * chipsets supporting SDIO new mode eg SD8897.
+ */
+static int mwifiex_init_sdio_new_mode(struct mwifiex_adapter *adapter)
+{
+	u8 reg;
+
+	adapter->ioport = MEM_PORT;
+
+	/* enable sdio new mode */
+	if (mwifiex_read_reg(adapter, CARD_CONFIG_2_1_REG, &reg))
+		return -1;
+	if (mwifiex_write_reg(adapter, CARD_CONFIG_2_1_REG,
+			      reg | CMD53_NEW_MODE))
+		return -1;
+
+	/* Configure cmd port and enable reading rx length from the register */
+	if (mwifiex_read_reg(adapter, CMD_CONFIG_0, &reg))
+		return -1;
+	if (mwifiex_write_reg(adapter, CMD_CONFIG_0, reg | CMD_PORT_RD_LEN_EN))
+		return -1;
+
+	/* Enable Dnld/Upld ready auto reset for cmd port after cmd53 is
+	 * completed
+	 */
+	if (mwifiex_read_reg(adapter, CMD_CONFIG_1, &reg))
+		return -1;
+	if (mwifiex_write_reg(adapter, CMD_CONFIG_1, reg | CMD_PORT_AUTO_EN))
+		return -1;
+
+	return 0;
+}
+
+/* This function initializes the IO ports.
  *
  * The following operations are performed -
  *      - Read the IO ports (0, 1 and 2)
@@ -426,6 +465,12 @@
 
 	adapter->ioport = 0;
 
+	if (card->supports_sdio_new_mode) {
+		if (mwifiex_init_sdio_new_mode(adapter))
+			return -1;
+		goto cont;
+	}
+
 	/* Read the IO port */
 	if (!mwifiex_read_reg(adapter, IO_PORT_0_REG, &reg))
 		adapter->ioport |= (reg & 0xff);
@@ -441,7 +486,7 @@
 		adapter->ioport |= ((reg & 0xff) << 16);
 	else
 		return -1;
-
+cont:
 	pr_debug("info: SDIO FUNC1 IO port: %#x\n", adapter->ioport);
 
 	/* Set Host interrupt reset to read to clear */
@@ -504,10 +549,16 @@
 
 	dev_dbg(adapter->dev, "data: mp_rd_bitmap=0x%08x\n", rd_bitmap);
 
-	if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
-		return -1;
+	if (card->supports_sdio_new_mode) {
+		if (!(rd_bitmap & reg->data_port_mask))
+			return -1;
+	} else {
+		if (!(rd_bitmap & (CTRL_PORT_MASK | reg->data_port_mask)))
+			return -1;
+	}
 
-	if (card->mp_rd_bitmap & CTRL_PORT_MASK) {
+	if ((card->has_control_mask) &&
+	    (card->mp_rd_bitmap & CTRL_PORT_MASK)) {
 		card->mp_rd_bitmap &= (u32) (~CTRL_PORT_MASK);
 		*port = CTRL_PORT;
 		dev_dbg(adapter->dev, "data: port=%d mp_rd_bitmap=0x%08x\n",
@@ -542,24 +593,34 @@
 static int mwifiex_get_wr_port_data(struct mwifiex_adapter *adapter, u32 *port)
 {
 	struct sdio_mmc_card *card = adapter->card;
+	const struct mwifiex_sdio_card_reg *reg = card->reg;
 	u32 wr_bitmap = card->mp_wr_bitmap;
 
 	dev_dbg(adapter->dev, "data: mp_wr_bitmap=0x%08x\n", wr_bitmap);
 
-	if (!(wr_bitmap & card->mp_data_port_mask))
+	if (card->supports_sdio_new_mode &&
+	    !(wr_bitmap & reg->data_port_mask)) {
+		adapter->data_sent = true;
+		return -EBUSY;
+	} else if (!card->supports_sdio_new_mode &&
+		   !(wr_bitmap & card->mp_data_port_mask)) {
 		return -1;
+	}
 
 	if (card->mp_wr_bitmap & (1 << card->curr_wr_port)) {
 		card->mp_wr_bitmap &= (u32) (~(1 << card->curr_wr_port));
 		*port = card->curr_wr_port;
-		if (++card->curr_wr_port == card->mp_end_port)
-			card->curr_wr_port = card->reg->start_wr_port;
+		if (((card->supports_sdio_new_mode) &&
+		     (++card->curr_wr_port == card->max_ports)) ||
+		    ((!card->supports_sdio_new_mode) &&
+		     (++card->curr_wr_port == card->mp_end_port)))
+			card->curr_wr_port = reg->start_wr_port;
 	} else {
 		adapter->data_sent = true;
 		return -EBUSY;
 	}
 
-	if (*port == CTRL_PORT) {
+	if ((card->has_control_mask) && (*port == CTRL_PORT)) {
 		dev_err(adapter->dev,
 			"invalid data port=%d cur port=%d mp_wr_bitmap=0x%08x -> 0x%08x\n",
 			*port, card->curr_wr_port, wr_bitmap,
@@ -904,6 +965,9 @@
 	if (sdio_ireg) {
 		/*
 		 * DN_LD_HOST_INT_STATUS and/or UP_LD_HOST_INT_STATUS
+		 * For SDIO new mode CMD port interrupts
+		 *	DN_LD_CMD_PORT_HOST_INT_STATUS and/or
+		 *	UP_LD_CMD_PORT_HOST_INT_STATUS
 		 * Clear the interrupt status register
 		 */
 		dev_dbg(adapter->dev, "int: sdio_ireg = %#x\n", sdio_ireg);
@@ -1031,7 +1095,7 @@
 	u8 *curr_ptr;
 	u32 rx_len = skb->len;
 
-	if (port == CTRL_PORT) {
+	if ((card->has_control_mask) && (port == CTRL_PORT)) {
 		/* Read the command Resp without aggr */
 		dev_dbg(adapter->dev, "info: %s: no aggregation for cmd "
 			"response\n", __func__);
@@ -1048,7 +1112,10 @@
 		goto rx_curr_single;
 	}
 
-	if (card->mp_rd_bitmap & (~((u32) CTRL_PORT_MASK))) {
+	if ((!card->has_control_mask && (card->mp_rd_bitmap &
+					 card->reg->data_port_mask)) ||
+	    (card->has_control_mask && (card->mp_rd_bitmap &
+					(~((u32) CTRL_PORT_MASK))))) {
 		/* Some more data RX pending */
 		dev_dbg(adapter->dev, "info: %s: not last packet\n", __func__);
 
@@ -1100,8 +1167,25 @@
 		dev_dbg(adapter->dev, "info: do_rx_aggr: num of packets: %d\n",
 			card->mpa_rx.pkt_cnt);
 
-		mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
-			 (card->mpa_rx.ports << 4)) + card->mpa_rx.start_port;
+		if (card->supports_sdio_new_mode) {
+			int i;
+			u32 port_count;
+
+			for (i = 0, port_count = 0; i < card->max_ports; i++)
+				if (card->mpa_rx.ports & BIT(i))
+					port_count++;
+
+			/* Reading data from "start_port + 0" to "start_port +
+			 * port_count -1", so decrease the count by 1
+			 */
+			port_count--;
+			mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+				 (port_count << 8)) + card->mpa_rx.start_port;
+		} else {
+			mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+				 (card->mpa_rx.ports << 4)) +
+				 card->mpa_rx.start_port;
+		}
 
 		if (mwifiex_read_data_sync(adapter, card->mpa_rx.buf,
 					   card->mpa_rx.buf_len, mport, 1))
@@ -1200,6 +1284,8 @@
 	u32 rx_blocks;
 	u16 rx_len;
 	unsigned long flags;
+	u32 bitmap;
+	u8 cr;
 
 	spin_lock_irqsave(&adapter->int_lock, flags);
 	sdio_ireg = adapter->int_status;
@@ -1209,12 +1295,60 @@
 	if (!sdio_ireg)
 		return ret;
 
+	/* Following interrupt is only for SDIO new mode */
+	if (sdio_ireg & DN_LD_CMD_PORT_HOST_INT_STATUS && adapter->cmd_sent)
+		adapter->cmd_sent = false;
+
+	/* Following interrupt is only for SDIO new mode */
+	if (sdio_ireg & UP_LD_CMD_PORT_HOST_INT_STATUS) {
+		u32 pkt_type;
+
+		/* read the len of control packet */
+		rx_len = card->mp_regs[CMD_RD_LEN_1] << 8;
+		rx_len |= (u16) card->mp_regs[CMD_RD_LEN_0];
+		rx_blocks = DIV_ROUND_UP(rx_len, MWIFIEX_SDIO_BLOCK_SIZE);
+		if (rx_len <= INTF_HEADER_LEN ||
+		    (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE) >
+		     MWIFIEX_RX_DATA_BUF_SIZE)
+			return -1;
+		rx_len = (u16) (rx_blocks * MWIFIEX_SDIO_BLOCK_SIZE);
+
+		skb = dev_alloc_skb(rx_len);
+		if (!skb)
+			return -1;
+
+		skb_put(skb, rx_len);
+
+		if (mwifiex_sdio_card_to_host(adapter, &pkt_type, skb->data,
+					      skb->len, adapter->ioport |
+							CMD_PORT_SLCT)) {
+			dev_err(adapter->dev,
+				"%s: failed to card_to_host", __func__);
+			dev_kfree_skb_any(skb);
+			goto term_cmd;
+		}
+
+		if ((pkt_type != MWIFIEX_TYPE_CMD) &&
+		    (pkt_type != MWIFIEX_TYPE_EVENT))
+			dev_err(adapter->dev,
+				"%s:Received wrong packet on cmd port",
+				__func__);
+
+		mwifiex_decode_rx_packet(adapter, skb, pkt_type);
+	}
+
 	if (sdio_ireg & DN_LD_HOST_INT_STATUS) {
-		card->mp_wr_bitmap =
-			((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
-		card->mp_wr_bitmap |=
-			(u32) card->mp_regs[reg->wr_bitmap_l];
-		dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%08x\n",
+		bitmap = (u32) card->mp_regs[reg->wr_bitmap_l];
+		bitmap |= ((u32) card->mp_regs[reg->wr_bitmap_u]) << 8;
+		if (card->supports_sdio_new_mode) {
+			bitmap |=
+				((u32) card->mp_regs[reg->wr_bitmap_1l]) << 16;
+			bitmap |=
+				((u32) card->mp_regs[reg->wr_bitmap_1u]) << 24;
+		}
+		card->mp_wr_bitmap = bitmap;
+
+		dev_dbg(adapter->dev, "int: DNLD: wr_bitmap=0x%x\n",
 			card->mp_wr_bitmap);
 		if (adapter->data_sent &&
 		    (card->mp_wr_bitmap & card->mp_data_port_mask)) {
@@ -1227,7 +1361,7 @@
 	/* As firmware will not generate download ready interrupt if the port
 	   updated is command port only, cmd_sent should be done for any SDIO
 	   interrupt. */
-	if (adapter->cmd_sent) {
+	if (card->has_control_mask && adapter->cmd_sent) {
 		/* Check if firmware has attach buffer at command port and
 		   update just that in wr_bit_map. */
 		card->mp_wr_bitmap |=
@@ -1239,10 +1373,16 @@
 	dev_dbg(adapter->dev, "info: cmd_sent=%d data_sent=%d\n",
 		adapter->cmd_sent, adapter->data_sent);
 	if (sdio_ireg & UP_LD_HOST_INT_STATUS) {
-		card->mp_rd_bitmap =
-			((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
-		card->mp_rd_bitmap |= (u32) card->mp_regs[reg->rd_bitmap_l];
-		dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%08x\n",
+		bitmap = (u32) card->mp_regs[reg->rd_bitmap_l];
+		bitmap |= ((u32) card->mp_regs[reg->rd_bitmap_u]) << 8;
+		if (card->supports_sdio_new_mode) {
+			bitmap |=
+				((u32) card->mp_regs[reg->rd_bitmap_1l]) << 16;
+			bitmap |=
+				((u32) card->mp_regs[reg->rd_bitmap_1u]) << 24;
+		}
+		card->mp_rd_bitmap = bitmap;
+		dev_dbg(adapter->dev, "int: UPLD: rd_bitmap=0x%x\n",
 			card->mp_rd_bitmap);
 
 		while (true) {
@@ -1285,37 +1425,33 @@
 
 			if (mwifiex_sdio_card_to_host_mp_aggr(adapter, skb,
 							      port)) {
-				u8 cr = 0;
-
 				dev_err(adapter->dev, "card_to_host_mpa failed:"
 					" int status=%#x\n", sdio_ireg);
-				if (mwifiex_read_reg(adapter,
-						     CONFIGURATION_REG, &cr))
-					dev_err(adapter->dev,
-						"read CFG reg failed\n");
-
-				dev_dbg(adapter->dev,
-					"info: CFG reg val = %d\n", cr);
-				if (mwifiex_write_reg(adapter,
-						      CONFIGURATION_REG,
-						      (cr | 0x04)))
-					dev_err(adapter->dev,
-						"write CFG reg failed\n");
-
-				dev_dbg(adapter->dev, "info: write success\n");
-				if (mwifiex_read_reg(adapter,
-						     CONFIGURATION_REG, &cr))
-					dev_err(adapter->dev,
-						"read CFG reg failed\n");
-
-				dev_dbg(adapter->dev,
-					"info: CFG reg val =%x\n", cr);
-				return -1;
+				goto term_cmd;
 			}
 		}
 	}
 
 	return 0;
+
+term_cmd:
+	/* terminate cmd */
+	if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+		dev_err(adapter->dev, "read CFG reg failed\n");
+	else
+		dev_dbg(adapter->dev, "info: CFG reg val = %d\n", cr);
+
+	if (mwifiex_write_reg(adapter, CONFIGURATION_REG, (cr | 0x04)))
+		dev_err(adapter->dev, "write CFG reg failed\n");
+	else
+		dev_dbg(adapter->dev, "info: write success\n");
+
+	if (mwifiex_read_reg(adapter, CONFIGURATION_REG, &cr))
+		dev_err(adapter->dev, "read CFG reg failed\n");
+	else
+		dev_dbg(adapter->dev, "info: CFG reg val =%x\n", cr);
+
+	return -1;
 }
 
 /*
@@ -1344,7 +1480,9 @@
 	s32 f_postcopy_cur_buf = 0;
 	u32 mport;
 
-	if ((!card->mpa_tx.enabled) || (port == CTRL_PORT)) {
+	if (!card->mpa_tx.enabled ||
+	    (card->has_control_mask && (port == CTRL_PORT)) ||
+	    (card->supports_sdio_new_mode && (port == CMD_PORT_SLCT))) {
 		dev_dbg(adapter->dev, "info: %s: tx aggregation disabled\n",
 			__func__);
 
@@ -1419,8 +1557,26 @@
 		dev_dbg(adapter->dev, "data: %s: send aggr buffer: %d %d\n",
 			__func__,
 				card->mpa_tx.start_port, card->mpa_tx.ports);
-		mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
-			 (card->mpa_tx.ports << 4)) + card->mpa_tx.start_port;
+		if (card->supports_sdio_new_mode) {
+			u32 port_count;
+			int i;
+
+			for (i = 0, port_count = 0; i < card->max_ports; i++)
+				if (card->mpa_tx.ports & BIT(i))
+					port_count++;
+
+			/* Writing data from "start_port + 0" to "start_port +
+			 * port_count -1", so decrease the count by 1
+			 */
+			port_count--;
+			mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+				 (port_count << 8)) + card->mpa_tx.start_port;
+		} else {
+			mport = (adapter->ioport | SDIO_MPA_ADDR_BASE |
+				 (card->mpa_tx.ports << 4)) +
+				 card->mpa_tx.start_port;
+		}
+
 		ret = mwifiex_write_data_to_card(adapter, card->mpa_tx.buf,
 						 card->mpa_tx.buf_len, mport);
 
@@ -1493,6 +1649,9 @@
 		    pkt_len > MWIFIEX_UPLD_SIZE)
 			dev_err(adapter->dev, "%s: payload=%p, nb=%d\n",
 				__func__, payload, pkt_len);
+
+		if (card->supports_sdio_new_mode)
+			port = CMD_PORT_SLCT;
 	}
 
 	/* Transfer data to card */
@@ -1748,8 +1907,11 @@
 
 	card->mp_data_port_mask = reg->data_port_mask;
 
-	for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
-		card->mp_data_port_mask &= ~(1 << (card->max_ports - i));
+	if (reg->start_wr_port) {
+		for (i = 1; i <= card->max_ports - card->mp_end_port; i++)
+			card->mp_data_port_mask &=
+					~(1 << (card->max_ports - i));
+	}
 
 	card->curr_wr_port = reg->start_wr_port;
 
@@ -1857,3 +2019,4 @@
 MODULE_FIRMWARE(SD8786_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8787_DEFAULT_FW_NAME);
 MODULE_FIRMWARE(SD8797_DEFAULT_FW_NAME);
+MODULE_FIRMWARE(SD8897_DEFAULT_FW_NAME);