aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax_cmc/hardware.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wimax_cmc/hardware.c')
-rwxr-xr-xdrivers/net/wimax_cmc/hardware.c599
1 files changed, 599 insertions, 0 deletions
diff --git a/drivers/net/wimax_cmc/hardware.c b/drivers/net/wimax_cmc/hardware.c
new file mode 100755
index 0000000..7540584
--- /dev/null
+++ b/drivers/net/wimax_cmc/hardware.c
@@ -0,0 +1,599 @@
+/*
+ * hardware.c
+ *
+ * gpio control functions (power on/off, init/deinit gpios)
+ * data tx and tx thread implemented.
+ */
+#include "headers.h"
+#include "download.h"
+#include "wimax_plat.h"
+
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+#include <mach/hardware.h>
+#include <plat/sdhci.h>
+#include <plat/devs.h>
+#include <linux/spinlock.h>
+
+#define WIMAX_POWER_SUCCESS 0
+#define WIMAX_ALREADY_POWER_ON -1
+#define WIMAX_PROBE_FAIL -2
+#define WIMAX_ALREADY_POWER_OFF -3
+
+
+static void wimax_hostwake_task(unsigned long data)
+{
+ struct net_adapter *adapter = (struct net_adapter *)data;
+ struct wimax_cfg *g_cfg = adapter->pdata->g_cfg;
+
+ wake_lock_timeout(&g_cfg->wimax_wake_lock, 1 * HZ);
+
+}
+
+static irqreturn_t wimax_hostwake_isr(int irq, void *dev)
+{
+ struct net_adapter *adapter = (struct net_adapter *)dev;
+ tasklet_schedule(&adapter->hostwake_task);
+ return IRQ_HANDLED;
+}
+static int cmc732_setup_wake_irq(struct net_adapter *adapter)
+{
+ int rc = -EIO;
+ int irq;
+
+ rc = gpio_request(WIMAX_INT, "gpio_wimax_int");
+ if (rc < 0) {
+ dump_debug("%s: gpio %d request failed (%d)\n",
+ __func__, WIMAX_INT, rc);
+ return rc;
+ }
+
+ rc = gpio_direction_input(WIMAX_INT);
+ if (rc < 0) {
+ dump_debug("%s: failed to set gpio %d as input (%d)\n",
+ __func__, WIMAX_INT, rc);
+ goto err_gpio_direction_input;
+ }
+
+ irq = gpio_to_irq(WIMAX_INT);
+
+ rc = request_irq(irq, wimax_hostwake_isr, IRQF_TRIGGER_FALLING,
+ "wimax_int", adapter);
+ if (rc < 0) {
+ dump_debug("%s: request_irq(%d) failed for gpio %d (%d)\n",
+ __func__, irq,
+ WIMAX_INT, rc);
+ goto err_request_irq;
+ }
+
+ rc = enable_irq_wake(irq);
+
+ if (rc < 0) {
+ dump_debug("%s: enable_irq_wake(%d) failed for gpio %d (%d)\n",
+ __func__, irq,
+ WIMAX_INT, rc);
+ goto err_enable_irq_wake;
+ }
+
+ adapter->wake_irq = irq;
+
+ tasklet_init(&adapter->hostwake_task,
+ wimax_hostwake_task, (unsigned long)adapter);
+
+ goto done;
+err_enable_irq_wake:
+ free_irq(irq, adapter);
+err_request_irq:
+err_gpio_direction_input:
+ gpio_free(WIMAX_INT);
+done:
+ return rc;
+
+}
+void cmc732_release_wake_irq(struct net_adapter *adapter)
+{
+ if (adapter->wake_irq) {
+ disable_irq_wake(adapter->wake_irq);
+ free_irq(adapter->wake_irq, adapter);
+ gpio_free(WIMAX_INT);
+ tasklet_kill(&adapter->hostwake_task);
+ }
+}
+
+
+
+int hw_start(struct net_adapter *adapter)
+{
+ struct wimax_cfg *g_cfg = adapter->pdata->g_cfg;
+
+ if (load_wimax_image(g_cfg->wimax_mode))
+ return STATUS_UNSUCCESSFUL;
+
+ adapter->download_complete = FALSE;
+
+ if (adapter->downloading) {
+ sdio_claim_host(adapter->func);
+ send_cmd_packet(adapter, MSG_DRIVER_OK_REQ);
+ sdio_release_host(adapter->func);
+ switch (wait_event_interruptible_timeout
+ (adapter->download_event,
+ (adapter->download_complete == TRUE),
+ msecs_to_jiffies(FWDOWNLOAD_TIMEOUT))) {
+ case 0:
+ /* timeout */
+ dump_debug("Error hw_start :"
+ "F/W Download timeout failed");
+ adapter->halted = TRUE;
+ return STATUS_UNSUCCESSFUL;
+ case -ERESTARTSYS:
+ /* Interrupted by signal */
+ dump_debug("Error hw_start : -ERESTARTSYS retry");
+ return STATUS_UNSUCCESSFUL;
+ default:
+ /* normal condition check */
+ if (adapter->removed == TRUE
+ || adapter->halted == TRUE) {
+ dump_debug("Error hw_start : "
+ " F/W Download surprise removed");
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ /*Setup hostwake interrupt*/
+
+ if (cmc732_setup_wake_irq(adapter) < 0)
+ dump_debug("hw_start : "
+ " Error setting up wimax_int");
+
+
+ break;
+ }
+ adapter->downloading = FALSE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+int hw_stop(struct net_adapter *adapter)
+{
+ adapter->halted = TRUE;
+
+
+ /* Stop Sdio Interface */
+ sdio_claim_host(adapter->func);
+ sdio_release_irq(adapter->func);
+ sdio_disable_func(adapter->func);
+ sdio_release_host(adapter->func);
+
+ /*Remove wakeup interrupt*/
+ cmc732_release_wake_irq(adapter);
+
+ return STATUS_SUCCESS;
+}
+
+int hw_init(struct net_adapter *adapter)
+{
+
+ /* set WIMAX_WAKEUP & WIMAX_IF_MODE0 */
+ adapter->pdata->set_mode();
+
+ /* initilize hardware info structure */
+ memset(&adapter->hw, 0x0, sizeof(struct hardware_info));
+
+ /* allocate sdio receive buffer once */
+ if (adapter->hw.receive_buffer == NULL) {
+ dump_debug("Alloc ReceiveBuffer");
+ /* the extra 8 bytes space required to copy ethernet header */
+ adapter->hw.receive_buffer = kmalloc(SDIO_BUFFER_SIZE + 8,
+ GFP_ATOMIC | GFP_DMA);
+ if (adapter->hw.receive_buffer == NULL) {
+ dump_debug("kmalloc fail!!");
+ return -ENOMEM;
+ }
+ }
+
+ /* initialize sdio receive buffer */
+ memset(adapter->hw.receive_buffer, 0x0, SDIO_BUFFER_SIZE + 8);
+
+ /* For sending data and control packets */
+ queue_init_list(adapter->hw.q_send.head);
+ spin_lock_init(&adapter->hw.q_send.lock);
+
+ init_waitqueue_head(&adapter->download_event);
+
+ return STATUS_SUCCESS;
+}
+
+void hw_remove(struct net_adapter *adapter)
+{
+ struct buffer_descriptor *dsc;
+
+ /* Free the pending data packets and control packets */
+ spin_lock(&adapter->hw.q_send.lock);
+ while (!queue_empty(adapter->hw.q_send.head)) {
+ dump_debug("Freeing q_send");
+ dsc = (struct buffer_descriptor *)
+ queue_get_head(adapter->hw.q_send.head);
+ if (!dsc) {
+ dump_debug("Fail...node is null");
+ continue;
+ }
+ queue_remove_head(adapter->hw.q_send.head);
+ kfree(dsc->buffer);
+ kfree(dsc);
+ }
+ spin_unlock(&adapter->hw.q_send.lock);
+
+}
+
+int con0_poll_thread(void *data)
+{
+ struct net_adapter *adapter = (struct net_adapter *)data;
+ struct wimax_cfg *g_cfg = adapter->pdata->g_cfg;
+ int prev_val = 0;
+ int curr_val = 0;
+
+ wake_lock(&g_cfg->wimax_tx_lock);
+
+ while ((!adapter->halted)) {
+ curr_val = gpio_get_value(GPIO_WIMAX_CON0);
+ if ((prev_val && (!curr_val)) || (curr_val == GPIO_LEVEL_LOW)) {
+ adapter->pdata->restore_uart_path();
+ break;
+ }
+ prev_val = curr_val;
+ msleep(40);
+ }
+ wake_unlock(&g_cfg->wimax_tx_lock);
+ do_exit(0);
+ return 0;
+}
+
+
+
+
+
+/* get MAC address from device */
+void hw_get_mac_address(void *data)
+{
+ struct net_adapter *adapter = (struct net_adapter *)data;
+ struct hw_private_packet req;
+ int nResult = 0;
+ int retry = 5;
+ req.id0 = 'W';
+ req.id1 = 'P';
+ req.code = HwCodeMacRequest;
+ req.value = 0;
+ do {
+ if (adapter == NULL)
+ break;
+
+ if (retry == 2) //odb backup takes 5.8sec
+ msleep(6000);
+
+ sdio_claim_host(adapter->func);
+ nResult = sd_send(adapter, (u_char *)&req,
+ sizeof(struct hw_private_packet));
+ sdio_release_host(adapter->func);
+
+ if (nResult != STATUS_SUCCESS)
+ dump_debug("hw_get_mac_address: sd_send fail!!");
+ msleep(300);
+ retry--;
+ /*in case we dont get MAC we need
+ to release power lock and probe finsh */
+ if (!retry) {
+ adapter->download_complete = TRUE;
+ wake_up_interruptible(&adapter->download_event);
+ msleep(100);
+
+ }
+ } while ((!adapter->mac_ready) && (!adapter->halted) && retry);
+
+ adapter->pdata->g_cfg->powerup_done = true ;
+ dump_debug("MAC thread exit");
+ return;
+}
+
+u_int hw_send_data(struct net_adapter *adapter,
+ void *buffer , u_long length, bool control)
+{
+ struct buffer_descriptor *dsc;
+ struct hw_packet_header *hdr;
+ struct net_device *net = adapter->net;
+ u_char *ptr;
+ unsigned long flags ;
+ struct wimax_cfg *g_cfg = adapter->pdata->g_cfg;
+
+ spin_lock_irqsave(&adapter->hw.q_send.lock, flags);
+
+ dsc = (struct buffer_descriptor *)kmalloc(
+ sizeof(struct buffer_descriptor), GFP_ATOMIC | GFP_DMA);
+ if (dsc == NULL)
+ return STATUS_RESOURCES;
+
+ dsc->buffer = kmalloc(BUFFER_DATA_SIZE , GFP_ATOMIC | GFP_DMA);
+ if (dsc->buffer == NULL) {
+ kfree(dsc);
+ return STATUS_RESOURCES;
+ }
+
+ ptr = dsc->buffer;
+
+ /* shift data pointer */
+ ptr += sizeof(struct hw_packet_header);
+#ifdef HARDWARE_USE_ALIGN_HEADER
+ ptr += 2;
+#endif
+ hdr = (struct hw_packet_header *)dsc->buffer;
+
+ if (control) {
+ memcpy(ptr, buffer + (ETHERNET_ADDRESS_LENGTH * 2),
+ length - (ETHERNET_ADDRESS_LENGTH * 2));
+
+ /* add packet header */
+ hdr->id0 = 'W';
+ hdr->id1 = 'C';
+ hdr->length = (u_short)length - (ETHERNET_ADDRESS_LENGTH * 2);
+
+ /* set length */
+ dsc->length = (u_short)length - (ETHERNET_ADDRESS_LENGTH * 2)
+ + sizeof(struct hw_packet_header);
+ #ifdef HARDWARE_USE_ALIGN_HEADER
+ dsc->length += 2;
+ #endif
+
+ /* dump control packet for debug */
+ if (g_cfg->enable_dump_msg == 1)
+ dump_buffer("Control Tx",
+ (u_char *)dsc->buffer + 6, dsc->length - 6);
+ } else {
+
+ length -= (ETHERNET_ADDRESS_LENGTH * 2);
+ buffer += (ETHERNET_ADDRESS_LENGTH * 2);
+
+ memcpy(ptr, buffer, length);
+
+ hdr->id0 = 'W';
+ hdr->id1 = 'D';
+ hdr->length = (u_short)length;
+
+ dsc->length = length + sizeof(struct hw_packet_header);
+ #ifdef HARDWARE_USE_ALIGN_HEADER
+ dsc->length += 2;
+ #endif
+ adapter->netstats.tx_packets++;
+ adapter->netstats.tx_bytes += dsc->length;
+
+ /* add statistics */
+ if (!netif_running(net))
+ dump_debug("!netif_running");
+
+ }
+
+
+ queue_put_tail(adapter->hw.q_send.head, dsc->node);
+ spin_unlock_irqrestore(&adapter->hw.q_send.lock, flags);
+
+ queue_work(adapter->wimax_workqueue, &adapter->transmit_work);
+ return STATUS_SUCCESS;
+}
+
+u_int sd_send_data(struct net_adapter *adapter, struct buffer_descriptor *dsc)
+{
+ int nRet = 0;
+ int nWriteIdx;
+ dsc->length += (dsc->length & 1) ? 1 : 0;
+
+#ifdef HARDWARE_USE_ALIGN_HEADER
+ if (dsc->length > SDIO_MAX_BYTE_SIZE)
+ dsc->length = (dsc->length + SDIO_MAX_BYTE_SIZE)
+ & ~(SDIO_MAX_BYTE_SIZE);
+#endif
+
+ if (adapter->halted) {
+ dump_debug("Halted Already");
+ return STATUS_UNSUCCESSFUL;
+ }
+ hwSdioWriteBankIndex(adapter, &nWriteIdx, &nRet);
+
+ if (nRet || (nWriteIdx < 0)) {
+ dump_debug("sd_send_data : "
+ " error fetch bank index!! nRet = %d", nRet);
+ return STATUS_UNSUCCESSFUL;
+ }
+
+ sdio_writeb(adapter->func, (nWriteIdx + 1) % 15,
+ SDIO_H2C_WP_REG, NULL);
+
+ nRet = sdio_memcpy_toio(adapter->func,
+ SDIO_TX_BANK_ADDR+(SDIO_BANK_SIZE * nWriteIdx)+4,
+ dsc->buffer, dsc->length);
+
+ if (nRet < 0)
+ dump_debug("sd_send_data :"
+ " error writing dsc packet!! nRet = %d", nRet);
+ nRet = sdio_memcpy_toio(adapter->func,
+ SDIO_TX_BANK_ADDR + (SDIO_BANK_SIZE * nWriteIdx),
+ &(dsc->length), 4);
+
+ if (nRet < 0)
+ dump_debug("sd_send_data :"
+ "error writing bank length info!! nRet = %d", nRet);
+ return nRet;
+}
+
+/* Return packet for packet buffer freeing */
+void hw_return_packet(struct net_adapter *adapter, u_short type)
+{
+ struct buffer_descriptor *curElem;
+ struct buffer_descriptor *prevElem = NULL;
+
+ if (queue_empty(adapter->ctl.q_received.head))
+ return;
+
+ /* first time get head needed to get the dsc nodes */
+ curElem = (struct buffer_descriptor *)
+ queue_get_head(adapter->ctl.q_received.head);
+
+ for ( ; curElem != NULL; prevElem = curElem,
+ curElem = (struct buffer_descriptor *)curElem->node.next) {
+ if (curElem->type == type) {
+ /* process found*/
+ if (prevElem == NULL) {
+ /* First node or only
+ one node present to delete */
+ adapter->ctl.q_received.head.next =
+ ((struct list_head *)curElem)->next;
+ if (!((adapter->ctl.q_received.head).next)) {
+ /* rechain list pointer to next link */
+ /* if the list pointer is null,
+ null out the reverse link */
+ (adapter->ctl.q_received.head).prev =
+ NULL;
+ }
+ } else if (((struct list_head *)curElem)->next
+ == NULL) {
+ /* last node */
+ ((struct list_head *)prevElem)->next = NULL;
+ (adapter->ctl.q_received.head).prev =
+ (struct list_head *)(&prevElem);
+ } else {
+ /* middle node */
+ ((struct list_head *)prevElem)->next =
+ ((struct list_head *)curElem)->next;
+ }
+
+ kfree(curElem->buffer);
+ kfree(curElem);
+ break;
+ }
+ }
+}
+
+int hw_device_wakeup(struct net_adapter *adapter)
+{
+ int rc = 0;
+
+ adapter->pdata->wakeup_assert(1);
+
+ while (!adapter->pdata->is_modem_awake()) {
+ if (rc == 0)
+ dump_debug("hw_device_wakeup (CON0 status):"
+ " waiting for modem awake");
+ rc++;
+ if (rc > WAKEUP_MAX_TRY) {
+ dump_debug("hw_device_wakeup (CON0 status):"
+ " modem wake up time out!!");
+ return -1;
+ }
+ msleep(WAKEUP_TIMEOUT/2);
+ adapter->pdata->wakeup_assert(0);
+ msleep(WAKEUP_TIMEOUT/2);
+ adapter->pdata->wakeup_assert(1);
+ s3c_bat_use_wimax(1);
+ }
+ if (rc != 0)
+ dump_debug("hw_device_wakeup (CON0 status): modem awake");
+ adapter->pdata->wakeup_assert(0);
+
+ return 0;
+}
+
+/*
+This Work is responsible for Transmiting Both Control And Data packet
+*/
+
+void hw_transmit_thread(struct work_struct *work)
+{
+ struct buffer_descriptor *dsc;
+ struct hw_private_packet hdr;
+ struct net_adapter *adapter;
+ int nRet = 0;
+ int modem_reset = false;
+ struct wimax_cfg *g_cfg;
+
+ adapter = container_of(work, struct net_adapter, transmit_work);
+ g_cfg = adapter->pdata->g_cfg;
+ wake_lock(&g_cfg->wimax_tx_lock);
+
+ if (!gpio_get_value(WIMAX_EN)) {
+ dump_debug("WiMAX Power OFF!! (TX)");
+ goto exit;
+ }
+
+ mutex_lock(&adapter->rx_lock);
+
+ while (!queue_empty(adapter->hw.q_send.head)) {
+ if (adapter->halted) {
+ /* send stop message */
+ hdr.id0 = 'W';
+ hdr.id1 = 'P';
+ hdr.code = HwCodeHaltedIndication;
+ hdr.value = 0;
+
+ if (sd_send(adapter, (unsigned char *)&hdr,
+ sizeof(struct hw_private_packet)))
+ dump_debug("halted,"
+ " send HaltIndication to FW err");
+ modem_reset = true;
+ break;
+ }
+
+ if (!g_cfg->modem_reset_flag) {
+ dump_debug("modem_reset_flag is not set");
+ break;
+ }
+
+ if(hw_device_wakeup(adapter)) {
+ modem_reset = true;
+ break;
+ }
+
+ dsc = (struct buffer_descriptor *)
+ queue_get_head(adapter->hw.q_send.head);
+
+ if (!dsc->buffer) {
+ dump_debug("dsc->buffer is NULL");
+ break;
+ }
+
+ if (!dsc) {
+ dump_debug("Fail...node is null");
+ break;
+ }
+
+ sdio_claim_host(adapter->func);
+ nRet = sd_send_data(adapter, dsc);
+ sdio_release_host(adapter->func);
+ queue_remove_head(adapter->hw.q_send.head);
+ kfree(dsc->buffer);
+ kfree(dsc);
+ if (nRet != STATUS_SUCCESS) {
+ sdio_claim_host(adapter->func);
+ sdio_release_irq(adapter->func);
+ sdio_release_host(adapter->func);
+ ++adapter->XmitErr;
+ dump_debug("SendData Fail******");
+ if (nRet == -ENOMEDIUM || nRet == /*-ETIMEOUT*/-110) {
+ dump_debug("%s: No medium or timeout error", __func__);
+ // adapter->halted = TRUE;
+ }
+ modem_reset = true;
+ break;
+ }
+ }
+
+ if(modem_reset) {
+ while (!g_cfg->modem_reset_flag) {
+ dump_debug("Waiting for PM_POST_SUSPEND notifier");
+ msleep(100);
+ }
+ adapter->pdata->power(0);
+ dump_debug("Modem reset done");
+ }
+
+ mutex_unlock(&adapter->rx_lock);
+exit:
+ wake_unlock(&g_cfg->wimax_tx_lock);
+ return ;
+}