aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wimax_cmc
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wimax_cmc')
-rw-r--r--drivers/net/wimax_cmc/Kconfig7
-rw-r--r--drivers/net/wimax_cmc/Makefile4
-rw-r--r--drivers/net/wimax_cmc/firmware.c247
-rw-r--r--drivers/net/wimax_cmc/firmware.h29
-rw-r--r--drivers/net/wimax_cmc/wimax_i2c.c854
-rw-r--r--drivers/net/wimax_cmc/wimax_i2c.h53
-rw-r--r--drivers/net/wimax_cmc/wimax_sdio.c1789
-rw-r--r--drivers/net/wimax_cmc/wimax_sdio.h259
8 files changed, 3242 insertions, 0 deletions
diff --git a/drivers/net/wimax_cmc/Kconfig b/drivers/net/wimax_cmc/Kconfig
new file mode 100644
index 0000000..16efe94
--- /dev/null
+++ b/drivers/net/wimax_cmc/Kconfig
@@ -0,0 +1,7 @@
+comment "Enable MMC support to see CMC7xx driver"
+ depends on MMC = n
+
+config WIMAX_CMC7XX_DEBUG
+ bool "Samsung WiMax CMC7xx debug log"
+ depends on WIMAX_CMC
+ default n
diff --git a/drivers/net/wimax_cmc/Makefile b/drivers/net/wimax_cmc/Makefile
new file mode 100644
index 0000000..e49f7fb
--- /dev/null
+++ b/drivers/net/wimax_cmc/Makefile
@@ -0,0 +1,4 @@
+ifeq ($(CONFIG_WIMAX_CMC7XX_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+obj-$(CONFIG_WIMAX_CMC) += wimax_sdio.o firmware.o wimax_i2c.o
diff --git a/drivers/net/wimax_cmc/firmware.c b/drivers/net/wimax_cmc/firmware.c
new file mode 100644
index 0000000..d31eb8c
--- /dev/null
+++ b/drivers/net/wimax_cmc/firmware.c
@@ -0,0 +1,247 @@
+/*
+ * firmware.c
+ *
+ * functions for Linux filesystem access
+ * Firmware binary file is on the filesystem, read fild and send it through SDIO
+ */
+#include "firmware.h"
+#include <linux/wimax/samsung/wimax732.h>
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+#define WIMAX_BAT_SYSPATH \
+ "/sys/devices/platform/sec-battery/power_supply/battery/wimax"
+
+/******************************************************************************
+ * Library Functions
+ ******************************************************************************/
+
+/*!
+ *************************************************************************
+ * \brief file open.
+ *
+ * user context.
+ *
+ * \return file pointer, if error, return NULL.
+ *************************************************************************/
+struct file *klib_fopen(
+ const char *filename, /*!< filename to open */
+ /*!< O_RDONLY, O_WRONLY, O_RDWR, O_CREAT,
+ O_EXCL, O_TRUNC, O_APPEND, O_NONBLOCK, O_SYNC,...*/
+ int flags,
+ /*!< file creation permisstion. S_IRxxx
+ S_IWxxx S_IXxxx (xxx=USR,GRP,OTH), S_IRWXx(x=U,G,O)*/
+ int mode
+ )
+{
+ struct file *filp = filp_open(filename, flags, mode);
+ return (IS_ERR(filp)) ? NULL : filp;
+}
+
+/*!
+ *************************************************************************
+ * \brief file close.
+ *
+ * user context.
+ *
+ * \return none.
+ *************************************************************************/
+void klib_fclose(
+ struct file *filp /*!< file pointer */
+ )
+{
+ if (filp)
+ fput(filp);
+}
+
+/*!
+ *************************************************************************
+ * \brief move file pointer to the request.
+ *
+ * user context
+ * do not support SEEK_END
+ * no boundary check (file position may exceed file size).
+ *
+ * \return .
+ *************************************************************************/
+int klib_fseek(
+ struct file *filp, /*!< file pointer */
+ int offset, /*!< */
+ int whence /*!< SEEK_SET, SEEK_CUR */
+ )
+{
+ int pos = filp->f_pos;
+
+ if (filp) {
+ if (whence == SEEK_SET)
+ pos = offset;
+
+ else if (whence == SEEK_CUR)
+ pos += offset;
+
+ if (pos < 0)
+ pos = 0;
+
+ filp->f_pos = pos;
+ return pos;
+ } else
+ return -ENOENT;
+}
+
+
+
+/*!
+ *************************************************************************
+ * \brief file read function.
+ *
+ * user context.
+ *
+ * \return actually read number, 0 = EOF, negative = error.
+ *************************************************************************/
+int
+klib_fread(
+ char *buf, /*!< buffer to read into */
+ int len, /*!< number of bytes to read */
+ struct file *filp /*!< file pointer */
+ )
+{
+ int readlen;
+ mm_segment_t oldfs;
+
+ if (filp == NULL) {
+ printk(KERN_INFO "filp == NULL\n");
+ return -ENOENT;
+ }
+
+ if (filp->f_op->read == NULL) {
+ printk(KERN_INFO "filp->f_op->read == NULL\n");
+ return -ENOSYS;
+ }
+
+ if (((filp->f_flags & O_ACCMODE) & O_RDONLY) != 0) {
+ printk(KERN_INFO "((filp->f_flags & O_ACCMODE) & O_RDONLY) != 0\n");
+ return -EACCES;
+ }
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ readlen = filp->f_op->read(filp, buf, len, &filp->f_pos);
+ set_fs(oldfs);
+
+ return readlen;
+}
+
+int klib_flen_fcopy(char *buf, int len, struct file *filp)
+{
+ int readlen;
+ mm_segment_t oldfs;
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ readlen = filp->f_op->read(filp, buf, len, &filp->f_pos);
+ set_fs(oldfs);
+
+ return readlen;
+}
+
+/*!
+ *************************************************************************
+ * \brief .
+ *
+ * user context.
+ *
+ * \return read character, EOF if end of file.
+ *************************************************************************/
+int klib_fgetc(
+ struct file *filp /*!< file pointer */
+ )
+{
+ int len;
+ u_char buf[4];
+
+ len = klib_fread((char *)buf, 1, filp);
+ if (len > 0)
+ return buf[0];
+ else if (len == 0)
+ return -1;
+ else
+ return len;
+}
+
+/*!
+ *************************************************************************
+ * \brief to get the length of file.
+ *
+ * detailed decrtiption.
+ *
+ * \return .
+ *************************************************************************/
+int klib_flength(
+ struct file *filp /*!< file pointer */
+ )
+{
+ int total_len = 0;
+ int buf;
+
+ do {
+ buf = klib_fgetc(filp);
+ if (buf == -1)
+ break;
+ total_len++;
+ } while (1);
+
+ klib_fseek(filp, 0, SEEK_SET);
+
+ return total_len;
+}
+
+/*!
+ *************************************************************************
+ * \brief file write function.
+ *
+ * user context.
+ *
+ * \return actually write number, 0 = EOF, negative = error.
+ *************************************************************************/
+int klib_fwrite(
+ char *buf, /*!< buffer to write into */
+ int len, /*!< number of bytes to write */
+ struct file *filp /*!< file pointer */
+ )
+{
+ int writelen;
+ mm_segment_t oldfs;
+
+ if (filp == NULL) {
+ printk(KERN_INFO "filp == NULL\n");
+ return -ENOENT;
+ }
+
+ if (filp->f_op->write == NULL) {
+ printk(KERN_INFO "filp->f_op->write == NULL\n");
+ return -ENOSYS;
+ }
+
+ oldfs = get_fs();
+ set_fs(KERNEL_DS);
+ writelen = filp->f_op->write(filp, buf, len, &filp->f_pos);
+ set_fs(oldfs);
+
+ return writelen;
+}
+
+void s3c_bat_use_wimax(int onoff)
+{
+ struct file *fp;
+ fp = klib_fopen(WIMAX_BAT_SYSPATH, O_RDWR, 0);
+
+ if (!fp)
+ pr_err("open fail");
+ if (onoff)
+ klib_fwrite("1", 1, fp);
+ else
+ klib_fwrite("0", 1, fp);
+ klib_fclose(fp);
+}
+EXPORT_SYMBOL(s3c_bat_use_wimax);
diff --git a/drivers/net/wimax_cmc/firmware.h b/drivers/net/wimax_cmc/firmware.h
new file mode 100644
index 0000000..862d0d8
--- /dev/null
+++ b/drivers/net/wimax_cmc/firmware.h
@@ -0,0 +1,29 @@
+/*
+ * firmware.h
+ *
+ * functions for Linux filesystem access
+ * Firmware binary file is on the filesystem, read fild and send it through SDIO
+ */
+#ifndef _WIMAX_FIRMWARE_H
+#define _WIMAX_FIRMWARE_H
+
+#include <linux/fs.h>
+#include <linux/file.h>
+#include <linux/uaccess.h>
+
+#define SEEK_SET 0
+#define SEEK_CUR 1
+#define SEEK_END 2
+
+/******************************************************************************
+ * Function Prototypes
+ ******************************************************************************/
+struct file *klib_fopen(const char *filename, int flags, int mode);
+void klib_fclose(struct file *filp);
+int klib_fseek(struct file *filp, int offset, int whence);
+int klib_fread(char *buf, int len, struct file *filp);
+int klib_fgetc(struct file *filp);
+int klib_flength(struct file *filp);
+int klib_flen_fcopy(char *buf, int len, struct file *filp);
+int klib_fwrite(char *buf, int len, struct file *filp);
+#endif /* _WIMAX_FIRMWARE_H */
diff --git a/drivers/net/wimax_cmc/wimax_i2c.c b/drivers/net/wimax_cmc/wimax_i2c.c
new file mode 100644
index 0000000..018bfd3
--- /dev/null
+++ b/drivers/net/wimax_cmc/wimax_i2c.c
@@ -0,0 +1,854 @@
+/**
+ * wimax_i2c.c
+ *
+ * EEPROM access functions
+ */
+#include "wimax_i2c.h"
+#include "firmware.h"
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <mach/gpio.h>
+#include <plat/gpio-cfg.h>
+#include <linux/vmalloc.h>
+#include <plat/gpio-cfg.h>
+#include <mach/regs-gpio.h>
+#include <mach/gpio-u1.h>
+#include <linux/slab.h>
+
+#define WIMAX_EN GPIO_WIMAX_EN
+#define I2C_SEL GPIO_WIMAX_I2C_CON
+#define EEPROM_SCL GPIO_CMC_SCL_18V
+#define EEPROM_SDA GPIO_CMC_SDA_18V
+
+
+#define MAX_WIMAXBOOTIMAGE_SIZE 6000
+#define MAX_BOOT_WRITE_LENGTH 128
+#define WIMAX_EEPROM_ADDRLEN 2
+
+
+struct boot_image_data g_wimax_image;
+
+static void wimax_i2c_reset(void);
+
+void dump_buffer(const char *desc, u_char *buffer, u_int len)
+{
+ char print_buf[256] = {0};
+ char chr[8] = {0};
+ int i;
+ /* dump packets */
+ u_char *b = buffer;
+ pr_debug("%s (%d) =>", desc, len);
+
+ for (i = 0; i < len; i++) {
+ sprintf(chr, " %02x", b[i]);
+ strcat(print_buf, chr);
+ if (((i + 1) != len) && (i % 16 == 15)) {
+ pr_debug("%s", print_buf);
+ memset(print_buf, 0x0, 256);
+ }
+ }
+ pr_debug("%s", print_buf);
+}
+void eeprom_poweron(void)
+{
+ mutex_init(&g_wimax_image.lock);
+ mutex_lock(&g_wimax_image.lock);
+
+ s3c_gpio_cfgpin(EEPROM_SCL, S3C_GPIO_SFN(1));
+ s3c_gpio_setpull(EEPROM_SCL, S3C_GPIO_PULL_NONE);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ s3c_gpio_setpull(EEPROM_SDA, S3C_GPIO_PULL_NONE);
+
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+
+ /* SEL = 1 to switch i2c-eeprom path to AP */
+ gpio_set_value(I2C_SEL, GPIO_LEVEL_HIGH);
+ usleep_range(10000, 10000);
+
+
+ /* power on */
+ gpio_set_value(WIMAX_EN, GPIO_LEVEL_HIGH);
+
+ msleep(100);
+
+ wimax_i2c_reset();
+ usleep_range(10000, 10000);
+}
+
+void eeprom_poweroff(void)
+{
+ /* power off */
+ gpio_set_value(WIMAX_EN, GPIO_LEVEL_LOW);
+
+ /* SEL = 0 to switch i2c-eeprom path to wimax */
+ gpio_set_value(I2C_SEL, GPIO_LEVEL_LOW);
+
+ msleep(100);
+
+ mutex_unlock(&g_wimax_image.lock);
+ mutex_destroy(&g_wimax_image.lock);
+}
+
+/* I2C functions for read/write EEPROM */
+#ifdef DRIVER_BIT_BANG
+void wimax_i2c_write_byte(char c, int len)
+{
+ int i;
+ char level;
+
+ /* 8 bits */
+ for (i = 0; i < len; i++) {
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ udelay(1);
+ level = (c >> (len - i - 1)) & 0x1;
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, level);
+ udelay(2);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+ }
+}
+
+char wimax_i2c_read_byte(int len)
+{
+ int j;
+ char val = 0;
+
+ /* 8 bits */
+ for (j = 0; j < len; j++) {
+ /* read data */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(0));
+ udelay(2);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+ val |= (gpio_get_value(EEPROM_SDA) << (len - j - 1));
+ }
+
+ return val;
+}
+
+void wimax_i2c_init(void)
+{
+ const int DEVICE_ADDRESS = 0x50;
+
+ s3c_gpio_cfgpin(EEPROM_SCL, S3C_GPIO_SFN(1));
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+
+ /* initial */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+ udelay(3);
+
+ /* start bit */
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_LOW);
+ udelay(2);
+
+ /* send 7 bits address */
+ wimax_i2c_write_byte(DEVICE_ADDRESS, 7);
+}
+
+void wimax_i2c_deinit(void)
+{
+ /* stop */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ udelay(1);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_LOW);
+ udelay(1);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(1);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+
+ mdelay(10);
+
+}
+
+void wimax_i2c_reset(void)
+{
+ int i;
+
+ /* initial */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+ udelay(3);
+
+ /* start bit */
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_LOW);
+ udelay(3);
+
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ udelay(1);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+
+ /* 9 clocks */
+ for (i = 0; i < 9; i++) {
+ udelay(2);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ }
+ udelay(2);
+
+ /* start bit & stop bit */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_LOW);
+ udelay(2);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ udelay(2);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+ udelay(2);
+}
+
+void wimax_i2c_ack(void)
+{
+ /* ack */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ udelay(1);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_LOW);
+ udelay(1);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(1);
+}
+
+void wimax_i2c_no_ack(void)
+{
+ /* no ack */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ udelay(1);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH);
+ udelay(1);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(1);
+}
+
+int wimax_i2c_check_ack(void)
+{
+ char val;
+
+ /* ack */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(0));
+ udelay(1);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+ val = gpio_get_value(EEPROM_SDA);
+
+ if (val == 1) {
+ pr_debug("EEPROM ERROR: NO ACK!!");
+ return -1;
+ }
+
+ return 0;
+}
+
+int wimax_i2c_write_cmd(void)
+{
+ /* write cmd */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_LOW); /* write cmd: 0 */
+ udelay(1);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+
+ return wimax_i2c_check_ack();
+}
+
+int wimax_i2c_read_cmd(void)
+{
+ /* read cmd */
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_LOW);
+ s3c_gpio_cfgpin(EEPROM_SDA, S3C_GPIO_SFN(1));
+ gpio_set_value(EEPROM_SDA, GPIO_LEVEL_HIGH); /* read cmd: 1 */
+ udelay(1);
+ gpio_set_value(EEPROM_SCL, GPIO_LEVEL_HIGH);
+ udelay(2);
+
+ return wimax_i2c_check_ack();
+}
+
+int wimax_i2c_eeprom_address(short addr)
+{
+ char buf[2] = {0};
+
+ buf[0] = addr & 0xff;
+ buf[1] = (addr >> 8) & 0xff;
+
+ /* send 2 bytes address */
+ wimax_i2c_write_byte(buf[1], 8);
+ if (wimax_i2c_check_ack())
+ return -1;
+ wimax_i2c_write_byte(buf[0], 8);
+ return wimax_i2c_check_ack();
+}
+
+int wimax_i2c_write_buffer(char *data, int len)
+{
+ int i;
+ for (i = 0; i < len; i++) {
+ wimax_i2c_write_byte(data[i], 8); /* 1 byte data */
+ if (wimax_i2c_check_ack())
+ return -1;
+ }
+ return 0;
+}
+
+int wimax_i2c_write(u_short addr, u_char *data, int length)
+{
+ int ret = 0;
+
+ /* write buffer */
+ wimax_i2c_init();
+ ret = wimax_i2c_write_cmd();
+ ret |= wimax_i2c_eeprom_address(addr);
+ ret |= wimax_i2c_write_buffer(data, length);
+ wimax_i2c_deinit();
+ return ret;
+}
+
+int wimax_i2c_read(u_short addr, u_char *data, int length)
+{
+ int i = 0;
+ int ret = 0;
+
+ /* read buffer */
+ wimax_i2c_init();
+ ret = wimax_i2c_write_cmd();
+ ret |= wimax_i2c_eeprom_address(addr);
+
+ wimax_i2c_init();
+ ret |= wimax_i2c_read_cmd();
+
+ for (i = 0; i < length; i++) {
+ *(data + i) = wimax_i2c_read_byte(8); /* 1 byte data */
+ if ((i + 1) == length)
+ wimax_i2c_no_ack(); /* no ack end of data */
+ else
+ wimax_i2c_ack();
+ }
+ wimax_i2c_deinit();
+ return ret;
+}
+#else
+
+static struct i2c_client *pclient;
+
+void wimax_i2c_reset(void)
+{}
+
+int wimax_i2c_write(u_short addr, u_char *data, int length)
+{
+ int rc;
+ int len;
+ char data_buffer[MAX_BOOT_WRITE_LENGTH + WIMAX_EEPROM_ADDRLEN];
+ struct i2c_msg msg;
+ data_buffer[0] = (unsigned char)((addr >> 8) & 0xff);
+ data_buffer[1] = (unsigned char)(addr & 0xff);
+ while (length) {
+ len = (length > MAX_BOOT_WRITE_LENGTH) ?
+ MAX_BOOT_WRITE_LENGTH : length ;
+ memcpy(data_buffer+WIMAX_EEPROM_ADDRLEN, data, len);
+ msg.addr = pclient->addr;
+ msg.flags = 0; /*write*/
+ msg.len = (u16)length+WIMAX_EEPROM_ADDRLEN;
+ msg.buf = data_buffer;
+ rc = i2c_transfer(pclient->adapter, &msg, 1);
+ if (rc < 0)
+ return rc;
+ length -= len;
+ data += len;
+ }
+ return 0;
+}
+
+int wimax_i2c_read(u_short addr, u_char *data, int length)
+{
+ char data_buffer[MAX_BOOT_WRITE_LENGTH + WIMAX_EEPROM_ADDRLEN];
+ struct i2c_msg msgs[2];
+ data_buffer[0] = (unsigned char)((addr >> 8) & 0xff);
+ data_buffer[1] = (unsigned char)(addr & 0xff);
+ msgs[0].addr = pclient->addr;
+ msgs[0].flags = 0; /*write*/
+ msgs[0].len = WIMAX_EEPROM_ADDRLEN;
+ msgs[0].buf = data_buffer;
+ msgs[1].addr = pclient->addr;
+ msgs[1].flags = I2C_M_RD; /*read*/
+ msgs[1].len = length;
+ msgs[1].buf = data;
+ return i2c_transfer(pclient->adapter, msgs, 2);
+
+}
+
+static ssize_t eeprom_show(struct device *dev,
+ struct device_attribute *attr, char *buf){
+ pr_debug("Writing boot & revision to wimax eeprom");
+
+ eeprom_write_boot();
+ eeprom_write_rev();
+
+ return 0;
+
+}
+
+static ssize_t eeprom_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buffer, size_t count) {
+
+ if (strncmp(buffer, "wb00", 4) == 0) {
+ pr_debug("Write EEPROM!!");
+ eeprom_write_boot();
+ } else if (strncmp(buffer, "rb00", 4) == 0) {
+ pr_debug("Read Boot!!");
+ eeprom_read_boot();
+ } else if (strncmp(buffer, "re00", 4) == 0) {
+ pr_debug("Read EEPROM!!");
+ eeprom_read_all();
+ } else if (strncmp(buffer, "ee00", 4) == 0) {
+ pr_debug("Erase EEPROM!!");
+ eeprom_erase_all();
+ } else if (strncmp(buffer, "rcal", 4) == 0) {
+ pr_debug("Check Cal!!");
+ eeprom_check_cal();
+ } else if (strncmp(buffer, "ecer", 4) == 0) {
+ pr_debug("Erase Cert!!");
+ eeprom_erase_cert();
+ } else if (strncmp(buffer, "rcer", 4) == 0) {
+ pr_debug("Check Cert!!");
+ eeprom_check_cert();
+ } else if (strncmp(buffer, "wrev", 4) == 0) {
+ pr_debug("Write Rev!!");
+ eeprom_write_rev();
+ } else
+ pr_debug("Wrong option");
+
+ return count;
+}
+
+static DEVICE_ATTR(eeprom, 0664, eeprom_show, eeprom_store);
+
+int wmxeeprom_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct class *wimax_class;
+ struct device *dev_t;
+ int err;
+
+ pr_debug("[WMXEEPROM]: probe");
+
+ pclient = client;
+ wimax_class = class_create(THIS_MODULE, "wimax");
+ if (IS_ERR(wimax_class))
+ pr_err("%s: Class create failed", __func__);
+
+ dev_t = device_create(wimax_class, NULL, 0, "%s", "cmc732");
+ if (IS_ERR(dev_t))
+ pr_err("%s: Device create failed", __func__);
+
+ err = device_create_file(dev_t, &dev_attr_eeprom);
+ if (err < 0)
+ pr_err("%s: Device file create failed", __func__);
+
+ return 0;
+}
+
+int wmxeeprom_remove(struct i2c_client *client)
+{
+ pr_debug("[WMXEEPROM]: remvove");
+
+ return 0;
+}
+
+const struct i2c_device_id wmxeeprom_id[] = {
+ { "wmxeeprom", 0 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, wmxeeprom_id);
+static struct i2c_driver wmxeeprom_driver = {
+ .probe = wmxeeprom_probe,
+ .remove = wmxeeprom_remove,
+ .id_table = wmxeeprom_id,
+ .driver = {
+ .name = "wmxeeprom",
+ },
+};
+int wmxeeprom_init(void)
+{
+ return i2c_add_driver(&wmxeeprom_driver);
+}
+
+void wmxeeprom_exit(void)
+{
+ i2c_del_driver(&wmxeeprom_driver);
+}
+#endif
+
+int load_wimax_boot(void)
+{
+ struct file *fp;
+ int read_size = 0;
+
+ fp = klib_fopen(WIMAX_BOOTIMAGE_PATH, O_RDONLY, 0);
+
+ if (fp) {
+ pr_debug("LoadWiMAXBootImage ..");
+ g_wimax_image.data = vmalloc(MAX_WIMAXBOOTIMAGE_SIZE);
+ if (!g_wimax_image.data) {
+ pr_debug("Error: Memory alloc failure.");
+ klib_fclose(fp);
+ return -1;
+ }
+
+ memset(g_wimax_image.data, 0, MAX_WIMAXBOOTIMAGE_SIZE);
+ read_size = klib_flen_fcopy(g_wimax_image.data,
+ MAX_WIMAXBOOTIMAGE_SIZE, fp);
+ g_wimax_image.size = read_size;
+ g_wimax_image.address = 0;
+ g_wimax_image.offset = 0;
+ mutex_init(&g_wimax_image.lock);
+
+ klib_fclose(fp);
+ } else {
+ pr_debug("Error: WiMAX image file open failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+int write_rev(void)
+{
+ u_int val;
+ int retry = 100;
+ int err;
+
+ pr_debug("HW REV: %d", system_rev);
+
+ /* swap */
+ val = be32_to_cpu(system_rev);
+
+ do {
+ err = wimax_i2c_write(0x7080, (char *)(&val), 4);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+ if (retry < 0)
+ pr_debug("eeprom error");
+ return err ;
+}
+
+void erase_cert(void)
+{
+ char buf[256] = {0};
+ int len = 4;
+ int retry = 100;
+ int err;
+
+ do {
+ err = wimax_i2c_write(0x5800, buf, len);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+ if (retry < 0)
+ pr_debug("eeprom error");
+}
+
+int check_cert(void)
+{
+ char buf[256] = {0};
+ int len = 16;
+ int retry = 100;
+ int err;
+
+ do {
+ err = wimax_i2c_read(0x5800, buf, len);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+ if (retry < 0)
+ pr_debug("eeprom error");
+
+ dump_buffer("Certification", (u_char *)buf, (u_int)len);
+
+ /* check "Cert" */
+ if (buf[0] == 0x43 && buf[1] == 0x65 &&
+ buf[2] == 0x72 && buf[3] == 0x74)
+ return 0;
+
+ return -1;
+}
+
+int check_cal(void)
+{
+ char buf[256] = {0};
+ int i;
+ int len = 32;
+ int retry = 100;
+ int err;
+
+ do {
+ err = wimax_i2c_read(0x5400, buf, len);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+ if (retry < 0)
+ pr_debug("eeprom error");
+
+ dump_buffer("Calibration", (u_char *)buf, (u_int)len);
+
+ /* at lease one buf[i] not equal 0xff means calibrated */
+ for (i = 0; i < len; i++)
+ if (buf[i] != 0xff)
+ return 0;
+
+ /* all 0xff means not calibrated */
+ return -1;
+}
+
+void eeprom_read_boot()
+{
+ char *buf = NULL;
+ int i, j;
+ int len;
+ u_int checksum = 0;
+ short addr = 0;
+ int retry = 1000;
+ int err;
+
+ eeprom_poweron();
+
+ buf = kmalloc(4096, GFP_KERNEL);
+ if (buf == NULL) {
+ pr_debug("eeprom_read_boot: MALLOC FAIL!!");
+ return;
+ }
+
+ addr = 0x0;
+ for (j = 0; j < 1; j++) { /* read 4K */
+ len = 4096;
+ do {
+ err = wimax_i2c_read(addr, buf, len);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+
+ {
+ /* dump boot data */
+ char print_buf[256] = {0};
+ char chr[8] = {0};
+
+ /* dump packets with checksum */
+ u_char *b = (u_char *)buf;
+ pr_debug("EEPROM dump [0x%04x] = ", addr);
+
+ for (i = 0; i < len; i++) {
+ checksum += b[i];
+ sprintf(chr, " %02x", b[i]);
+ strcat(print_buf, chr);
+ if (((i + 1) != len) && (i % 16 == 15)) {
+ pr_debug("%s", print_buf);
+ memset(print_buf, 0x0, 256);
+ }
+ }
+ pr_debug("%s", print_buf);
+ }
+ addr += len;
+ }
+
+ pr_debug("Checksum: 0x%08x", checksum);
+
+ kfree(buf);
+
+ eeprom_poweroff();
+ if (retry < 0)
+ pr_debug("eeprom error");
+}
+
+void eeprom_read_all()
+{
+ char *buf = NULL;
+ int i, j;
+ int len;
+ u_int checksum = 0;
+ short addr = 0;
+ int retry = 1000;
+ int err;
+
+ eeprom_poweron();
+
+ /* allocate 4K buffer */
+ buf = kmalloc(4096, GFP_KERNEL);
+ if (buf == NULL) {
+ pr_debug("eeprom_read_all: MALLOC FAIL!!");
+ return;
+ }
+
+ addr = 0x0;
+
+ /* read 64K */
+ for (j = 0; j < 16; j++) {
+ len = 4096;
+ do {
+ err = wimax_i2c_read(addr, buf, len);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+
+ {
+ /* dump EEPROM */
+ char print_buf[256] = {0};
+ char chr[8] = {0};
+
+ /* dump packets with checksum */
+ u_char *b = (u_char *)buf;
+ pr_debug("EEPROM dump [0x%04x] = ", addr);
+
+ for (i = 0; i < len; i++) {
+ checksum += b[i];
+ sprintf(chr, " %02x", b[i]);
+ strcat(print_buf, chr);
+ if (((i + 1) != len) && (i % 16 == 15)) {
+ pr_debug("%s", print_buf);
+ memset(print_buf, 0x0, 256);
+ }
+ }
+ pr_debug("%s", print_buf);
+ }
+ addr += len;
+ }
+
+ pr_debug("Checksum: 0x%08x", checksum);
+
+ /* free 4K buffer */
+ kfree(buf);
+
+ eeprom_poweroff();
+ if (retry < 0)
+ pr_debug("eeprom error");
+}
+
+void eeprom_erase_all()
+{
+ char buf[128] = {0};
+ int i;
+ int retry = 1000;
+ int err;
+
+ eeprom_poweron();
+
+ memset(buf, 0xff, 128);
+ for (i = 0; i < 512; i++) { /* clear all EEPROM */
+ pr_debug("ERASE [0x%04x]\n", i * 128);
+ do {
+ err = wimax_i2c_write(128 * i, buf, 128);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+ }
+
+ eeprom_poweroff();
+ if (retry < 0)
+ pr_debug("eeprom error");
+}
+
+/* Write boot image to EEPROM */
+int eeprom_write_boot(void)
+{
+ u_char *buffer;
+ int retry = 1000;
+ int err = 0;
+ u_short ucsize = MAX_BOOT_WRITE_LENGTH;
+
+ if (load_wimax_boot() < 0) {
+ pr_debug("ERROR READ WIMAX BOOT IMAGE");
+ return -1;
+ }
+
+ eeprom_poweron();
+
+ g_wimax_image.offset = 0;
+
+ while (g_wimax_image.size > g_wimax_image.offset) {
+ buffer = (u_char *)(g_wimax_image.data + g_wimax_image.offset);
+ ucsize = MAX_BOOT_WRITE_LENGTH;
+
+ /* write buffer */
+ do {
+ err = wimax_i2c_write(
+ (u_short)g_wimax_image.offset, buffer, ucsize);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+
+ g_wimax_image.offset += MAX_BOOT_WRITE_LENGTH;
+
+ if ((g_wimax_image.size - g_wimax_image.offset)
+ < MAX_BOOT_WRITE_LENGTH) {
+ buffer = (u_char *)(g_wimax_image.data +
+ g_wimax_image.offset);
+ ucsize = g_wimax_image.size - g_wimax_image.offset;
+
+ /* write last data */
+ do {
+ err = wimax_i2c_write(
+ (u_short)g_wimax_image.offset, buffer, ucsize);
+ } while (err < 0 ? ((retry--) > 0) : 0);
+
+ g_wimax_image.offset += MAX_BOOT_WRITE_LENGTH;
+ }
+ }
+
+ eeprom_poweroff();
+
+ if (g_wimax_image.data) {
+ pr_debug("Delete the Image Loaded");
+ vfree(g_wimax_image.data);
+ g_wimax_image.data = NULL;
+ }
+
+ if (retry < 0)
+ pr_debug("eeprom error");
+ else
+ pr_debug("EEPROM WRITING DONE.");
+
+ return err;
+}
+
+int eeprom_write_rev(void)
+{
+ int ret;
+
+ eeprom_poweron();
+ ret = write_rev(); /* write hw rev to eeprom */
+ eeprom_poweroff();
+
+ pr_debug("REV WRITING DONE.");
+ return ret;
+}
+
+void eeprom_erase_cert(void)
+{
+ eeprom_poweron();
+ erase_cert();
+ eeprom_poweroff();
+
+ pr_debug("ERASE CERT DONE.");
+}
+
+int eeprom_check_cert(void)
+{
+ int ret;
+
+ eeprom_poweron();
+ ret = check_cert(); /* check cert */
+ eeprom_poweroff();
+
+ pr_debug("CHECK CERT DONE. (ret: %d)", ret);
+
+ return ret;
+}
+
+int eeprom_check_cal(void)
+{
+ int ret;
+
+ eeprom_poweron();
+ ret = check_cal(); /* check cal */
+ eeprom_poweroff();
+
+ pr_debug("CHECK CAL DONE. (ret: %d)", ret);
+
+ return ret;
+}
+
diff --git a/drivers/net/wimax_cmc/wimax_i2c.h b/drivers/net/wimax_cmc/wimax_i2c.h
new file mode 100644
index 0000000..cadd762
--- /dev/null
+++ b/drivers/net/wimax_cmc/wimax_i2c.h
@@ -0,0 +1,53 @@
+/**
+ * wimax_i2c.h
+ *
+ * EEPROM access functions
+ */
+#ifndef __WIMAX_I2C_H__
+#define __WIMAX_I2C_H__
+#include <linux/mutex.h>
+#define WIMAX_BOOTIMAGE_PATH "/system/etc/wimax_boot.bin"
+
+struct boot_image_data {
+ unsigned int size;
+ unsigned int address;
+ unsigned int offset;
+ struct mutex lock;
+ unsigned char *data;
+};
+
+/* Write WiMAX boot image to EEPROM */
+int eeprom_write_boot(void);
+
+/* Write HW rev to EEPROM */
+int eeprom_write_rev(void);
+
+/* Erase WiMAX certification */
+void eeprom_erase_cert(void);
+
+/* Check WiMAX cerification exist or not */
+int eeprom_check_cert(void);
+
+/* Check WiMAX calibration done or not */
+int eeprom_check_cal(void);
+
+/* Read WiMAX boot image */
+void eeprom_read_boot(void);
+
+/* Read entire EEPROM data */
+void eeprom_read_all(void);
+
+/* Erase entire EEPROM data */
+void eeprom_erase_all(void);
+
+#ifndef DRIVER_BIT_BANG
+
+/* Initialize i2c-gpio driver for eeprom*/
+int wmxeeprom_init(void);
+
+/* Remove the eeprom i2c-gpio driver */
+void wmxeeprom_exit(void);
+
+#endif
+
+#endif /* __WIMAX_I2C_H__ */
diff --git a/drivers/net/wimax_cmc/wimax_sdio.c b/drivers/net/wimax_cmc/wimax_sdio.c
new file mode 100644
index 0000000..ece4b04
--- /dev/null
+++ b/drivers/net/wimax_cmc/wimax_sdio.c
@@ -0,0 +1,1789 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include "wimax_sdio.h"
+#include "firmware.h"
+#include "wimax_i2c.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/suspend.h>
+#include <linux/miscdevice.h>
+#include <linux/gpio.h>
+#include <linux/etherdevice.h>
+#include <linux/ethtool.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/pm.h>
+#include <linux/mmc/sdio_func.h>
+#include <asm/byteorder.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/wimax/samsung/wimax732.h>
+#include <linux/kthread.h>
+
+/* driver Information */
+#define WIMAX_DRIVER_VERSION_STRING "3.0.6"
+#define DRIVER_AUTHOR "Samsung"
+#define DRIVER_DESC "Samsung WiMAX SDIO Device Driver"
+/* use ethtool to change the level for any given device */
+static int msg_level = -1;
+module_param(msg_level, int, 0);
+
+static int hw_sdio_tx_bank_index(struct net_adapter *adapter, int *write_idx)
+{
+ int ret = 0;
+
+ *write_idx = sdio_readb(adapter->func, SDIO_H2C_WP_REG, &ret);
+ if (ret)
+ return ret;
+
+ if (((*write_idx + 1) % 15) == sdio_readb(adapter->func,
+ SDIO_H2C_RP_REG, &ret))
+ *write_idx = -1;
+
+ return ret;
+}
+
+static void adapter_interrupt(struct sdio_func *func);
+static void sdio_error(struct net_adapter *adapter)
+{
+ if ((adapter->sdio_error_count++) > MAX_SDIO_ERROR)
+ pr_err("Unable to recover from SDIO failure");
+ schedule_work(&adapter->pdata->g_cfg->shutdown);
+}
+
+bool sd_send(struct net_adapter *adapter, u8 *buffer, u32 len)
+{
+ int nRet;
+ int nWriteIdx;
+
+ /*round off len to even*/
+ (len & 1) ? len++ : 0;
+
+ sdio_claim_host(adapter->func);
+ nRet = hw_sdio_tx_bank_index(adapter, &nWriteIdx);
+
+
+ if (unlikely(nRet))
+ goto sdio_error;
+
+ if (unlikely(nWriteIdx == -1)) {
+ pr_err("modem buffer full, skipping packet write");
+ goto skip;
+ }
+ sdio_writeb(adapter->func, (nWriteIdx + 1) % 15, SDIO_H2C_WP_REG, NULL);
+ nRet = sdio_memcpy_toio(adapter->func,
+ SDIO_TX_BANK_ADDR+(CMC732_SDIO_BANK_SIZE * nWriteIdx)+
+ CMC732_PACKET_LENGTH_SIZE, buffer, len);
+ if (unlikely(nRet < 0))
+ goto sdio_error;
+ nRet = sdio_memcpy_toio(adapter->func,
+ SDIO_TX_BANK_ADDR + (CMC732_SDIO_BANK_SIZE * nWriteIdx),
+ &len, CMC732_PACKET_LENGTH_SIZE);
+ if (unlikely(nRet < 0))
+ goto sdio_error;
+skip:
+ sdio_release_host(adapter->func);
+ /*Reset SDIO error count once it is functional again*/
+ adapter->sdio_error_count = 0;
+ return false;
+sdio_error:
+ pr_err("SDIO error");
+ if (adapter->pdata->g_cfg->power_state == CMC_POWER_ON)
+ sdio_error(adapter);
+ sdio_release_host(adapter->func);
+ return true;
+}
+
+bool send_cmd_packet(struct net_adapter *adapter, u16 cmd_id)
+{
+ u8 tx_buf[CMD_MSG_TOTAL_LENGTH];
+
+ ((struct hw_packet_header *)tx_buf)->id0 = 'W';
+ ((struct hw_packet_header *)tx_buf)->id1 = 'C';
+ ((struct hw_packet_header *)tx_buf)->length =
+ be16_to_cpu(CMD_MSG_TOTAL_LENGTH);
+ ((struct wimax_msg_header *)(tx_buf +
+ sizeof(struct hw_packet_header)))->type
+ = be16_to_cpu(ETHERTYPE_DL);
+ ((struct wimax_msg_header *)(tx_buf +
+ sizeof(struct hw_packet_header)))->id
+ = be16_to_cpu(cmd_id);
+ ((struct wimax_msg_header *)(tx_buf +
+ sizeof(struct hw_packet_header)))->length
+ = be32_to_cpu(CMD_MSG_LENGTH);
+ return sd_send(adapter, tx_buf, CMD_MSG_TOTAL_LENGTH);
+}
+
+bool send_image_info_packet(struct net_adapter *adapter, u16 cmd_id)
+{
+ struct hw_packet_header *pkt_hdr;
+ struct wimax_msg_header *msg_hdr;
+ u32 image_info[4];
+ u8 tx_buf[IMAGE_INFO_MSG_TOTAL_LENGTH];
+ u32 offset;
+
+ pkt_hdr = (struct hw_packet_header *)tx_buf;
+ pkt_hdr->id0 = 'W';
+ pkt_hdr->id1 = 'C';
+ pkt_hdr->length = be16_to_cpu(IMAGE_INFO_MSG_TOTAL_LENGTH);
+
+ offset = sizeof(struct hw_packet_header);
+ msg_hdr = (struct wimax_msg_header *)(tx_buf + offset);
+ msg_hdr->type = be16_to_cpu(ETHERTYPE_DL);
+ msg_hdr->id = be16_to_cpu(cmd_id);
+ msg_hdr->length = be32_to_cpu(IMAGE_INFO_MSG_LENGTH);
+
+ image_info[0] = 0;
+ image_info[1] = be32_to_cpu(adapter->fw->size);
+ image_info[2] = be32_to_cpu(CMC732_WIMAX_ADDRESS);
+ image_info[3] = 0;
+
+ offset += sizeof(struct wimax_msg_header);
+ memcpy(&(tx_buf[offset]), image_info, sizeof(image_info));
+ return sd_send(adapter, tx_buf, IMAGE_INFO_MSG_TOTAL_LENGTH);
+}
+
+bool send_image_data_packet(struct net_adapter *adapter, u16 cmd_id)
+{
+ struct hw_packet_header *pkt_hdr;
+ struct image_data_payload *pImageDataPayload;
+ struct wimax_msg_header *msg_hdr;
+ u8 *tx_buf = NULL;
+ u32 len;
+ u32 offset;
+ u32 size;
+ bool status;
+
+ tx_buf = kmalloc(MAX_IMAGE_DATA_MSG_LENGTH, GFP_KERNEL);
+ if (tx_buf == NULL) {
+ pr_err("%s malloc fail", __func__);
+ return -ENOMEM;
+ }
+
+ pkt_hdr = (struct hw_packet_header *)tx_buf;
+ pkt_hdr->id0 = 'W';
+ pkt_hdr->id1 = 'C';
+
+ offset = sizeof(struct hw_packet_header);
+ msg_hdr = (struct wimax_msg_header *)(tx_buf + offset);
+ msg_hdr->type = be16_to_cpu(ETHERTYPE_DL);
+ msg_hdr->id = be16_to_cpu(cmd_id);
+
+ if (adapter->image_offset <
+ (adapter->fw->size - MAX_IMAGE_DATA_LENGTH))
+ len = MAX_IMAGE_DATA_LENGTH;
+ else
+ len = adapter->fw->size - adapter->image_offset;
+
+ offset += sizeof(struct wimax_msg_header);
+ pImageDataPayload = (struct image_data_payload *)(tx_buf + offset);
+ pImageDataPayload->offset = be32_to_cpu(adapter->image_offset);
+ pImageDataPayload->size = be32_to_cpu(len);
+
+ memcpy(pImageDataPayload->data,
+ adapter->fw->data + adapter->image_offset, len);
+
+ size = len + 8; /* length of Payload offset + length + data */
+ pkt_hdr->length = be16_to_cpu(CMD_MSG_TOTAL_LENGTH + size);
+ msg_hdr->length = be32_to_cpu(size);
+
+ status = sd_send(adapter, tx_buf, CMD_MSG_TOTAL_LENGTH + size);
+
+ kfree(tx_buf);
+
+ adapter->image_offset += len;
+
+ return status;
+}
+
+
+static u32 process_private_cmd(struct net_adapter *adapter, void *buffer)
+{
+ struct hw_private_packet *cmd;
+ struct wimax_cfg *g_cfg = adapter->pdata->g_cfg;
+ u8 *bufp = (u8 *)buffer;
+
+ cmd = (struct hw_private_packet *)buffer;
+
+ switch (cmd->code) {
+ case HWCODEMACRESPONSE: {
+
+ if (!completion_done(&adapter->mac))
+ complete(&adapter->mac);
+
+ /* processing for mac_req request */
+ #ifndef PRODUCT_SHIP
+ pr_debug("MAC address = %02x:%02x:%02x:%02x:%02x:%02x",
+ bufp[3], bufp[4], bufp[5],
+ bufp[6], bufp[7], bufp[8]);
+ #endif
+ /* create ethernet header */
+ memcpy(adapter->eth_header,
+ bufp + 3, ETHERNET_ADDRESS_LENGTH);
+ memcpy(adapter->eth_header + ETHERNET_ADDRESS_LENGTH,
+ bufp + 3, ETHERNET_ADDRESS_LENGTH);
+ adapter->eth_header[(ETHERNET_ADDRESS_LENGTH * 2) - 1]++;
+
+ memcpy(adapter->net->dev_addr, bufp + 3,
+ ETHERNET_ADDRESS_LENGTH);
+
+ return sizeof(*cmd) + ETHERNET_ADDRESS_LENGTH - sizeof(u8);
+ }
+ case HWCODEIDLENTFY: {
+ pr_debug("%s HWCODEIDLENTFY", __func__);
+
+ s3c_bat_use_wimax(0);
+ break;
+ }
+ case HWCODELINKINDICATION: {
+ if (cmd->value == HW_PROT_VALUE_LINK_DOWN) {
+ pr_debug("LINK_DOWN_INDICATION");
+
+ s3c_bat_use_wimax(0);
+ /* indicate link down */
+ netif_stop_queue(adapter->net);
+ netif_carrier_off(adapter->net);
+ } else if (cmd->value == HW_PROT_VALUE_LINK_UP) {
+ pr_debug("LINK_UP_INDICATION");
+
+ s3c_bat_use_wimax(1);
+ /* indicate link up */
+ netif_start_queue(adapter->net);
+ netif_carrier_on(adapter->net);
+ }
+ break;
+ }
+ case HWCODEWAKEUPNTFY: {
+ /*
+ *dont suspend for at least
+ *4 sec after modem wake up
+ */
+ s3c_bat_use_wimax(1);
+ wake_lock_timeout(&g_cfg->wimax_driver_lock, 4 * HZ);
+ pr_debug("%s HWCODEWAKEUPNTFY", __func__);
+ break;
+ }
+ case HWCODEHALTEDINDICATION: {
+ pr_debug("%s HWCODEHALTEDINDICATION, stop driver", __func__);
+ schedule_work(&g_cfg->shutdown);
+ break;
+ }
+ case HWCODERXREADYINDICATION: {
+ pr_debug("Device RxReady");
+ break;
+ }
+ default:
+ pr_debug("%s packet not supported ", __func__);
+ break;
+ }
+ return sizeof(*cmd);
+}
+static void process_indicate_packet(struct net_adapter *adapter, u8 *buffer)
+{
+ struct wimax_msg_header *packet;
+ char *tmp_byte;
+
+ packet = (struct wimax_msg_header *)buffer;
+
+ if (packet->type != be16_to_cpu(ETHERTYPE_DL)) {
+ pr_warn("%s: not a download packet\n", __func__);
+ return;
+ }
+
+ switch (be16_to_cpu(packet->id)) {
+ case MSG_DRIVER_OK_RESP:
+ pr_debug("%s: MSG_DRIVER_OK_RESP\n", __func__);
+ adapter->modem_resp = true;
+ wake_up_interruptible(&adapter->modem_resp_event);
+ send_image_info_packet(adapter, MSG_IMAGE_INFO_REQ);
+ break;
+ case MSG_IMAGE_INFO_RESP:
+ pr_debug("%s: MSG_IMAGE_INFO_RESP\n", __func__);
+ send_image_data_packet(adapter, MSG_IMAGE_DATA_REQ);
+ break;
+ case MSG_IMAGE_DATA_RESP:
+ if (adapter->image_offset == adapter->fw->size) {
+ pr_debug("%s: Image Download Complete\n", __func__);
+ send_cmd_packet(adapter, MSG_RUN_REQ);
+ } else {
+ send_image_data_packet(adapter, MSG_IMAGE_DATA_REQ);
+ }
+ break;
+ case MSG_RUN_RESP:
+ tmp_byte = (char *)(buffer + sizeof(*packet));
+
+ if (*tmp_byte != 0x01)
+ break;
+ complete(&adapter->firmware_download);
+ pr_debug("%s: MSG_RUN_RESP\n", __func__);
+ break;
+ default:
+ pr_warn("%s: Unknown packet type\n", __func__);
+ break;
+ }
+}
+
+/* receive control data */
+void control_recv(struct net_adapter *adapter, void *buffer, u32 length)
+{
+ struct process_descriptor *procdsc;
+ struct buffer_descriptor *bufdsc;
+ struct list_head *pos, *nxt;
+
+ mutex_lock(&adapter->control_lock);
+ list_for_each_safe(pos, nxt, &adapter->control_process_list) {
+ procdsc = list_entry(pos, struct process_descriptor, list);
+ if (procdsc->type != *((u16 *)buffer))
+ continue;
+ bufdsc = kmalloc(sizeof(*bufdsc), GFP_KERNEL);
+ bufdsc->buffer = kmalloc(
+ (length + (ETHERNET_ADDRESS_LENGTH * 2)), GFP_KERNEL);
+ memcpy(bufdsc->buffer, adapter->eth_header,
+ (ETHERNET_ADDRESS_LENGTH * 2));
+ memcpy(bufdsc->buffer + (ETHERNET_ADDRESS_LENGTH * 2),
+ buffer, length);
+
+ /* fill out descriptor */
+ bufdsc->length = length + (ETHERNET_ADDRESS_LENGTH * 2);
+ list_add_tail(&bufdsc->list, &procdsc->buffer_list);
+ wake_up_interruptible(&procdsc->read_wait);
+ }
+ mutex_unlock(&adapter->control_lock);
+}
+
+void prepare_skb(struct net_adapter *adapter, struct sk_buff *rx_skb)
+{
+ skb_reserve(rx_skb,
+ (ETHERNET_ADDRESS_LENGTH * 2) +
+ NET_IP_ALIGN);
+
+ memcpy(skb_push(rx_skb,
+ (ETHERNET_ADDRESS_LENGTH * 2)),
+ adapter->eth_header,
+ (ETHERNET_ADDRESS_LENGTH * 2));
+
+ rx_skb->dev = adapter->net;
+ rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+void flush_skb(struct net_adapter *adapter)
+{
+ if (adapter->rx_skb) {
+ dev_kfree_skb(adapter->rx_skb);
+ adapter->rx_skb = NULL;
+ }
+}
+struct sk_buff *fetch_skb(struct net_adapter *adapter)
+{
+ struct sk_buff *ret_skb;
+ if (adapter->rx_skb) {
+ ret_skb = adapter->rx_skb;
+ adapter->rx_skb = NULL;
+ return ret_skb;
+ }
+ ret_skb = dev_alloc_skb(WIMAX_MTU_SIZE+2+
+ (ETHERNET_ADDRESS_LENGTH * 2) +
+ NET_IP_ALIGN);
+ if (!ret_skb) {
+ pr_debug("unable to allocate skb");
+ return NULL;
+ }
+ prepare_skb(adapter, ret_skb);
+ return ret_skb;
+}
+void pull_skb(struct net_adapter *adapter)
+{
+ struct sk_buff *t_skb;
+ if (adapter->rx_skb == NULL) {
+ t_skb = dev_alloc_skb(WIMAX_MTU_SIZE+2+
+ (ETHERNET_ADDRESS_LENGTH * 2) +
+ NET_IP_ALIGN);
+ if (!t_skb) {
+ pr_debug("unable to allocate skb");
+ return;
+ }
+ prepare_skb(adapter, t_skb);
+ adapter->rx_skb = t_skb;
+ }
+}
+
+static void adapter_rx_packet(struct net_adapter *adapter)
+{
+ struct hw_packet_header *hdr;
+ s32 rlen = adapter->buff_len;
+ u32 l;
+ u8 *ofs;
+ struct sk_buff *rx_skb;
+ ofs = adapter->receive_buffer;
+
+ while (rlen > 0) {
+ hdr = (struct hw_packet_header *)ofs;
+
+ /* "WD", "WC", "WP" or "WE" */
+ if (unlikely(hdr->id0 != 'W')) {
+ /*Ignore if it is the 4 byte allignment*/
+ if (rlen > 4) {
+ pr_warn("Wrong packet ID (%02x %02x)",
+ hdr->id0, hdr->id1);
+ }
+ /* skip rest of packets */
+ break;
+ }
+
+ /* change offset */
+ ofs += sizeof(*hdr);
+ rlen -= sizeof(*hdr);
+
+ /* check packet type */
+ switch (hdr->id1) {
+ case 'P': {
+ /* revert offset */
+ ofs -= sizeof(*hdr);
+ rlen += sizeof(*hdr);
+ /* process packet */
+ l = process_private_cmd(adapter, ofs);
+ /* shift */
+ ofs += l;
+ rlen -= l;
+
+ /* process next packet */
+ continue;
+ }
+ case 'C':
+ if (adapter->pdata->g_cfg->power_state ==
+ CMC_POWER_ON) {
+ ofs += 2;
+ rlen -= 2;
+ control_recv(adapter, (u8 *)ofs, hdr->length);
+ break;
+ } else {
+ hdr->length -= sizeof(*hdr);
+ process_indicate_packet(adapter, ofs);
+ break;
+ }
+ case 'D':
+ ofs += 2;
+ rlen -= 2;
+
+ if (hdr->length > BUFFER_DATA_SIZE) {
+ pr_err("Data packet too large");
+ adapter->netstats.rx_dropped++;
+ break;
+ }
+
+ if (likely(hdr->length <= (WIMAX_MTU_SIZE + 2))) {
+ rx_skb = fetch_skb(adapter);
+ if (!rx_skb) {
+ pr_err("unable to allocate skb");
+ break;
+ }
+ } else {
+ rx_skb = dev_alloc_skb(hdr->length +
+ (ETHERNET_ADDRESS_LENGTH * 2) +
+ NET_IP_ALIGN);
+ if (!rx_skb) {
+ pr_err("unable to allocate skb");
+ break;
+ }
+ prepare_skb(adapter, rx_skb);
+ }
+
+ memcpy(skb_put(rx_skb, hdr->length),
+ (u8 *)ofs,
+ hdr->length);
+
+ rx_skb->protocol =
+ eth_type_trans(rx_skb, adapter->net);
+
+ if (netif_rx_ni(rx_skb) == NET_RX_DROP) {
+ pr_err("packet dropped!");
+ adapter->netstats.rx_dropped++;
+ }
+ adapter->netstats.rx_packets++;
+ adapter->netstats.rx_bytes +=
+ (hdr->length +
+ (ETHERNET_ADDRESS_LENGTH * 2));
+
+ break;
+ case 'E':
+ /* skip rest of buffer */
+ break;
+ default:
+ pr_warn("%s :Wrong packet ID [%02x %02x]",
+ __func__, hdr->id0, hdr->id1);
+ /* skip rest of buffer */
+ break;
+ }
+
+ ofs += hdr->length;
+ rlen -= hdr->length;
+ }
+
+ return;
+}
+
+void rx_packet(struct net_adapter *adapter)
+{
+ int ret = 0;
+ int read_idx;
+ s32 t_len;
+ s32 t_index;
+ s32 t_size;
+ u8 *t_buff;
+
+ sdio_claim_host(adapter->func);
+ read_idx = sdio_readb(adapter->func, SDIO_C2H_RP_REG, &ret);
+
+ t_len = sdio_readl(adapter->func, (SDIO_RX_BANK_ADDR +
+ (read_idx * CMC732_SDIO_BANK_SIZE)), &ret);
+ if (unlikely(ret)) {
+ pr_err("%s sdio_readl error", __func__);
+ sdio_error(adapter);
+ goto err;
+ }
+
+ if (unlikely(t_len > CMC732_MAX_PACKET_SIZE)) {
+ pr_err("%s length out of bound", __func__);
+ t_len = CMC732_MAX_PACKET_SIZE;
+ }
+
+ sdio_writeb(adapter->func, (read_idx + 1) % 16,
+ SDIO_C2H_RP_REG, NULL);
+ if (unlikely(!t_len))
+ goto err;
+
+
+ adapter->buff_len = t_len;
+ t_index = (SDIO_RX_BANK_ADDR + (CMC732_SDIO_BANK_SIZE * read_idx) + 4);
+ t_buff = adapter->receive_buffer;
+
+ while (likely(t_len)) {
+ t_size = (t_len > CMC_BLOCK_SIZE) ?
+ (CMC_BLOCK_SIZE) : t_len;
+ ret = sdio_memcpy_fromio(adapter->func, (void *)t_buff,
+ t_index, t_size);
+ if (unlikely(ret)) {
+ pr_err("%s sdio_memcpy_fromio fail", __func__);
+ sdio_error(adapter);
+ goto err;
+ }
+ t_len -= t_size;
+ t_buff += t_size;
+ t_index += t_size;
+ }
+ sdio_release_host(adapter->func);
+ return;
+err:
+ adapter->netstats.rx_dropped++;
+ sdio_release_host(adapter->func);
+ return;
+}
+static void rx_process_data(struct work_struct *rx_work)
+{
+ struct net_adapter *adapter = container_of(rx_work,
+ struct net_adapter, rx_work);
+
+ rx_packet(adapter);
+ adapter_rx_packet(adapter);
+
+}
+
+static void adapter_interrupt(struct sdio_func *func)
+{
+ struct net_adapter *adapter = sdio_get_drvdata(func);
+ int intrd = 0;
+ struct buffer_descriptor *bufdsc;
+
+ /* read interrupt identification register and clear the interrupt */
+ intrd = sdio_readb(func, SDIO_INT_STATUS_REG, NULL);
+ sdio_writeb(func, intrd, SDIO_INT_STATUS_CLR_REG, NULL);
+
+ if (likely(intrd & SDIO_INT_DATA_READY)) {
+ queue_work(adapter->wimax_workqueue,
+ &adapter->rx_work);
+ } else {
+ adapter->netstats.rx_errors++;
+ pr_err("%s intrd = SDIO_INT_ERROR occurred",
+ __func__);
+ }
+}
+
+bool send_mac_request(struct net_adapter *adapter)
+{
+ struct hw_private_packet req;
+ req.id0 = 'W';
+ req.id1 = 'P';
+ req.code = HWCODEMACREQUEST;
+ req.value = 0;
+ return sd_send(adapter, (u8 *)&req, sizeof(req));
+}
+
+int hw_device_wakeup(struct net_adapter *adapter)
+{
+ int rc = 0;
+ int ret = 0;
+ struct wimax732_platform_data *pdata = adapter->pdata;
+ adapter->pdata->wakeup_assert(1);
+
+ while (unlikely(!pdata->is_modem_awake())) {
+ rc++;
+ if (rc > WAKEUP_MAX_TRY) {
+ pr_err("%s (CON0 status): modem wake up time out",
+ __func__);
+ ret = -EIO;
+ break;
+ }
+
+ if (rc == 1)
+ pr_debug("%s (CON0 status): waiting for modem awake",
+ __func__);
+ msleep(WAKEUP_ASSERT_T);
+ if (pdata->is_modem_awake())
+ break;
+ pdata->wakeup_assert(0);
+ msleep(WAKEUP_ASSERT_T);
+ pdata->wakeup_assert(1);
+ }
+
+ s3c_bat_use_wimax(1);
+ if (unlikely((rc > 0) && (rc <= WAKEUP_MAX_TRY)))
+ pr_debug("%s (CON0 status): modem awake", __func__);
+ pdata->wakeup_assert(0);
+ return ret;
+}
+
+static void tx_process_data(struct work_struct *tx_work)
+{
+ struct buffer_descriptor *bufdsc;
+ struct net_adapter *adapter = container_of(tx_work,
+ struct net_adapter, tx_work);
+ struct wimax_cfg *g_cfg = adapter->pdata->g_cfg;
+ unsigned long flags;
+ bool nRet;
+
+ while (1) {
+
+ if (!mutex_trylock(&g_cfg->suspend_mutex))
+ return;
+ spin_lock_irqsave(&adapter->send_lock, flags);
+ if (likely(!list_empty(&adapter->q_send))) {
+ bufdsc = list_first_entry(&adapter->q_send,
+ struct buffer_descriptor, list);
+ list_del(&bufdsc->list);
+ } else {
+ spin_unlock_irqrestore(&adapter->send_lock, flags);
+ mutex_unlock(&g_cfg->suspend_mutex);
+ return;
+ }
+ spin_unlock_irqrestore(&adapter->send_lock, flags);
+ if (unlikely(hw_device_wakeup(adapter))) {
+ schedule_work(&g_cfg->shutdown);
+ mutex_unlock(&g_cfg->suspend_mutex);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ return;
+ }
+ if (bufdsc->length > CMC_MAX_BYTE_SIZE)
+ bufdsc->length = (bufdsc->length + CMC_MAX_BYTE_SIZE) &
+ ~(CMC_MAX_BYTE_SIZE);
+ nRet = sd_send(adapter, bufdsc->buffer, bufdsc->length);
+ mutex_unlock(&g_cfg->suspend_mutex);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ if (unlikely(nRet))
+ return;
+ };
+}
+
+struct process_descriptor *process_by_id(struct net_adapter *adapter, u32 id)
+{
+ struct process_descriptor *procdsc;
+
+ list_for_each_entry(procdsc, &adapter->control_process_list, list) {
+ if (procdsc->id == id) /* process found */
+ return procdsc;
+ }
+ return NULL;
+}
+
+struct process_descriptor *fetch_process_by_id(struct net_adapter *adapter,
+ u32 id)
+{
+ struct process_descriptor *procdsc;
+ procdsc = process_by_id(adapter, id);
+ if (!procdsc) {
+ procdsc = kzalloc(sizeof(*procdsc), GFP_KERNEL);
+ if (!procdsc)
+ return NULL;
+ procdsc->id = id;
+ init_waitqueue_head(&procdsc->read_wait);
+ INIT_LIST_HEAD(&procdsc->buffer_list);
+ mutex_lock(&adapter->control_lock);
+ list_add_tail(&procdsc->list, &adapter->control_process_list);
+ mutex_unlock(&adapter->control_lock);
+ }
+ return procdsc;
+}
+
+u32 control_send(struct net_adapter *adapter, void *buffer, u32 length)
+{
+ struct buffer_descriptor *bufdsc;
+ struct hw_packet_header *hdr;
+ unsigned long flags;
+ u8 *ptr;
+
+ if ((length + sizeof(*hdr)) >= WIMAX_MAX_TOTAL_SIZE)
+ return -ENOMEM;/* changed from SUCCESS return status */
+
+ bufdsc = (struct buffer_descriptor *)
+ kmalloc(sizeof(*bufdsc), GFP_KERNEL);
+ if (bufdsc == NULL)
+ return -ENOMEM;
+ bufdsc->buffer = kmalloc(BUFFER_DATA_SIZE, GFP_KERNEL);
+ if (bufdsc->buffer == NULL)
+ return -ENOMEM;
+
+ ptr = bufdsc->buffer;
+ hdr = (struct hw_packet_header *)bufdsc->buffer;
+
+ ptr += sizeof(*hdr);
+ ptr += 2;
+
+ memcpy(ptr, buffer + (ETHERNET_ADDRESS_LENGTH * 2),
+ length - (ETHERNET_ADDRESS_LENGTH * 2));
+
+ /* add packet header */
+ hdr->id0 = 'W';
+ hdr->id1 = 'C';
+ hdr->length = (u16)length - (ETHERNET_ADDRESS_LENGTH * 2);
+
+ /* set length */
+ bufdsc->length = (u16)length - (ETHERNET_ADDRESS_LENGTH * 2)
+ + sizeof(*hdr);
+ bufdsc->length += 2;
+
+ spin_lock_irqsave(&adapter->send_lock, flags);
+ list_add_tail(&bufdsc->list, &adapter->q_send);
+ spin_unlock_irqrestore(&adapter->send_lock, flags);
+ schedule_work(&adapter->tx_work);
+ return 0;
+}
+
+/*
+* uwibro functions
+* (send and receive control packet with WiMAX modem)
+*/
+static int uwbrdev_open(struct inode *inode, struct file *file)
+{
+ struct net_adapter *adapter = container_of(file->private_data,
+ struct net_adapter, uwibro_dev);
+ file->private_data = adapter;
+ return 0;
+}
+
+static long uwbrdev_ioctl(struct file *file, u32 cmd,
+ unsigned long arg)
+{
+ struct process_descriptor *procdsc;
+ int ret = 0;
+ u8 *tx_buffer;
+ int length;
+ struct net_adapter *adapter =
+ (struct net_adapter *)(file->private_data);
+
+ if (adapter->pdata->g_cfg->power_state != CMC_POWER_ON)
+ return -ENODEV;
+
+ if (cmd != CONTROL_IOCTL_WRITE_REQUEST) {
+ pr_debug("uwbrdev_ioctl: unknown ioctl cmd: 0x%x", cmd);
+ return -EINVAL;
+ }
+
+ if ((char *)arg == NULL) {
+ pr_debug("arg == NULL: return -EFAULT");
+ return -EFAULT;
+ }
+
+ procdsc = fetch_process_by_id(adapter, current->tgid);
+ if (!procdsc)
+ return -ENOMEM;
+
+ length = ((int *)arg)[0];
+
+ if (length >= WIMAX_MAX_TOTAL_SIZE)
+ return -EFBIG;
+
+ tx_buffer = kmalloc(length, GFP_KERNEL);
+ if (!tx_buffer) {
+ pr_err("%s: not enough memory to allocate tx_buffer\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(tx_buffer, (void *)(arg + sizeof(int)), length)) {
+ pr_err("%s: error copying buffer from user space\n", __func__);
+ ret = -EFAULT;
+ goto err_copy;
+ }
+
+ procdsc->type = ((struct eth_header *)tx_buffer)->type;
+ control_send(adapter, tx_buffer, length);
+
+err_copy:
+ kfree(tx_buffer);
+ return ret;
+}
+
+static ssize_t uwbrdev_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos)
+{
+ struct net_adapter *adapter;
+ struct process_descriptor *procdsc;
+ struct buffer_descriptor *bufdsc;
+ u32 len = 0;
+ adapter = (struct net_adapter *)(file->private_data);
+
+ if (buf == NULL) {
+ pr_debug("BUFFER is NULL");
+ return -EFAULT; /* bad address */
+ }
+ if (adapter->pdata->g_cfg->power_state == CMC_POWERING_OFF)
+ return -ENODEV;
+ procdsc = fetch_process_by_id(adapter, current->tgid);
+ if (procdsc == NULL)
+ return -ENOMEM;
+
+ if (wait_event_interruptible(procdsc->read_wait,
+ (!list_empty(&procdsc->buffer_list)) ||
+ (adapter->pdata->g_cfg->power_state ==
+ CMC_POWERING_OFF)))
+ return -ERESTARTSYS;
+ if (adapter->pdata->g_cfg->power_state == CMC_POWERING_OFF)
+ return -ENODEV;
+
+ if (count == 1500) { /* app passes read count as 1500 */
+ mutex_lock(&adapter->control_lock);
+ bufdsc = list_first_entry(&procdsc->buffer_list,
+ struct buffer_descriptor, list);
+ list_del(&bufdsc->list);
+ mutex_unlock(&adapter->control_lock);
+ len = bufdsc->length;
+ if (copy_to_user(buf, bufdsc->buffer, len)) {
+ pr_debug("%s: copy_to_user failed len=%u !!",
+ __func__, bufdsc->length);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ return -EFAULT;
+ }
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ return len;
+ }
+
+ return 0;
+}
+
+static const struct file_operations uwbr_fops = {
+ .owner = THIS_MODULE,
+ .open = uwbrdev_open,
+ .unlocked_ioctl = uwbrdev_ioctl,
+ .read = uwbrdev_read,
+};
+
+u32 hw_send_data(struct net_adapter *adapter, void *buffer , u32 length)
+{
+ struct buffer_descriptor *bufdsc;
+ struct hw_packet_header *hdr;
+ struct net_device *net = adapter->net;
+ unsigned long flags;
+ u8 *ptr;
+
+ bufdsc = (struct buffer_descriptor *)
+ kmalloc(sizeof(*bufdsc), GFP_ATOMIC);
+ if (bufdsc == NULL)
+ return -ENOMEM;
+
+ bufdsc->buffer = kmalloc(BUFFER_DATA_SIZE, GFP_ATOMIC);
+ if (bufdsc->buffer == NULL) {
+ kfree(bufdsc);
+ return -ENOMEM;
+ }
+
+ ptr = bufdsc->buffer;
+
+ /* shift data pointer */
+ ptr = ptr + sizeof(*hdr) + 2;
+ hdr = (struct hw_packet_header *)bufdsc->buffer;
+
+ length -= (ETHERNET_ADDRESS_LENGTH * 2);
+ buffer += (ETHERNET_ADDRESS_LENGTH * 2);
+
+ memcpy(ptr, buffer, length);
+
+ hdr->id0 = 'W';
+ hdr->id1 = 'D';
+ hdr->length = (u16)length;
+
+ bufdsc->length = length + sizeof(*hdr) + 2;
+
+ /* add statistics */
+ adapter->netstats.tx_packets++;
+ adapter->netstats.tx_bytes += bufdsc->length;
+
+ spin_lock_irqsave(&adapter->send_lock, flags);
+ list_add_tail(&bufdsc->list, &adapter->q_send);
+ spin_unlock_irqrestore(&adapter->send_lock, flags);
+
+ queue_work(adapter->wimax_workqueue, &adapter->tx_work);
+ if (!netif_running(net))
+ pr_debug("!netif_running");
+
+ return 0;
+}
+
+static int netdev_ethtool_ioctl(struct net_device *dev,
+ void *useraddr)
+{
+ u32 ethcmd;
+ struct ethtool_drvinfo info = {ETHTOOL_GDRVINFO};
+
+ if (copy_from_user(&ethcmd, useraddr, sizeof(ethcmd)))
+ return -EFAULT;
+
+ if (ethcmd != ETHTOOL_GDRVINFO)
+ return -EOPNOTSUPP;
+
+ strncpy(info.driver, "C732SDIO", sizeof(info.driver) - 1);
+ if (copy_to_user(useraddr, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static struct net_device_stats *adapter_netdev_stats(struct net_device *dev)
+{
+ return &((struct net_adapter *)netdev_priv(dev))->netstats;
+}
+
+static int adapter_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct net_adapter *adapter = netdev_priv(net);
+ hw_send_data(adapter, skb->data, skb->len);
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+static int adapter_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+ switch (cmd) {
+ case SIOCETHTOOL:
+ return netdev_ethtool_ioctl(net, (void *)rq->ifr_data);
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+
+static struct net_device_ops wimax_net_ops = {
+ .ndo_get_stats = adapter_netdev_stats,
+ .ndo_do_ioctl = adapter_ioctl,
+ .ndo_start_xmit = adapter_start_xmit,
+};
+
+static irqreturn_t wimax_hostwake_isr(int irq, void *dev)
+{
+ struct net_adapter *adapter = dev;
+ wake_lock_timeout(&adapter->pdata->g_cfg->wimax_driver_lock, 1 * HZ);
+
+ return IRQ_HANDLED;
+}
+
+static int cmc732_setup_wake_irq(struct net_adapter *adapter)
+{
+ struct wimax732_platform_data *pdata = adapter->pdata;
+ int rc;
+ int irq;
+
+ rc = gpio_request(pdata->wimax_int, "gpio_wimax_int");
+ if (rc < 0) {
+ pr_debug("%s: gpio %d request failed (%d)\n",
+ __func__, pdata->wimax_int, rc);
+ return rc;
+ }
+
+ rc = gpio_direction_input(pdata->wimax_int);
+ if (rc < 0) {
+ pr_debug("%s: failed to set gpio %d as input (%d)\n",
+ __func__, pdata->wimax_int, rc);
+ goto err_gpio_direction_input;
+ }
+
+ irq = gpio_to_irq(pdata->wimax_int);
+
+ rc = request_threaded_irq(irq, NULL, wimax_hostwake_isr,
+ IRQF_TRIGGER_FALLING, "wimax_int", adapter);
+ if (rc < 0) {
+ pr_debug("%s: request_irq(%d) failed for gpio %d (%d)\n",
+ __func__, irq,
+ pdata->wimax_int, rc);
+ goto err_request_irq;
+ }
+
+ rc = enable_irq_wake(irq);
+ if (rc < 0) {
+ pr_err("%s: enable_irq_wake(%d) failed for gpio %d (%d)\n",
+ __func__, irq, pdata->wimax_int, rc);
+ goto err_enable_irq_wake;
+ }
+
+ adapter->wake_irq = irq;
+
+ return 0;
+
+err_enable_irq_wake:
+ free_irq(irq, adapter);
+err_request_irq:
+err_gpio_direction_input:
+ gpio_free(pdata->wimax_int);
+ return rc;
+}
+
+static void cmc732_release_wake_irq(struct net_adapter *adapter)
+{
+ if (!adapter->wake_irq)
+ return;
+ disable_irq_wake(adapter->wake_irq);
+ free_irq(adapter->wake_irq, adapter);
+ gpio_free(adapter->pdata->wimax_int);
+}
+
+#ifdef WIMAX_CON0_POLL
+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;
+
+ wake_lock(&g_cfg->wimax_driver_lock);
+
+ while ((g_cfg->power_state != CMC_POWERING_OFF) &&
+ (g_cfg->power_state != CMC_POWER_OFF)) {
+ curr_val = adapter->pdata->is_modem_awake();
+ if ((prev_val && (!curr_val)) || (!curr_val)) {
+ adapter->pdata->restore_uart_path();
+ break;
+ }
+ prev_val = curr_val;
+ wait_event_interruptible_timeout(adapter->con0_poll,
+ (g_cfg->power_state == CMC_POWERING_OFF) ||
+ (g_cfg->power_state == CMC_POWER_OFF),
+ msecs_to_jiffies(40));
+ }
+ wake_unlock(&g_cfg->wimax_driver_lock);
+ do_exit(0);
+ return 0;
+}
+#endif
+
+static int wimax_power_on(struct wimax732_platform_data *pdata)
+{
+
+ struct net_adapter *adapter = NULL;
+ struct net_device *net;
+ struct wimax_cfg *g_cfg = pdata->g_cfg;
+ struct buffer_descriptor *bufdsc;
+ struct process_descriptor *procdsc;
+ struct list_head *pos, *nxt, *pos1, *nxt1;
+ int count;
+ long ret;
+ int err = 0;
+ u8 node_id[ETH_ALEN];
+
+ mutex_lock(&g_cfg->power_mutex);
+ /*dont sleep when turning on*/
+ mutex_lock(&g_cfg->suspend_mutex);
+
+ /*Exit if wimax is already ON*/
+ if (g_cfg->power_state == CMC_POWER_ON) {
+ pr_debug("WiMAX already ON");
+ err = WIMAX_ALREADY_POWER_ON;
+ goto exit;
+ }
+
+ g_cfg->power_state = CMC_POWERING_ON;
+ pr_debug("WIMAX POWERING ON");
+
+ net = alloc_etherdev(sizeof(*adapter));
+ if (!net) {
+ pr_debug("%s: error can't allocate device", __func__);
+ goto alloceth_fail;
+ }
+
+ adapter = netdev_priv(net);
+ memset(adapter, 0, sizeof(*adapter));
+ adapter->pdata = pdata;
+ pdata->adapter_data = adapter;
+ adapter->net = net;
+
+ init_completion(&adapter->probe);
+ init_completion(&adapter->remove);
+ init_completion(&adapter->firmware_download);
+ init_completion(&adapter->mac);
+
+ pdata->power(1); /*power ON wimax chipset*/
+
+ /*We need to sleep wait for atleast CMC_BOOT_LOAD duration.
+ *using msleep sometimes returns before the requested time
+ * hence the alternative below*/
+ if (wait_for_completion_interruptible_timeout(
+ &adapter->probe,
+ msecs_to_jiffies(CMC_BOOTLOAD_TIME))) {
+ pr_warn("-ERESTARTSYS during CMC_BOOTLOAD_TIME");
+ goto probe_timeout;
+ }
+
+ pdata->detect(1); /*detect the presence of SDIO card*/
+
+ /*wait for SDIO driver probe*/
+ ret = wait_for_completion_interruptible_timeout(
+ &adapter->probe,
+ msecs_to_jiffies(CMC_PROBE_TIMEOUT));
+ if (ret) {
+ if (ret == -ERESTARTSYS) {
+ pr_err("-ERESTARTSYS during CMC_PROBE_TIMEOUT");
+ goto probe_timeout;
+ }
+ } else {
+ pr_err("%s CMC_PROBE_TIMEOUT", __func__);
+ err = WIMAX_POWER_FAIL;
+ goto probe_timeout;
+ }
+
+ /*set the mode pins of modem for the firmware to boot in desired mode*/
+ pdata->set_mode();
+
+ mutex_init(&adapter->control_lock);
+
+ /* For sending data and control packets */
+ INIT_LIST_HEAD(&adapter->q_send);
+ spin_lock_init(&adapter->send_lock);
+ INIT_LIST_HEAD(&adapter->control_process_list);
+
+ INIT_WORK(&adapter->tx_work, tx_process_data);
+
+ /*load the wimax firmware*/
+ if (request_firmware(&adapter->fw, (g_cfg->wimax_mode == AUTH_MODE) ?
+ WIMAX_LOADER_PATH : WIMAX_IMAGE_PATH,
+ &adapter->func->dev)) {
+ dev_err(&adapter->func->dev, "%s: Can't open firmware file\n",
+ __func__);
+ goto firmwareload_fail;
+ }
+
+ sdio_claim_host(adapter->func);
+ err = sdio_enable_func(adapter->func);
+ if (err < 0) {
+ pr_err("sdio_enable func error = %d", err);
+ release_firmware(adapter->fw);
+ goto sdioen_fail;
+ }
+
+ adapter->wimax_workqueue = create_workqueue("wimax_queue");
+ INIT_WORK(&adapter->rx_work, rx_process_data);
+ adapter->receive_buffer = kmalloc(CMC732_MAX_PACKET_SIZE, GFP_KERNEL);
+ if (!adapter->receive_buffer) {
+ pr_err("receive_buffer alloc error");
+ goto buffer_fail;
+ }
+ err = sdio_claim_irq(adapter->func, adapter_interrupt);
+ if (err < 0) {
+ pr_err("sdio_claim_irq = %d", err);
+ release_firmware(adapter->fw);
+ goto sdioirq_fail;
+ }
+ sdio_set_block_size(adapter->func, CMC_BLOCK_SIZE);
+ sdio_release_host(adapter->func);
+ init_waitqueue_head(&adapter->modem_resp_event);
+ count = 0;
+
+ while (!adapter->modem_resp) {
+ /*This command will start the
+ firmware download sequence through sdio*/
+ send_cmd_packet(adapter, MSG_DRIVER_OK_REQ);
+ ret = wait_event_interruptible_timeout(
+ adapter->modem_resp_event,
+ adapter->modem_resp, HZ/10);
+ if (!adapter->modem_resp)
+ pr_err("no modem response");
+ if ((++count > MODEM_RESP_RETRY) || (ret == -ERESTARTSYS)) {
+ release_firmware(adapter->fw);
+ goto firmware_download_fail;
+ }
+ }
+
+ ret = wait_for_completion_interruptible_timeout(
+ &adapter->firmware_download,
+ msecs_to_jiffies(CMC_FIRMWARE_DOWNLOAD_TIMEOUT));
+ if (ret) {
+ if (ret == -ERESTARTSYS) {
+ pr_err("-ERESTARTSYS firmware download fail");
+ release_firmware(adapter->fw);
+ goto firmware_download_fail;
+ }
+ } else {
+ pr_err("%s CMC_FIRMWARE_DOWNLOAD_TIMEOUT", __func__);
+ release_firmware(adapter->fw);
+ goto firmware_download_fail;
+ }
+ release_firmware(adapter->fw);
+
+ /*wait for firmware to initialize before proceeding*/
+ msleep(1700);
+
+
+ /* Dummy value for "ifconfig up" for 2.6.24 */
+ random_ether_addr(node_id);
+ memcpy(net->dev_addr, node_id, sizeof(node_id));
+
+ /*get the MAC when in the following modes*/
+ if (g_cfg->wimax_mode == SDIO_MODE
+ || g_cfg->wimax_mode == DM_MODE
+ || g_cfg->wimax_mode == USB_MODE
+ || g_cfg->wimax_mode == USIM_RELAY_MODE) {
+
+ count = MAC_RETRY_COUNT;
+ do {
+ if (!(count--)) {
+ pr_err("%s MAC request timeout", __func__);
+ goto mac_request_fail;
+ }
+ if (send_mac_request(adapter))
+ goto mac_request_fail;
+ ret = wait_for_completion_interruptible_timeout(
+ &adapter->mac,
+ msecs_to_jiffies((MAC_RETRY_COUNT - count) *
+ MAC_RETRY_INTERVAL));
+ if (ret == -ERESTARTSYS) {
+ pr_err("-ERESTARTSYS MAC request fail");
+ goto mac_request_fail;
+ }
+ } while (!ret);
+
+ }
+#ifdef WIMAX_CON0_POLL
+ if (g_cfg->wimax_mode == WTM_MODE) {
+ init_waitqueue_head(&adapter->con0_poll);
+ adapter->wtm_task = kthread_create(con0_poll_thread,
+ adapter, "%s", "wimax_con0_poll_thread");
+ if (adapter->wtm_task)
+ wake_up_process(adapter->wtm_task);
+ }
+#endif
+ adapter->uwibro_dev.minor = MISC_DYNAMIC_MINOR;
+ adapter->uwibro_dev.name = "uwibro";
+ adapter->uwibro_dev.fops = &uwbr_fops;
+
+ strcpy(net->name, "uwbr%d");
+ net->netdev_ops = &wimax_net_ops;
+ net->watchdog_timeo = ADAPTER_TIMEOUT;
+ net->mtu = WIMAX_MTU_SIZE;
+ adapter->msg_enable = netif_msg_init(msg_level, NETIF_MSG_DRV
+ | NETIF_MSG_PROBE | NETIF_MSG_LINK);
+
+ ether_setup(net);
+ net->flags |= IFF_NOARP;
+
+ SET_NETDEV_DEV(net, &adapter->func->dev);
+ if (register_netdev(net))
+ goto regnetdev_fail;
+
+ netif_carrier_off(net);
+ netif_tx_stop_all_queues(net);
+
+ if (misc_register(&adapter->uwibro_dev)) {
+ pr_err("adapter_probe: misc_register() failed");
+ goto miscreg_fail;
+ }
+
+ cmc732_setup_wake_irq(adapter);
+
+ g_cfg->power_state = CMC_POWER_ON;
+ mutex_unlock(&g_cfg->suspend_mutex);
+ mutex_unlock(&g_cfg->power_mutex);
+ pr_debug("WIMAX ON");
+ return 0;
+
+ misc_deregister(&adapter->uwibro_dev);
+miscreg_fail:
+ unregister_netdev(net);
+regnetdev_fail:
+#ifdef WIMAX_CON0_POLL
+ if (g_cfg->wimax_mode == WTM_MODE) {
+ g_cfg->power_state = CMC_POWERING_OFF;
+ wake_up_interruptible(&adapter->con0_poll);
+ }
+#endif
+mac_request_fail:
+firmware_download_fail:
+ g_cfg->power_state = CMC_POWERING_OFF;
+
+ /*wake up everyone waiting on uwbr_dev read wait*/
+ list_for_each_safe(pos, nxt, &adapter->control_process_list) {
+ procdsc = list_entry(pos, struct process_descriptor, list);
+ wake_up_interruptible(&procdsc->read_wait);
+ }
+
+ list_for_each_safe(pos, nxt, &adapter->q_send) {
+ bufdsc = list_entry(pos, struct buffer_descriptor, list);
+ list_del(pos);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ }
+ list_for_each_safe(pos, nxt, &adapter->control_process_list) {
+ procdsc = list_entry(pos, struct process_descriptor, list);
+ list_del(pos);
+ list_for_each_safe(pos1, nxt1, &procdsc->buffer_list) {
+ bufdsc = list_entry(pos1,
+ struct buffer_descriptor, list);
+ list_del(pos1);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ }
+ kfree(procdsc);
+ }
+ sdio_claim_host(adapter->func);
+ sdio_release_irq(adapter->func);
+
+sdioirq_fail:
+ kfree(adapter->receive_buffer);
+buffer_fail:
+ destroy_workqueue(adapter->wimax_workqueue);
+ sdio_disable_func(adapter->func);
+sdioen_fail:
+ sdio_release_host(adapter->func);
+firmwareload_fail:
+ mutex_destroy(&adapter->control_lock);
+
+probe_timeout:
+
+ pdata->power(0);
+ if (adapter->func) {
+ mdelay(10);
+ pdata->detect(0);
+ if (!wait_for_completion_interruptible_timeout(
+ &adapter->remove,
+ msecs_to_jiffies(2000))) {
+ pr_err("%s CMC_REMOVE_TIMEOUT", __func__);
+ }
+ }
+
+ /*wait for power off transients*/
+ msleep(250);
+
+ free_netdev(net);
+ pdata->adapter_data = NULL;
+alloceth_fail:
+ g_cfg->power_state = CMC_POWER_OFF;
+ err = WIMAX_POWER_FAIL;
+exit:
+ mutex_unlock(&g_cfg->suspend_mutex);
+ mutex_unlock(&pdata->g_cfg->power_mutex);
+ return err;
+
+}
+
+
+static int wimax_power_off(struct wimax732_platform_data *pdata)
+{
+ int err = 0;
+ struct net_adapter *adapter = NULL;
+ struct wimax_cfg *g_cfg = pdata->g_cfg;
+ struct buffer_descriptor *bufdsc;
+ struct process_descriptor *procdsc;
+ struct list_head *pos, *nxt, *pos1, *nxt1;
+
+ mutex_lock(&g_cfg->power_mutex);
+
+ /*dont sleep when turning off*/
+ mutex_lock(&g_cfg->suspend_mutex);
+
+ if (g_cfg->power_state == CMC_POWER_OFF) {
+ pr_debug("WiMAX already OFF");
+ err = WIMAX_ALREADY_POWER_OFF;
+ goto exit;
+ }
+ adapter = (struct net_adapter *) pdata->adapter_data;
+ g_cfg->power_state = CMC_POWERING_OFF;
+#ifdef WIMAX_CON0_POLL
+ if (g_cfg->wimax_mode == WTM_MODE)
+ wake_up_interruptible(&adapter->con0_poll);
+#endif
+
+ cmc732_release_wake_irq(adapter);
+ misc_deregister(&adapter->uwibro_dev);
+ netif_stop_queue(adapter->net);
+ netif_carrier_off(adapter->net);
+ unregister_netdev(adapter->net);
+
+ sdio_claim_host(adapter->func);
+ sdio_release_irq(adapter->func);
+ sdio_disable_func(adapter->func);
+ sdio_release_host(adapter->func);
+
+ /*wake up everyone waiting on uwbr_dev read wait*/
+ list_for_each_safe(pos, nxt, &adapter->control_process_list) {
+ procdsc = list_entry(pos, struct process_descriptor, list);
+ wake_up_interruptible(&procdsc->read_wait);
+ }
+
+ /*clean up and remove the send queue*/
+ list_for_each_safe(pos, nxt, &adapter->q_send) {
+ bufdsc = list_entry(pos, struct buffer_descriptor, list);
+ list_del(pos);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ }
+
+ /*wait for modem to flush eeprom data*/
+ msleep(500);
+
+ pdata->power(0);
+ mdelay(10);
+ pdata->detect(0);
+ if (!wait_for_completion_interruptible_timeout(
+ &adapter->remove,
+ msecs_to_jiffies(2000))) {
+ pr_err("%s CMC_REMOVE_TIMEOUT", __func__);
+ }
+
+ /*wait for power off transients*/
+ msleep(250);
+
+ list_for_each_safe(pos, nxt, &adapter->control_process_list) {
+ procdsc = list_entry(pos, struct process_descriptor, list);
+ list_del(pos);
+ list_for_each_safe(pos1, nxt1, &procdsc->buffer_list) {
+ bufdsc = list_entry(pos1,
+ struct buffer_descriptor, list);
+ list_del(pos1);
+ kfree(bufdsc->buffer);
+ kfree(bufdsc);
+ }
+ kfree(procdsc);
+ }
+ mutex_destroy(&adapter->control_lock);
+ free_netdev(adapter->net);
+ kfree(adapter->receive_buffer);
+ destroy_workqueue(adapter->wimax_workqueue);
+ pdata->adapter_data = NULL;
+ g_cfg->power_state = CMC_POWER_OFF;
+exit:
+ mutex_unlock(&g_cfg->suspend_mutex);
+ mutex_unlock(&g_cfg->power_mutex);
+ return err;
+}
+
+void wimax_shutdown(struct work_struct *work)
+{
+ struct wimax_cfg *g_cfg =
+ container_of(work,
+ struct wimax_cfg, shutdown);
+ wimax_power_off(g_cfg->pdata);
+}
+
+static int swmxdev_open(struct inode *inode, struct file *file)
+{
+ struct wimax732_platform_data *pdata =
+ container_of(file->private_data,
+ struct wimax732_platform_data, swmxctl_dev);
+ file->private_data = pdata;
+ return 0;
+}
+
+
+static long swmxdev_ioctl(struct file *file, u32 cmd,
+ unsigned long arg) {
+ int ret = 0;
+ u8 val = ((u8 *)arg)[0];
+ struct wimax732_platform_data *gpdata =
+ (struct wimax732_platform_data *)(file->private_data);
+
+ pr_debug(" %s CMD: %x, PID: %d", __func__, cmd, current->tgid);
+
+ switch (cmd) {
+ case CONTROL_IOCTL_WIMAX_POWER_CTL: {
+ pr_debug("CONTROL_IOCTL_WIMAX_POWER_CTL..");
+ if (val == 0)
+ wimax_power_off(gpdata);
+ else
+ ret = wimax_power_on(gpdata);
+ break;
+ }
+ case CONTROL_IOCTL_WIMAX_MODE_CHANGE: {
+ pr_debug("CONTROL_IOCTL_WIMAX_MODE_CHANGE to 0x%02x..", val);
+
+ if ((val < 0) || (val > AUTH_MODE)) {
+ pr_debug("Wrong mode 0x%02x", val);
+ return 0;
+ }
+
+ wimax_power_off(gpdata);
+ gpdata->g_cfg->wimax_mode = val;
+ ret = wimax_power_on(gpdata);
+ break;
+ }
+ case CONTROL_IOCTL_WIMAX_EEPROM_DOWNLOAD: {
+ pr_debug("CNT_IOCTL_WIMAX_EEPROM_DOWNLOAD");
+ wimax_power_off(gpdata);
+ ret = eeprom_write_boot();
+ break;
+ }
+ case CONTROL_IOCTL_WIMAX_WRITE_REV: {
+ pr_debug("CONTROL_IOCTL_WIMAX_WRITE_REV");
+ wimax_power_off(gpdata);
+ ret = eeprom_write_rev();
+ break;
+ }
+ case CONTROL_IOCTL_WIMAX_CHECK_CERT: {
+ pr_debug("CONTROL_IOCTL_WIMAX_CHECK_CERT");
+ wimax_power_off(gpdata);
+ ret = eeprom_check_cert();
+ break;
+ }
+ case CONTROL_IOCTL_WIMAX_CHECK_CAL: {
+ pr_debug("CONTROL_IOCTL_WIMAX_CHECK_CAL");
+ wimax_power_off(gpdata);
+ ret = eeprom_check_cal();
+ break;
+ }
+ } /* switch (cmd) */
+
+ return ret;
+}
+
+
+static const struct file_operations swmx_fops = {
+ .owner = THIS_MODULE,
+ .open = swmxdev_open,
+ .unlocked_ioctl = swmxdev_ioctl,
+};
+
+
+static struct sdio_device_id adapter_table[] = {
+ { SDIO_DEVICE(0x98, 0x1) },
+ { } /* Terminating entry */
+};
+
+
+static int adapter_probe(struct sdio_func *func,
+ const struct sdio_device_id *id)
+{
+ struct wimax732_platform_data *pdata =
+ (struct wimax732_platform_data *) id->driver_data;
+ struct net_adapter *adapter = pdata->adapter_data;
+ pr_debug("%s", __func__);
+ adapter->func = func;
+ sdio_set_drvdata(func, adapter);
+ complete(&adapter->probe);
+ return 0;
+}
+
+static void adapter_remove(struct sdio_func *func)
+{
+ struct net_adapter *adapter = sdio_get_drvdata(func);
+ pr_debug("%s", __func__);
+ complete(&adapter->remove);
+}
+
+static struct sdio_driver adapter_driver = {
+ .name = "C732SDIO",
+ .probe = adapter_probe,
+ .remove = adapter_remove,
+ .id_table = adapter_table,
+};
+
+static int wimax_probe(struct platform_device *pdev)
+{
+ struct wimax732_platform_data *pdata = pdev->dev.platform_data;
+ int err = 0;
+ int i;
+
+ pdata->g_cfg = kzalloc(sizeof(struct wimax_cfg), GFP_KERNEL);
+ if (pdata->g_cfg == NULL) {
+ dev_err(&pdev->dev,
+ "failed to allocate memory for module data\n");
+ err = -ENOMEM;
+ goto alloc_fail;
+ }
+ pdata->g_cfg->pdata = pdata;
+
+ /*This mutex prevents simultaneous instances of wimax_power()*/
+ mutex_init(&pdata->g_cfg->power_mutex);
+
+ /*This mutex ensures that driver does not suspend
+ *with modem wakeup pin asserted*/
+ mutex_init(&pdata->g_cfg->suspend_mutex);
+
+ /*To make plaform data available at SDIO driver probe*/
+ for (i = 0; i < ARRAY_SIZE(adapter_table); i++)
+ adapter_table[i].driver_data =
+ (unsigned long) pdev->dev.platform_data;
+
+ pdata->power(0);
+ pdata->g_cfg->power_state = CMC_POWER_OFF;
+
+ /* This node is used for turning on/off wimax*/
+ pdata->swmxctl_dev.minor = MISC_DYNAMIC_MINOR;
+ pdata->swmxctl_dev.name = "swmxctl";
+ pdata->swmxctl_dev.fops = &swmx_fops;
+ err = misc_register(&pdata->swmxctl_dev);
+ if (err) {
+ pr_err("swmxctl: misc_register failed\n");
+ goto err_swmxctl_register;
+ }
+
+ /* register SDIO driver */
+ err = sdio_register_driver(&adapter_driver);
+ if (err < 0) {
+ pr_err("%s: sdio_register_driver() failed",
+ adapter_driver.name);
+ goto sdio_register_fail;
+ }
+ wake_lock_init(&pdata->g_cfg->wimax_driver_lock,
+ WAKE_LOCK_SUSPEND, "wimax_driver");
+
+ INIT_WORK(&pdata->g_cfg->shutdown, wimax_shutdown);
+
+#ifndef DRIVER_BIT_BANG
+ if (wmxeeprom_init())
+ pr_err("wmxeeprom_init() failed");
+#endif
+
+ goto exit;
+
+sdio_register_fail:
+ misc_deregister(&pdata->swmxctl_dev);
+err_swmxctl_register:
+ mutex_destroy(&pdata->g_cfg->suspend_mutex);
+ mutex_destroy(&pdata->g_cfg->power_mutex);
+ kfree(pdata->g_cfg);
+alloc_fail:
+
+exit:
+ return err;
+}
+
+/* wimax suspend function */
+int wimax_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct wimax732_platform_data *pdata = pdev->dev.platform_data;
+
+ /*Dont go to sleep when turining on or turning off*/
+ if (!mutex_trylock(&pdata->g_cfg->power_mutex)) {
+ pr_debug("wimax is turning on/off");
+ return -EBUSY;
+ }
+
+ if (!mutex_trylock(&pdata->g_cfg->suspend_mutex)) {
+ pr_debug("wimax send processing");
+ mutex_unlock(&pdata->g_cfg->power_mutex);
+ return -EBUSY;
+ }
+
+ /* AP active pin LOW */
+ if (pdata->g_cfg->power_state == CMC_POWER_ON) {
+ pdata->signal_ap_active(0);
+ pr_debug("wimax_suspend");
+ }
+ return 0;
+}
+
+/* wimax resume function */
+int wimax_resume(struct platform_device *pdev)
+{
+ struct wimax732_platform_data *pdata = pdev->dev.platform_data;
+ struct net_adapter *adapter;
+ /* AP active pin HIGH */
+ if (pdata->g_cfg->power_state == CMC_POWER_ON) {
+ pdata->signal_ap_active(1);
+ /* wait wakeup noti for 1 sec otherwise suspend again */
+ wake_lock_timeout(&pdata->g_cfg->wimax_driver_lock, 1 * HZ);
+ pr_debug("wimax_resume");
+ }
+ mutex_unlock(&pdata->g_cfg->power_mutex);
+ mutex_unlock(&pdata->g_cfg->suspend_mutex);
+ if (pdata->g_cfg->power_state == CMC_POWER_ON) {
+ adapter = (struct net_adapter *)pdata->adapter_data;
+ schedule_work(&adapter->tx_work);
+ }
+ return 0;
+}
+static int wimax_remove(struct platform_device *pdev)
+{
+ struct wimax732_platform_data *pdata = pdev->dev.platform_data;
+
+#ifndef DRIVER_BIT_BANG
+ wmxeeprom_exit();
+#endif
+ misc_deregister(&pdata->swmxctl_dev);
+ pdata->power(0);
+ wake_lock_destroy(&pdata->g_cfg->wimax_driver_lock);
+ sdio_unregister_driver(&adapter_driver);
+ mutex_destroy(&pdata->g_cfg->power_mutex);
+ mutex_destroy(&pdata->g_cfg->suspend_mutex);
+ kfree(pdata->g_cfg);
+ return 0;
+}
+
+
+static struct platform_driver wimax_driver = {
+ .probe = wimax_probe,
+ .remove = wimax_remove,
+ .suspend = wimax_suspend,
+ .resume = wimax_resume,
+ .driver = {
+ .name = "wimax732_driver",
+ }
+};
+
+static int __init adapter_init_module(void)
+{
+ return platform_driver_register(&wimax_driver);
+}
+
+
+static void __exit adapter_deinit_module(void)
+{
+ platform_driver_unregister(&wimax_driver);
+}
+
+module_init(adapter_init_module);
+module_exit(adapter_deinit_module);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_PARM_DESC(msg_level, "Override default message level");
+MODULE_DEVICE_TABLE(sdio, adapter_table);
+MODULE_VERSION(WIMAX_DRIVER_VERSION_STRING);
+MODULE_LICENSE("GPL");
diff --git a/drivers/net/wimax_cmc/wimax_sdio.h b/drivers/net/wimax_cmc/wimax_sdio.h
new file mode 100644
index 0000000..998c695
--- /dev/null
+++ b/drivers/net/wimax_cmc/wimax_sdio.h
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+#ifndef _WIMAX_SDIO_H
+#define _WIMAX_SDIO_H
+
+#include <linux/miscdevice.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/netdevice.h>
+#include <linux/completion.h>
+#include <linux/firmware.h>
+#include <asm/byteorder.h>
+#define WIMAX_CON0_POLL
+
+/* Macro definition for defining IOCTL */
+#define CTL_CODE(DeviceType, Function, Method, Access) \
+( \
+ ((DeviceType) << 16) | ((Access) << 14) | \
+ ((Function) << 2) | (Method) \
+)
+/* Define the method codes for how buffers are passed for I/O and FS controls */
+#define METHOD_BUFFERED 0
+
+/*
+* Define the access check value for any access
+
+* The FILE_READ_ACCESS and FILE_WRITE_ACCESS constants are also defined in
+* ntioapi.h as FILE_READ_DATA and FILE_WRITE_DATA. The values for these
+* constants *MUST* always be in sync.
+*/
+#define FILE_ANY_ACCESS 0
+
+#define CONTROL_ETH_TYPE_WCM 0x0015
+#ifndef FILE_DEVICE_UNKNOWN
+#define FILE_DEVICE_UNKNOWN 0x89
+#endif
+#define CONTROL_IOCTL_WRITE_REQUEST \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x820, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define CONTROL_IOCTL_WIMAX_POWER_CTL \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x821, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define CONTROL_IOCTL_WIMAX_MODE_CHANGE \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x838, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define CONTROL_IOCTL_WIMAX_EEPROM_DOWNLOAD \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x839, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define CONTROL_IOCTL_WIMAX_WRITE_REV \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83B, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define CONTROL_IOCTL_WIMAX_CHECK_CERT \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83C, METHOD_BUFFERED, FILE_ANY_ACCESS)
+#define CONTROL_IOCTL_WIMAX_CHECK_CAL \
+ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x83D, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+#define ETHERNET_ADDRESS_LENGTH 6
+
+/* eth types for control message */
+enum {
+ ETHERTYPE_HIM = 0x1500,
+ ETHERTYPE_MC = 0x1501,
+ ETHERTYPE_DM = 0x1502,
+ ETHERTYPE_CT = 0x1503,
+ ETHERTYPE_DL = 0x1504,
+ ETHERTYPE_VSP = 0x1510,
+ ETHERTYPE_AUTH = 0x1521
+};
+
+#define WIMAX_IMAGE_PATH "wimaxfw.bin"
+#define WIMAX_LOADER_PATH "wimaxloader.bin"
+
+/*Time(ms) taken for CMC bootloader to be ready for firmware download*/
+#define CMC_BOOTLOAD_TIME 250
+
+ /*Time(ms) to wait for SDIO probe after SDIO force detect call*/
+#define CMC_PROBE_TIMEOUT 1000
+
+ /*Time(ms) to wait the firmware to complete downloading*/
+#define CMC_FIRMWARE_DOWNLOAD_TIMEOUT 2000
+
+/*maximum time(ms) spent for MAC request */
+#define CMC_MAC_TIMEOUT 9000
+
+#define MODEM_RESP_RETRY 10
+#define ADAPTER_TIMEOUT (HZ * 10)
+
+#define CMC732_RAM_START 0xC0000000
+#define CMC732_WIMAX_ADDRESS CMC732_RAM_START
+
+#define CMD_MSG_TOTAL_LENGTH 12
+#define IMAGE_INFO_MSG_TOTAL_LENGTH 28
+#define CMD_MSG_LENGTH 0
+#define IMAGE_INFO_MSG_LENGTH 16
+#define MAX_IMAGE_DATA_LENGTH 3564
+#define MAX_IMAGE_DATA_MSG_LENGTH 4096
+
+
+/*
+* SDIO general defines
+* size of a bank in cmc732's rx and tx buffers
+*/
+#define CMC732_SDIO_BANK_SIZE 4096
+#define CMC732_PACKET_LENGTH_SIZE 4
+#define CMC732_MAX_PACKET_SIZE (CMC732_SDIO_BANK_SIZE \
+ - CMC732_PACKET_LENGTH_SIZE)
+#define CMC_BLOCK_SIZE 512
+
+#define WIMAX_MTU_SIZE 1400
+#define WIMAX_MAX_FRAMESIZE 1500
+#define WIMAX_HEADER_SIZE 14
+#define WIMAX_MAX_TOTAL_SIZE (WIMAX_MAX_FRAMESIZE + WIMAX_HEADER_SIZE)
+/* maximum allocated data size, mtu 1400 so 3 blocks max 1536 */
+#define BUFFER_DATA_SIZE 1600
+
+/*maximum permitted SDIO error before wimax restart*/
+#define MAX_SDIO_ERROR 10
+/* SDIO function addresses */
+#define SDIO_TX_BANK_ADDR 0x1000
+#define SDIO_RX_BANK_ADDR 0x10000
+#define SDIO_INT_STATUS_REG 0xC0
+#define SDIO_INT_STATUS_CLR_REG 0xC4
+
+#define SDIO_C2H_WP_REG 0xE4
+#define SDIO_C2H_RP_REG 0xE8
+#define SDIO_H2C_WP_REG 0xEC
+#define SDIO_H2C_RP_REG 0xF0
+
+#define SDIO_INT_DATA_READY 0x01
+#define SDIO_INT_ERROR 0x02
+
+
+#define MAC_RETRY_COUNT 9
+#define MAC_RETRY_INTERVAL 200
+
+#define WAKEUP_MAX_TRY 60
+#define WAKEUP_ASSERT_T 100
+
+#define CMC_BLOCK_SIZE 512
+#define CMC_MAX_BYTE_SIZE (CMC_BLOCK_SIZE - 1)
+/* used for host boot (firmware download) */
+enum {
+ MSG_DRIVER_OK_REQ = 0x5010,
+ MSG_DRIVER_OK_RESP = 0x6010,
+ MSG_IMAGE_INFO_REQ = 0x3021,
+ MSG_IMAGE_INFO_RESP = 0x4021,
+ MSG_IMAGE_DATA_REQ = 0x3022,
+ MSG_IMAGE_DATA_RESP = 0x4022,
+ MSG_RUN_REQ = 0x5014,
+ MSG_RUN_RESP = 0x6014
+};
+
+/* private packet opcodes */
+enum {
+ HWCODEMACREQUEST = 0x01,
+ HWCODEMACRESPONSE,
+ HWCODELINKINDICATION,
+ HWCODERXREADYINDICATION,
+ HWCODEHALTEDINDICATION,
+ HWCODEIDLENTFY,
+ HWCODEWAKEUPNTFY
+};
+
+#define HW_PROT_VALUE_LINK_DOWN 0x00
+#define HW_PROT_VALUE_LINK_UP 0xff
+
+/* process element managed by control type */
+struct process_descriptor {
+ struct list_head list;
+ struct list_head buffer_list;
+ wait_queue_head_t read_wait;
+ u32 id;
+ u16 type;
+};
+
+struct buffer_descriptor {
+ struct list_head list; /* list node */
+ u8 *buffer; /* allocated buffer: */
+ s32 length; /* current data length */
+};
+
+struct image_data_payload {
+ u32 offset;
+ u32 size;
+ u8 data[MAX_IMAGE_DATA_LENGTH];
+};
+
+#pragma pack(1)
+
+/* eth header structure */
+struct eth_header {
+ u8 dest[ETHERNET_ADDRESS_LENGTH];
+ u8 src[ETHERNET_ADDRESS_LENGTH];
+ u16 type;
+};
+
+struct hw_packet_header {
+ char id0; /* packet ID */
+ char id1;
+ u16 length; /* packet length */
+};
+
+struct hw_private_packet {
+ char id0; /* packet ID */
+ char id1;
+ u8 code; /* command code */
+ u8 value; /* command value */
+};
+
+
+struct wimax_msg_header {
+ u16 type;
+ u16 id;
+ u32 length;
+};
+
+#pragma pack()
+
+/* network adapter structure */
+struct net_adapter {
+ struct completion probe;
+ struct completion firmware_download;
+ struct completion mac;
+ struct completion remove;
+ wait_queue_head_t modem_resp_event;
+ wait_queue_head_t con0_poll;
+ struct workqueue_struct *wimax_workqueue;
+ struct work_struct rx_work;
+ struct work_struct tx_work;
+ struct sdio_func *func;
+ struct net_device *net;
+ struct net_device_stats netstats;
+ struct miscdevice uwibro_dev;
+ const struct firmware *fw;
+ struct list_head q_send; /* send pending queue */
+ spinlock_t send_lock;
+ struct list_head control_process_list;
+ struct mutex control_lock;
+ struct wimax732_platform_data *pdata;
+#ifdef WIMAX_CON0_POLL
+ struct task_struct *wtm_task;
+#endif
+ struct sk_buff *rx_skb;
+ u8 *receive_buffer;
+ u32 buff_len;
+ u32 image_offset;
+ u32 msg_enable;
+ s32 wake_irq;
+ u32 sdio_error_count;
+ u8 eth_header[ETHERNET_ADDRESS_LENGTH * 2];
+ bool modem_resp;
+};
+
+#endif /* _WIMAX_SDIO_H */