diff options
Diffstat (limited to 'drivers/net/wimax_cmc')
-rw-r--r-- | drivers/net/wimax_cmc/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/Makefile | 4 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/firmware.c | 247 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/firmware.h | 29 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/wimax_i2c.c | 854 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/wimax_i2c.h | 53 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/wimax_sdio.c | 1789 | ||||
-rw-r--r-- | drivers/net/wimax_cmc/wimax_sdio.h | 259 |
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(ðcmd, 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 */ |