diff options
Diffstat (limited to 'drivers/staging/westbridge/astoria/device/cyasdevice_na_spr.c')
-rwxr-xr-x | drivers/staging/westbridge/astoria/device/cyasdevice_na_spr.c | 1560 |
1 files changed, 1560 insertions, 0 deletions
diff --git a/drivers/staging/westbridge/astoria/device/cyasdevice_na_spr.c b/drivers/staging/westbridge/astoria/device/cyasdevice_na_spr.c new file mode 100755 index 0000000..ef3970c --- /dev/null +++ b/drivers/staging/westbridge/astoria/device/cyasdevice_na_spr.c @@ -0,0 +1,1560 @@ +/* +## cyandevice.c - Linux Antioch device driver file +## =========================== +## Copyright (C) 2010 Cypress Semiconductor +## +## This program is free software; you can redistribute it and/or +## modify it under the terms of the GNU General Public License +## as published by the Free Software Foundation; either version 2 +## of the License, or (at your option) any later version. +## +## 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. +## +## You should have received a copy of the GNU General Public License +## along with this program; if not, write to the Free Software +## Foundation, Inc., 51 Franklin Street, Fifth Floor +## Boston, MA 02110-1301, USA. +## =========================== +*/ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/workqueue.h> + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/sched.h> +#include <linux/scatterlist.h> +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <linux/semaphore.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +/* moved for staging location + * update/patch submission +#include <linux/westbridge/cyastoria.h> +#include <linux/westbridge/cyashal.h> +#include <linux/westbridge/cyasregs.h> +*/ + +#include "../include/linux/westbridge/cyastoria.h" +#include "../include/linux/westbridge/cyashal.h" +#include "../include/linux/westbridge/cyasregs.h" +#include "../arch/arm/plat-c110/include/mach/westbridge/westbridge-c110-pnand-hal/cyasmemmap.h" + +#ifdef WESTBRIDGE_ASTORIA +#include "firmware/cyastfw_sd_mmc_rel_silicon.h" +#include "firmware/cyastfw_mtp_sd_mmc_rel_silicon.h" +#endif + +/* API exports include file */ +#include "cyandevice_export.h" +#include "cyasdiagnostics.h" + +#define __USE_ISR_FOR_SD_DETECT__ +#ifdef __USE_CYAS_AUTO_SUSPEND__ +#define CYASDEVICE_THREAD_EXIT (1 << 0) +#define CYASDEVICE_THREAD_SUSPENDED (1 << 1) +#define CYASDEVICE_THREAD_TIMER (1 << 2) + +#define CYASDEVICE_THREAD_ENABLE (1 << 7) + +#define CYASDEVICE_STANDBY_TIMEOUT 200 +#endif + +typedef struct { + struct delayed_work work; + int f_reload; + int f_status; + int f_isrunning; +} cy_work_t; + +typedef struct cyasdevice { + /* Handle to the Antioch device */ + cy_as_device_handle dev_handle; + /* Handle to the HAL */ + cy_as_hal_device_tag hal_tag; + /* spinlock_t common_lock; */ + struct semaphore common_sema; + unsigned long flags; + + struct workqueue_struct *cy_wq; + cy_work_t *cy_work; + struct semaphore wq_sema; + uint8_t f_platform_driver; + +#ifdef __USE_CYAS_AUTO_SUSPEND__ + struct completion thread_sleep_complete; + wait_queue_head_t thread_sleep_wq; + struct semaphore thread_sleep_sem; + unsigned long thread_sleep_flags; + unsigned long thread_sleep_enable; + int thread_sleep_count; +#endif +} cyasdevice; + +#ifdef __USE_CYAS_AUTO_SUSPEND__ +int cyasdevice_wakeup_thread(int flag); +int cyasdevice_enable_thread(void); +int cyasdevice_disable_thread(void); +#endif + +/* global ptr to astoria device */ +static cyasdevice *cy_as_device_controller; +int cy_as_device_init_done; +const char *dev_handle_name = "cy_astoria_dev_handle"; + +static int firmware_number; +static int f_isStandby; + +extern int cyasblkdev_start_sdcard(void); +extern int cyasblkdev_stop_sdcard(void); + +extern int cyasblkdev_check_sdcard(void); +extern int cyasblkdev_get_serial(void); +extern int cyasblkdev_get_CID(char *ptrCID); +extern int cyasblkdev_blk_init(int fmajor, int fsearch); +extern void cyasblkdev_blk_exit(void); +int cyasdevice_reload_firmware(int mtp_mode); +void cy_as_acquire_common_lock(void); +void cy_as_release_common_lock(void); +int cyasdevice_enter_standby(void); +int cyasdevice_leave_standby(void); + +#ifdef CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL +extern void cy_as_hal_config_c_s_mux(void); +#endif + +extern int cy_as_hal_detect_SD(void); + +#ifdef __USE_ISR_FOR_SD_DETECT__ +extern int cy_as_hal_enable_irq(void); +extern int cy_as_hal_disable_irq(void); +extern int cy_as_hal_configure_sd_isr(void *dev_p, irq_handler_t isr_function); +extern int cy_as_hal_free_sd_isr(void); +extern int cy_as_hal_enable_power(cy_as_hal_device_tag tag, int flag); +#endif + + +extern int cy_as_hal_enable_NANDCLK(int flag); + +int g_SD_flag; +static void cy_wq_function(struct work_struct *work) +{ + int status; + int is_inserted; + int ret; + int retry_count; + + down(&cy_as_device_controller->thread_sleep_sem); + + down(&cy_as_device_controller->wq_sema); + cy_as_device_controller->cy_work->f_isrunning = 1; + up(&cy_as_device_controller->wq_sema); + + status = cy_as_device_controller->cy_work->f_status; + g_SD_flag = is_inserted = cy_as_hal_detect_SD(); + + if (cy_as_device_controller->cy_work->f_reload == 1) { + cy_as_device_controller->cy_work->f_reload = 0; + cy_as_hal_enable_power(cy_as_device_controller->hal_tag, 1); + } + + #ifdef __USE_CYAS_AUTO_SUSPEND__ + cyasdevice_disable_thread(); + #endif + + if (!is_inserted) { /* removed SD card. */ + + cy_as_hal_print_message(KERN_ERR + "[cyasdevice.c] %s: SD card is removed.\n", __func__) ; + if (status) { + cy_as_hal_print_message(KERN_ERR + "[cyasdevice.c] %s: cyasblkdev_blk_exit\n", __func__) ; + down(&cy_as_device_controller->wq_sema); + cyasdevice_leave_standby(); + cy_as_device_controller->cy_work->f_status = 0x0; + up(&cy_as_device_controller->wq_sema); + + cyasblkdev_blk_exit(); + retry_count = 0; + while (!cy_as_device_init_done) { + if (++retry_count == 5) { + cyasdevice_reload_firmware(0); + break; + } + msleep(10); + } + + } + down(&cy_as_device_controller->wq_sema); + cyasdevice_enter_standby(); + up(&cy_as_device_controller->wq_sema); + msleep(10); + } else { /* insterted SD card. */ + + cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s:\ + leave standby\n", __func__) ; + down(&cy_as_device_controller->wq_sema); + cyasdevice_leave_standby(); + up(&cy_as_device_controller->wq_sema); + + retry_count = 0; + while (!cy_as_device_init_done) { + if (++retry_count == 5) { + cyasdevice_reload_firmware(0); + break; + } + msleep(10); + } + + cy_as_hal_print_message(KERN_ERR + "++++++++++++ [cyasdevice.c] %s: SD card is inserted.\n", + __func__) ; + if (cy_as_device_controller->cy_work->f_reload == 2) { + cy_as_device_controller->cy_work->f_reload = 0; + + ret = cyasblkdev_check_sdcard(); + if (ret == 1) { + cy_as_hal_print_message( + "[cyasdevice.c] %s: cyasblkdev_blk_exit\n", + __func__) ; + cyasblkdev_blk_exit(); + status = + cy_as_device_controller->cy_work->f_status = 0x0; + } else if (ret < 0) { + cyasblkdev_blk_exit(); + cy_as_hal_print_message( + "[cyasdevice.c] %s: reload firmware\n", __func__) ; + status = cy_as_device_controller->cy_work->f_status = 0x0; + } else { + #ifdef __USE_CYAS_AUTO_SUSPEND__ + cyasdevice_enable_thread(); + #endif + } + } + if (status == 0) { + cy_as_hal_print_message(KERN_ERR + "[cyasdevice.c] %s: cyasblkdev_blk_init\n", __func__) ; + cy_as_device_controller->cy_work->f_status = 0x1; + + retry_count = 2; + + while (retry_count--) { + ret = cyasblkdev_blk_init(0, 0); + + if (ret) { + cyasblkdev_blk_exit(); + msleep(10); + cyasdevice_reload_firmware(-1); + msleep(10); + } else + break; + } + + if (ret) { + down(&cy_as_device_controller->wq_sema); + cyasdevice_enter_standby(); + up(&cy_as_device_controller->wq_sema); + msleep(10); + + cy_as_device_controller->cy_work->f_isrunning = 0; + up(&cy_as_device_controller->thread_sleep_sem); + return; + } + } + + #ifdef __USE_CYAS_AUTO_SUSPEND__ + /* cyasdevice_enable_thread(); */ + #endif + } + cy_as_device_controller->cy_work->f_isrunning = 0; + + up(&cy_as_device_controller->thread_sleep_sem); + + return; +} +#ifdef __USE_ISR_FOR_SD_DETECT__ +static irqreturn_t cyasdevice_int_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + int cur_SD_flag = cy_as_hal_detect_SD(); + + if (g_SD_flag != cur_SD_flag) { + g_SD_flag = cur_SD_flag; + queue_delayed_work(cy_as_device_controller->cy_wq, + &cy_as_device_controller->cy_work->work, 0); + } + return IRQ_HANDLED ; +} +#endif +/* below structures and functions primarily + * implemented for firmware loading */ +#ifdef __CYAS_SYSFS_FOR_DIAGNOSTICS__ + +extern int cy_as_diagnostics(cy_as_diag_cmd_type mode, char *result); + +char cyas_diagnostics[1024]; + +static ssize_t show_cyas_diagnostics(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%s\n", cyas_diagnostics); +} + +static ssize_t store_cyas_diagnostics(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + int mode = 0; + int i = 0, len; + + sscanf(buf, "%s", cyas_diagnostics); + + len = strnlen(buf, PAGE_SIZE); + + while (i < len) { + if (buf[i] < 0x30 || buf[i] > 0x39) + break; + + mode += (buf[i]&0x0F); + + i++; + + if (i == len) + break; + else if (buf[i] == 0xa) + break; + else + mode *= 10; + } + + cy_as_hal_print_message("[%s] mode=%d\n", __func__, mode); + + switch (mode) { + case 1: + ret = cy_as_diagnostics(CY_AS_DIAG_GET_VERSION, cyas_diagnostics); + break; + case 2: + ret = cy_as_diagnostics(CY_AS_DIAG_DISABLE_MSM_SDIO, cyas_diagnostics); + break; + case 3: + ret = cy_as_diagnostics(CY_AS_DIAG_ENABLE_MSM_SDIO, cyas_diagnostics); + break; + case 4: + ret = cy_as_diagnostics(CY_AS_DIAG_LEAVE_STANDBY, cyas_diagnostics); + break; + case 5: + ret = cy_as_diagnostics(CY_AS_DIAG_ENTER_STANDBY, cyas_diagnostics); + break; + case 6: + ret = cy_as_diagnostics(CY_AS_DIAG_CREATE_BLKDEV, cyas_diagnostics); + break; + case 7: + ret = cy_as_diagnostics(CY_AS_DIAG_DESTROY_BLKDEV, cyas_diagnostics); + break; + case 11: + ret = cy_as_diagnostics(CY_AS_DIAG_SD_MOUNT, cyas_diagnostics); + break; + case 12: + ret = cy_as_diagnostics(CY_AS_DIAG_SD_READ, cyas_diagnostics); + break; + case 13: + ret = cy_as_diagnostics(CY_AS_DIAG_SD_WRITE, cyas_diagnostics); + break; + case 14: + ret = cy_as_diagnostics(CY_AS_DIAG_SD_UNMOUNT, cyas_diagnostics); + break; + case 21: + ret = cy_as_diagnostics(CY_AS_DIAG_CONNECT_UMS, cyas_diagnostics); + break; + case 22: + ret = cy_as_diagnostics(CY_AS_DIAG_DISCONNECT_UMS, cyas_diagnostics); + break; + case 31: + ret = cy_as_diagnostics(CY_AS_DIAG_CONNECT_MTP, cyas_diagnostics); + break; + case 32: + ret = cy_as_diagnostics(CY_AS_DIAG_DISCONNECT_MTP, cyas_diagnostics); + break; + case 40: + ret = cy_as_diagnostics(CY_AS_DIAG_TEST_RESET_LOW, cyas_diagnostics); + break; + case 41: + ret = cy_as_diagnostics(CY_AS_DIAG_TEST_RESET_HIGH, cyas_diagnostics); + break; + default: + ret = 1; + break; + } + + if (ret) + return 0; + + return len; +} + +static DEVICE_ATTR(cyas_diagnostics, + S_IRUGO | S_IWUSR, + show_cyas_diagnostics, + store_cyas_diagnostics); + +static int serial; +static ssize_t show_cyas_serial(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + serial = cyasblkdev_get_serial(); + return snprintf(buf, PAGE_SIZE, "0x%08x\n", serial); +} + +static ssize_t store_cyas_serial(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + return strlen(buf); +} + +static DEVICE_ATTR(serial, S_IRUGO | S_IWUSR, show_cyas_serial, store_cyas_serial); + +static int wbcid[4]; +static ssize_t show_cyas_wbcid(struct device *dev, struct device_attribute *attr, char *buf) +{ + int size; + size = cyasblkdev_get_CID((char *)wbcid); + return snprintf(buf, PAGE_SIZE, "%08x%08x%08x%08x\n", wbcid[0], wbcid[1], wbcid[2], wbcid[3]); +} + +static ssize_t store_cyas_wbcid(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + return strlen(buf); +} + +static DEVICE_ATTR(wbcid, S_IRUGO | S_IWUSR, show_cyas_wbcid, store_cyas_wbcid); + +#endif +static struct platform_device *westbridge_pd; + +static int __devinit wb_probe(struct platform_device *devptr) +{ + cy_as_hal_print_message("%s called\n", __func__); + return 0; +} + +static int __devexit wb_remove(struct platform_device *devptr) +{ + cy_as_hal_print_message("%s called\n", __func__); + return 0; +} + +static int wb_suspend(struct platform_device *devptr, pm_message_t pm) +{ + disable_irq(WB_SDCD_IRQ_INT); + + down(&cy_as_device_controller->wq_sema); + if (cy_as_device_controller->cy_work->f_isrunning) { + up(&cy_as_device_controller->wq_sema); + enable_irq(WB_SDCD_IRQ_INT); + return -1; + } + up(&cy_as_device_controller->wq_sema); + + /* cy_as_hal_print_message(KERN_ERR"%s called\n", __func__); */ + /* down(&cy_as_device_controller->wq_sema); */ + cyasdevice_disable_thread(); + cancel_delayed_work(&cy_as_device_controller->cy_work->work); + + cyasdevice_enter_standby(); + /* disable clock */ + /* cy_as_hal_sleep(10); */ + msleep(10); + disable_irq(WB_CYAS_IRQ_INT); + gpio_set_value(WB_CLK_EN, 0); + cy_as_hal_enable_NANDCLK(0); + cy_as_hal_enable_power(cy_as_device_controller->hal_tag, 0); + /* up(&cy_as_device_controller->wq_sema); */ + return 0; +} + +static int wb_resume(struct platform_device *devptr) +{ + int retval; + int sd_status; + /* cy_as_hal_print_message(KERN_ERR"%s called\n", __func__); */ + /* cyasdevice_leave_standby(); */ + /* down(&cy_as_device_controller->wq_sema); */ + cy_as_hal_enable_power(cy_as_device_controller->hal_tag, 1); + cy_as_hal_enable_NANDCLK(1); + gpio_set_value(WB_CLK_EN, 1); + msleep(10); + /* cy_as_hal_sleep(10); */ + /* up(&cy_as_device_controller->wq_sema); */ + enable_irq(WB_CYAS_IRQ_INT); + msleep(5); + sd_status = cy_as_hal_detect_SD(); + if (sd_status) { + down(&cy_as_device_controller->wq_sema); + cy_as_device_controller->cy_work->f_isrunning = 1; + up(&cy_as_device_controller->wq_sema); + retval = cyasdevice_wakeup_thread(1); + + if (retval == 0) { + msleep(10); + cyasblkdev_stop_sdcard(); + retval = cyasblkdev_start_sdcard(); + /* retval = cy_as_misc_storage_changed( + cy_as_device_controller->dev_handle, 0, 0); */ + if (retval != CY_AS_ERROR_SUCCESS) { + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR + "%s : fail in cy_as_misc_storage_changed (%d)\n", + __func__, retval); + #endif + } + } + down(&cy_as_device_controller->wq_sema); + cy_as_device_controller->cy_work->f_isrunning = 0; + up(&cy_as_device_controller->wq_sema); + } + if (sd_status || cy_as_device_controller->cy_work->f_status) { + cy_as_device_controller->cy_work->f_reload = 2; + queue_delayed_work(cy_as_device_controller->cy_wq, + &cy_as_device_controller->cy_work->work, 0); + } + enable_irq(WB_SDCD_IRQ_INT); + return 0; +} + +static struct platform_driver west_bridge_driver = { + .probe = wb_probe, + .remove = __devexit_p(wb_remove), + .suspend = wb_suspend, + .resume = wb_resume, + .driver = { + .name = "west_bridge_dev"}, +}; + + +static void cyasdevice_deinit(cyasdevice *cy_as_dev) +{ + cy_as_hal_print_message("<1>_cy_as_device deinitialize called\n"); + if (!cy_as_dev) { + cy_as_hal_print_message("<1>_cy_as_device_deinit: " + "device handle %x is invalid\n", (uint32_t)cy_as_dev); + return; + } + + /* stop west_brige */ + if (cy_as_dev->dev_handle) { + cy_as_hal_print_message("<1>_cy_as_device: " + "cy_as_misc_destroy_device called\n"); + if (cy_as_misc_destroy_device(cy_as_dev->dev_handle) != + CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message( + "<1>_cy_as_device: destroying failed\n"); + } + } + + if (cy_as_dev->hal_tag) { + + #ifdef CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL + if (stop_o_m_a_p_kernel(dev_handle_name, + cy_as_dev->hal_tag) != 0) + cy_as_hal_print_message("<1>_cy_as_device: stopping " + "OMAP kernel HAL failed\n"); + + #endif +#ifdef CONFIG_MACH_C110_WESTBRIDGE_AST_PNAND_HAL + if (cy_as_hal_c110_pnand_stop(dev_handle_name, cy_as_dev->hal_tag) != 0) + cy_as_hal_print_message("<1>_cy_as_device: stopping C110 CRAM HAL failed\n"); +#endif + + } + cy_as_hal_print_message("<1>_cy_as_device:HAL layer stopped\n"); + + if (cy_as_dev->f_platform_driver) { + cy_as_hal_print_message("<1>_cy_as_device: remove cyas_diagnostics\n") ; + #ifdef __CYAS_SYSFS_FOR_DIAGNOSTICS__ + device_remove_file(&westbridge_pd->dev, &dev_attr_cyas_diagnostics); + device_remove_file(&westbridge_pd->dev, &dev_attr_serial); + device_remove_file(&westbridge_pd->dev, &dev_attr_wbcid); + #endif + + cy_as_hal_print_message("<1>_cy_as_device: unregister west_bridge_driver\n") ; + platform_driver_unregister(&west_bridge_driver); + } + + kfree(cy_as_dev); + cy_as_device_controller = NULL; + cy_as_hal_print_message("<1>_cy_as_device: deinitialized\n"); +} + +/*called from src/cyasmisc.c:MyMiscCallback() as a func + * pointer [dev_p->misc_event_cb] which was previously + * registered by CyAsLLRegisterRequestCallback(..., + * MyMiscCallback); called from CyAsMiscConfigureDevice() + * which is in turn called from cyasdevice_initialize() in + * this src + */ +static void cy_misc_callback(cy_as_device_handle h, + cy_as_misc_event_type evtype, void *evdata) +{ + (void)h; + (void)evdata; + + switch (evtype) { + case cy_as_event_misc_initialized: + cy_as_hal_print_message("<1>_cy_as_device: " + "initialization done callback triggered\n"); + cy_as_device_init_done = 1; + break; + + case cy_as_event_misc_awake: + cy_as_hal_print_message("<1>_cy_as_device: " + "cy_as_event_misc_awake event callback triggered\n"); + cy_as_device_init_done = 1; + break; + default: + break; + } +} + +void cy_as_acquire_common_lock() +{ + /* if (cy_as_device_controller) */ + /*while(!cy_as_device_controller);*/ + /* printk("%s called by %s\n", __func__, caller); */ + down(&cy_as_device_controller->common_sema); +} +EXPORT_SYMBOL(cy_as_acquire_common_lock); + +void cy_as_release_common_lock(void) +{ + /* if (cy_as_device_controller) */ + /*while(!cy_as_device_controller);*/ + /* printk("%s called by %s\n", __func__, caller); */ + up(&cy_as_device_controller->common_sema); +} +EXPORT_SYMBOL(cy_as_release_common_lock); + +/* reset astoria and reinit all regs */ + #define PNAND_REG_CFG_INIT_VAL 0x0000 +void hal_reset(cy_as_hal_device_tag tag) +{ + cy_as_hal_print_message("<1> send soft hard rst: " + "MEM_RST_CTRL_REG_HARD...\n"); + cy_as_hal_write_register(tag, CY_AS_MEM_RST_CTRL_REG, + CY_AS_MEM_RST_CTRL_REG_HARD); + mdelay(60); + + cy_as_hal_print_message("<1> after RST: si_rev_REG:%x, " + "PNANDCFG_reg:%x\n", + cy_as_hal_read_register(tag, CY_AS_MEM_CM_WB_CFG_ID), + cy_as_hal_read_register(tag, CY_AS_MEM_PNAND_CFG) + ); + + /* set it to LBD */ + cy_as_hal_write_register(tag, CY_AS_MEM_PNAND_CFG, + PNAND_REG_CFG_INIT_VAL); +} +EXPORT_SYMBOL(hal_reset); + + + + +/* west bridge device driver main init */ +static int cyasdevice_initialize(void) +{ + cyasdevice *cy_as_dev = 0; + int ret = 0; + int retval = 0; + cy_as_device_config config; + cy_as_hal_sleep_channel channel; + cy_as_get_firmware_version_data ver_data = {0}; + const char *str = ""; + int spin_lim; + const struct firmware *fw_entry; + + cy_as_device_init_done = 0; + + cy_as_misc_set_log_level(8); + + cy_as_hal_print_message("<1>_cy_as_device initialize called\n"); + + if (cy_as_device_controller != 0) { + cy_as_hal_print_message("<1>_cy_as_device: the device " + "has already been initilaized. ignoring\n"); + return -EBUSY; + } + + /* cy_as_dev = CyAsHalAlloc (sizeof(cyasdevice), SLAB_KERNEL); */ + cy_as_dev = cy_as_hal_alloc(sizeof(cyasdevice)); + if (cy_as_dev == NULL) { + cy_as_hal_print_message("<1>_cy_as_device: " + "memory allocation failed\n"); + return -ENOMEM; + } + memset(cy_as_dev, 0, sizeof(cyasdevice)); + + + /* Init the HAL & CyAsDeviceHandle */ + + #ifdef CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL + /* start OMAP HAL init instsnce */ + + if (!start_o_m_a_p_kernel(dev_handle_name, + &(cy_as_dev->hal_tag), cy_false)) { + + cy_as_hal_print_message( + "<1>_cy_as_device: start OMAP34xx HAL failed\n"); + goto done; + } + #endif + +#ifdef CONFIG_MACH_C110_WESTBRIDGE_AST_PNAND_HAL + /* start C110 HAL init instsnce */ + if (!cy_as_hal_c110_pnand_start(dev_handle_name, + &(cy_as_dev->hal_tag), cy_false)) { + cy_as_hal_print_message("<1>_cy_as_device: start C110 HAL failed\n") ; + goto done; + } +#endif + /* Now create the device */ + if (cy_as_misc_create_device(&(cy_as_dev->dev_handle), + cy_as_dev->hal_tag) != CY_AS_ERROR_SUCCESS) { + + cy_as_hal_print_message( + "<1>_cy_as_device: create device failed\n"); + goto done; + } + + memset(&config, 0, sizeof(config)); + config.dmaintr = cy_true; + + ret = cy_as_misc_configure_device(cy_as_dev->dev_handle, &config); + if (ret != CY_AS_ERROR_SUCCESS) { + + cy_as_hal_print_message( + "<1>_cy_as_device: configure device " + "failed. reason code: %d\n", ret); + goto done; + } + + ret = cy_as_misc_register_callback(cy_as_dev->dev_handle, + cy_misc_callback); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_device: " + "cy_as_misc_register_callback failed. " + "reason code: %d\n", ret); + goto done; + } + + ret = platform_driver_register(&west_bridge_driver); + if (unlikely(ret < 0)) + return ret; + westbridge_pd = platform_device_register_simple( + "west_bridge_dev", -1, NULL, 0); + + if (IS_ERR(westbridge_pd)) { + cy_as_hal_print_message("[%s] error in register the platform\ + driver for west bridge\n", __FUNCTION__); + platform_driver_unregister(&west_bridge_driver); + return PTR_ERR(westbridge_pd); + } else { + #ifdef __CYAS_SYSFS_FOR_DIAGNOSTICS__ + ret = device_create_file(&westbridge_pd->dev, &dev_attr_cyas_diagnostics); + if (ret) + cy_as_hal_print_message("[%s] error in device_create_file\ + for device_create_file 1\n", __FUNCTION__); + ret = device_create_file(&westbridge_pd->dev, &dev_attr_serial); + if (ret) + cy_as_hal_print_message("[%s] error in device_create_file\ + for device_create_file 2\n", __FUNCTION__); + ret = device_create_file(&westbridge_pd->dev, &dev_attr_wbcid); + if (ret) + cy_as_hal_print_message("[%s] error in device_create_file\ + for device_create_file 2\n", __FUNCTION__); + #endif + } + cy_as_dev->f_platform_driver = 1; +#if 0 + /* Load the firmware */ + ret = request_firmware(&fw_entry, + "west bridge fw", &westbridge_pd->dev); + if (ret) { + cy_as_hal_print_message("cy_as_device: " + "request_firmware failed return val = %d\n", ret); + } else { + cy_as_hal_print_message("cy_as_device: " + "got the firmware %d size=0x%x\n", ret, fw_entry->size); + + ret = cy_as_misc_download_firmware( + cy_as_dev->dev_handle, + fw_entry->data, + fw_entry->size , + 0, 0); + } + release_firmware(fw_entry); +#endif + +#ifdef WESTBRIDGE_ASTORIA + if (firmware_number == 0) { + cy_as_hal_print_message("<1>_cy_as_device: firmware_number=0, download firmware cyastfw_sd_mmc_rel_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyastfw_sd_mmc_rel_silicon_array.fw_image, + CYASTFW_SD_MMC_REL_SILICON_SIZE , 0, 0) ; + } else { + cy_as_hal_print_message("<1>_cy_as_device: firmware_number=1, download firmware cyastfw_mtp_sd_mmc_rel_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, cyastfw_mtp_sd_mmc_rel_silicon_array.fw_image, CYASTFW_MTP_SD_MMC_REL_SILICON_SIZE , 0, 0) ; + } +#endif + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_device: cannot download firmware. reason code: %d\n", ret); + goto done; + } + + /* spin until the device init is completed */ + /* 50 -MAX wait time for the FW load & init + * to complete is 5sec*/ + spin_lim = 500; + + cy_as_hal_create_sleep_channel(&channel); + while (!cy_as_device_init_done) { + + cy_as_hal_sleep_on(&channel, 10) ; + + if (spin_lim-- <= 0) { + cy_as_hal_print_message( + "<1>\n_e_r_r_o_r!: " + "wait for FW init has timed out !!!"); + break; + } + } + cy_as_hal_destroy_sleep_channel(&channel); + + if (spin_lim > 0) + cy_as_hal_print_message( + "cy_as_device: astoria firmware is loaded\n"); + + ret = cy_as_misc_get_firmware_version(cy_as_dev->dev_handle, + &ver_data, 0, 0); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_device: cannot get firmware " + "version. reason code: %d\n", ret); + goto done; + } + + if ((ver_data.media_type & 0x01) && (ver_data.media_type & 0x06)) + str = "nand and SD/MMC."; + else if ((ver_data.media_type & 0x01) && (ver_data.media_type & 0x08)) + str = "nand and CEATA."; + else if (ver_data.media_type & 0x01) + str = "nand."; + else if (ver_data.media_type & 0x08) + str = "CEATA."; + else + str = "SD/MMC."; + + cy_as_hal_print_message("<1> cy_as_device:_firmware version: %s " + "major=%d minor=%d build=%d,\n_media types supported:%s\n", + ((ver_data.is_debug_mode) ? "debug" : "release"), + ver_data.major, ver_data.minor, ver_data.build, str); + +/* printk("%s: lock address is 0x%x\n", __func__, (unsigned int)&cy_as_dev->common_lock); */ +/* spin_lock_init(&cy_as_dev->common_lock); */ +/* printk("%s: lock address is 0x%x\n", __func__, (unsigned int)&cy_as_dev->common_lock); */ + + printk("%s: lock address is 0x%x\n", __func__, (unsigned int)&cy_as_dev->common_sema); + sema_init(&cy_as_dev->common_sema, 1); + sema_init(&cy_as_dev->wq_sema, 1); + printk("%s: lock address is 0x%x\n", __func__, (unsigned int)&cy_as_dev->common_sema); + /* done now */ + cy_as_device_controller = cy_as_dev; + + + + return 0; + +done: + if (cy_as_dev) + cyasdevice_deinit(cy_as_dev); + + return -EINVAL; +} + +cy_as_device_handle cyasdevice_getdevhandle(void) +{ + if (cy_as_device_controller) { + #ifdef CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL + cy_as_hal_config_c_s_mux(); + #endif + + return cy_as_device_controller->dev_handle; + } + return NULL; +} +EXPORT_SYMBOL(cyasdevice_getdevhandle); + +cy_as_hal_device_tag cyasdevice_gethaltag(void) +{ + if (cy_as_device_controller) + return (cy_as_hal_device_tag) + cy_as_device_controller->hal_tag; + + return NULL; +} +EXPORT_SYMBOL(cyasdevice_gethaltag); + +static int g_first_error_enter; +static int g_first_error_leave; + +int cyasdevice_enter_standby(void) +{ + int retval; + if (f_isStandby == cy_false) { + /* cy_as_acquire_common_lock(); */ + /* cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s mode\n", __func__) ; */ + retval = cy_as_misc_enter_standby(cy_as_device_controller->dev_handle, cy_false, 0, 0); + if (retval != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s: error - %d\n", __func__, retval) ; + if (!g_first_error_enter) + g_first_error_enter = retval; + } + /* msleep(5); */ + + f_isStandby = cy_true; + /* cy_as_release_common_lock(); */ + cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s end mode\n", __func__) ; + + if (g_first_error_enter || g_first_error_leave) + cy_as_hal_print_message(KERN_ERR + "[%s] 1st error %d - %d\n", __func__, + g_first_error_enter, g_first_error_leave) ; + } + return 0; +} +EXPORT_SYMBOL(cyasdevice_enter_standby) ; + +int cyasdevice_leave_standby(void) +{ + int retval = 0; + uint16_t v1, v2 ; + uint32_t count = 20 ; + + if (f_isStandby == cy_true) { + cy_as_device_init_done = 0; + + /* cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s mode \n", __func__) ; */ + retval = cy_as_misc_leave_standby(cy_as_device_controller->dev_handle, cy_as_bus_1); + if (retval != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s: error - %d\n", __func__, retval) ; + if (!g_first_error_leave) + g_first_error_leave = retval; + } + + while (cy_as_device_init_done == 0 && count--) + msleep(1); + + if (cy_as_device_init_done == 0) { + gpio_set_value(WB_WAKEUP, 0); + msleep(1); + gpio_set_value(WB_WAKEUP, 1); + msleep(10); + } + /* cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s count=%d\n", __func__,count) ; */ + + count = 0x08; + while (count) { + v1 = cy_as_hal_read_register(cy_as_device_controller->hal_tag, CY_AS_MEM_RST_CTRL_REG); + if ((v1 & CY_AS_MEM_RST_RSTCMPT) == 0) + cy_as_hal_print_message( + KERN_ERR"Initialization Message Received,but reset complete bit still not set\n"); + else { + v2 = cy_as_hal_read_register(cy_as_device_controller->hal_tag, CY_AS_MEM_P0_VM_SET); + if (v2 & CY_AS_MEM_P0_VM_SET_CFGMODE) + cy_as_hal_print_message( + KERN_ERR"Initialization Message Received,but config bit still not set\n"); + else + break; + } + count--; + msleep(1); + } + cy_as_hal_print_message(KERN_ERR"[cyasdevice.c] %s end mode count=%d\n", __func__, count) ; + + f_isStandby = cy_false; + + if (cy_as_device_init_done == 0) { + if (!g_first_error_leave) + g_first_error_leave = 0x7F; + retval = 0x7F; + } else + retval = 0; + + if (g_first_error_enter || g_first_error_leave) + cy_as_hal_print_message(KERN_ERR + "[%s] 1st error %d - %d\n", __func__, + g_first_error_enter, g_first_error_leave) ; + } + return retval; +} +EXPORT_SYMBOL(cyasdevice_leave_standby) ; + +/**********************************************/ +/* SD Card Detect Routine by Samsung */ +/**********************************************/ +extern int cyasblkdev_blk_init(int fmajor, int fsearch); +extern void cyasblkdev_blk_exit(void); + +void cyasdevice_spoof_removal(void) +{ + cyasblkdev_blk_exit(); + cy_as_device_controller->cy_work->f_status = 0; +} +EXPORT_SYMBOL(cyasdevice_spoof_removal); + +void cyasdevice_spoof_insertion(void) +{ + uint ret = 0; + ret = cyasblkdev_blk_init(0, 0); + if (ret != CY_AS_ERROR_SUCCESS) + printk(KERN_ERR "%s: unable to initialize block device\n", __func__); + cy_as_device_controller->cy_work->f_status = 1; +} +EXPORT_SYMBOL(cyasdevice_spoof_insertion); + + +int cyasdevice_reload_firmware(int mtp_mode) +{ + cyasdevice *cy_as_dev = cy_as_device_controller ; + int ret = 0 ; + cy_as_device_config config ; + cy_as_hal_sleep_channel channel ; + /* cy_as_get_firmware_version_data ver_data = {0}; */ + /* const char *str = "" ; */ + int spin_lim; + + cy_as_hal_print_message("<r>_cy_as_device deinitialize called\n") ; + + /* cyasdevice_leave_standby(); */ + +#if 0/* def CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL */ + /* ret = cy_as_misc_reset(cy_as_dev->dev_handle, cy_as_reset_hard, cy_false, 0, 0); */ + /* if (ret != CY_AS_ERROR_SUCCESS) { */ + /* cy_as_hal_print_message("<1>_cy_as_device: cy_as_misc_reset failed. reason code: %d\n", ret) ; */ + /* } */ + if (cy_as_dev) { + /* stop west_brige */ + if (cy_as_dev->dev_handle) { + cy_as_hal_print_message("<r>_cy_as_device: " + "cy_as_misc_destroy_device called\n") ; + if (cy_as_misc_destroy_device(cy_as_dev->dev_handle) != + CY_AS_ERROR_SUCCESS) + cy_as_hal_print_message( + "<1>_cy_as_device: destroying failed\n"); + } + + if (cy_as_dev->hal_tag) { + if (cy_as_hal_c110_pnand_stop(dev_handle_name, cy_as_dev->hal_tag) != 0) + cy_as_hal_print_message("<r>_cy_as_device: stopping C110 PNAND HAL failed\n"); + } + + cy_as_hal_print_message("<r>_cy_as_device:HAL layer stopped\n") ; + } + + /* Init the HAL & CyAsDeviceHandle */ + /* start OMAP HAL init instsnce */ + if (!cy_as_hal_c110_pnand_start(dev_handle_name, &(cy_as_dev->hal_tag), cy_false)) { + cy_as_hal_print_message("<1>_cy_as_device: start C110 HAL failed\n") ; + goto reload_done; + } + + /* Now create the device */ + if (cy_as_misc_create_device(&(cy_as_dev->dev_handle), + cy_as_dev->hal_tag) != CY_AS_ERROR_SUCCESS) { + + cy_as_hal_print_message( + "<1>_cy_as_device: create device failed\n") ; + goto reload_done ; + } +#else + ret = cy_as_misc_reset(cy_as_dev->dev_handle, + cy_as_reset_hard, cy_true, 0, 0); + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message( + "<1>_cy_as_device: cy_as_misc_reset failed. reason code: %d\n", ret) ; + } + + gpio_set_value(WB_RESET, 0); + mdelay(10); + gpio_set_value(WB_RESET, 1); + mdelay(50); + +#endif + if (mtp_mode == 1) + firmware_number = 1; + else if (mtp_mode == 0) + firmware_number = 0; + + f_isStandby = cy_false; + + cy_as_device_init_done = 0; + + cy_as_misc_set_log_level(8); + + cy_as_hal_print_message("<r>_cy_as_device initialize called\n") ; + + + memset(&config, 0, sizeof(config)); + config.dmaintr = cy_true; + + ret = cy_as_misc_configure_device(cy_as_dev->dev_handle, &config) ; + if (ret != CY_AS_ERROR_SUCCESS) { + + cy_as_hal_print_message( + "<1>_cy_as_device: configure device " + "failed. reason code: %d\n", ret) ; + goto reload_done; + } + + ret = cy_as_misc_register_callback(cy_as_dev->dev_handle, + cy_misc_callback) ; + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_device: " + "cy_as_misc_register_callback failed. " + "reason code: %d\n", ret) ; + goto reload_done; + } + +#if 0 + /* Load the firmware */ + ret = request_firmware(&fw_entry, + "west bridge fw", &westbridge_pd->dev); + if (ret) { + cy_as_hal_print_message("cy_as_device: " + "request_firmware failed return val = %d\n", ret); + } else { + cy_as_hal_print_message("cy_as_device: " + "got the firmware %d size=0x%x\n", ret, fw_entry->size); + + ret = cy_as_misc_download_firmware( + cy_as_dev->dev_handle, + fw_entry->data, + fw_entry->size , + 0, 0) ; + } + release_firmware(fw_entry); +#endif +#ifdef WESTBRIDGE_ANTIOCH_CSP + if (firmware_number == 0) { + cy_as_hal_print_message("<r>reload_fw: firmware_number=0,\ + download firmware cyanfw_sd_mmc_rel_csp_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyanfw_sd_mmc_rel_csp_silicon_array.fw_image, + CYANFW_SD_MMC_REL_CSP_SILICON_SIZE , 0, 0) ; + } else { + cy_as_hal_print_message("<r>reload_fw: firmware_number=0,\ + download firmware cyanfw_mtp_sd_mmc_rel_csp_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyanfw_mtp_sd_mmc_rel_csp_silicon_array.fw_image, + CYANFW_MTP_SD_MMC_REL_CSP_SILICON_SIZE , 0, 0) ; + } +#endif +#ifdef WESTBRIDGE_ANTIOCH + if (firmware_number == 0) { + cy_as_hal_print_message("<r>reload_fw: firmware_number=0,\ + download firmware cyanfw_sd_mmc_rel_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyanfw_sd_mmc_rel_silicon_array.fw_image, + CYANFW_SD_MMC_REL_SILICON_SIZE , 0, 0) ; + } else { + cy_as_hal_print_message("<r>reload_fw: firmware_number=0,\ + download firmware cyanfw_mtp_sd_mmc_rel_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyanfw_mtp_sd_mmc_rel_silicon_array.fw_image, + CYANFW_MTP_SD_MMC_REL_SILICON_SIZE , 0, 0) ; + } +#endif +#ifdef WESTBRIDGE_ASTORIA + if (firmware_number == 0) { + cy_as_hal_print_message( + "<r>reload_fw: firmware_number=0,\ + download firmware cyastfw_sd_mmc_rel_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyastfw_sd_mmc_rel_silicon_array.fw_image, + CYASTFW_SD_MMC_REL_SILICON_SIZE , 0, 0) ; + } else { + cy_as_hal_print_message( + "<r>reload_fw: firmware_number=0,\ + download firmware cyastfw_mtp_sd_mmc_rel_silicon_array image\n") ; + ret = cy_as_misc_download_firmware(cy_as_dev->dev_handle, + cyastfw_mtp_sd_mmc_rel_silicon_array.fw_image, + CYASTFW_MTP_SD_MMC_REL_SILICON_SIZE , 0, 0) ; + } +#endif + + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_device: cannot download "\ + "firmware. reason code: %d\n", ret) ; + goto reload_done; + } + + /* spin until the device init is completed */ + /* 50 -MAX wait time for the FW load & init + * to complete is 5sec*/ + spin_lim = 500; + + cy_as_hal_create_sleep_channel(&channel) ; + while (!cy_as_device_init_done) { + + cy_as_hal_sleep_on(&channel, 10) ; + + if (spin_lim-- <= 0) { + cy_as_hal_print_message( + "%s: ERROR!: wait for FW init " + "has timed out !!!", __func__); + break; + } + } + cy_as_hal_destroy_sleep_channel(&channel) ; + + if (spin_lim > 0) + cy_as_hal_print_message( + "cy_as_device: astoria firmware is loaded\n") ; +#if 0 + ret = cy_as_misc_get_firmware_version(cy_as_dev->dev_handle, + &ver_data, 0, 0) ; + if (ret != CY_AS_ERROR_SUCCESS) { + cy_as_hal_print_message("<1>_cy_as_device: cannot get firmware " + "version. reason code: %d\n", ret) ; + goto reload_done; + } + + if ((ver_data.media_type & 0x01) && (ver_data.media_type & 0x06)) + str = "nand and SD/MMC." ; + else if ((ver_data.media_type & 0x01) && (ver_data.media_type & 0x08)) + str = "nand and CEATA." ; + else if (ver_data.media_type & 0x01) + str = "nand." ; + else if (ver_data.media_type & 0x08) + str = "CEATA." ; + else + str = "SD/MMC." ; + + cy_as_hal_print_message("<1> cy_as_device:_firmware version: %s " + "major=%d minor=%d build=%d,\n_media types supported:%s\n", + ((ver_data.is_debug_mode) ? "debug" : "release"), + ver_data.major, ver_data.minor, ver_data.build, str) ; +#endif + return 0 ; + +reload_done: + return -EINVAL ; +} +EXPORT_SYMBOL(cyasdevice_reload_firmware) ; + +#ifdef __USE_CYAS_AUTO_SUSPEND__ +int cyasdevice_wakeup_thread(int flag) +{ + int retval; + + /* down(&cy_as_device_controller->thread_sleep_sem); */ + down(&cy_as_device_controller->wq_sema); + cy_as_device_controller->thread_sleep_count++; + cy_as_device_controller->thread_sleep_enable = 0; + /* up(&cy_as_device_controller->thread_sleep_sem); */ + retval = cyasdevice_leave_standby(); + /* if (!flag) */ + /* wake_up(&cy_as_device_controller->thread_sleep_wq); */ + + up(&cy_as_device_controller->wq_sema); + + if (retval) { + cyasdevice_reload_firmware(-1); + + if (flag) { + retval = cyasblkdev_start_sdcard(); + if (retval) + cy_as_hal_print_message(KERN_ERR + "%s: cyasblkdev_start_sdcard error %d\n", + __func__, retval); + } + } + + return retval; +} +EXPORT_SYMBOL(cyasdevice_wakeup_thread) ; + +int cyasdevice_enable_thread(void) +{ + /* down(&cy_as_device_controller->thread_sleep_sem); */ + down(&cy_as_device_controller->wq_sema); + cy_as_device_controller->thread_sleep_enable = CYASDEVICE_THREAD_ENABLE; + cy_as_device_controller->thread_sleep_count++; + /* up(&cy_as_device_controller->thread_sleep_sem); */ + wake_up(&cy_as_device_controller->thread_sleep_wq); + up(&cy_as_device_controller->wq_sema); + return 0; +} +EXPORT_SYMBOL(cyasdevice_enable_thread) ; + +int cyasdevice_disable_thread(void) +{ + /* down(&cy_as_device_controller->thread_sleep_sem); */ + down(&cy_as_device_controller->wq_sema); + cy_as_device_controller->thread_sleep_count++; + cy_as_device_controller->thread_sleep_enable = 0; + /* up(&cy_as_device_controller->thread_sleep_sem); */ + /* wake_up(&cy_as_device_controller->thread_sleep_wq); */ + up(&cy_as_device_controller->wq_sema); + + return 0; +} +EXPORT_SYMBOL(cyasdevice_disable_thread) ; + +/* queue worker thread */ +static int cyasdevice_sleep_thread(void *d) +{ + /* cyasdevice *cy_as_dev = (cyasdevice *)cy_as_device_controller; */ + u32 qth_pid; + DECLARE_WAITQUEUE(wait, current); + /* DBGPRN_FUNC_NAME; */ + long ret = 0; + int prev_counter = 0; + int check_retry = 0; + /* + * set iothread to ensure that we aren't put to sleep by + * the process freezing. we handle suspension ourselves. + */ + daemonize("cyasdevice_sleep_thread"); + + /* signal to queue_init() so it could contnue */ + complete(&cy_as_device_controller->thread_sleep_complete); + + down(&cy_as_device_controller->wq_sema); + add_wait_queue(&cy_as_device_controller->thread_sleep_wq, &wait); + + cy_as_device_controller->thread_sleep_flags = CYASDEVICE_THREAD_SUSPENDED; + cy_as_device_controller->thread_sleep_count = 0; + + qth_pid = current->pid; + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR + "%s:%x started, cyasdevice:%p\n", __func__, + qth_pid, cy_as_device_controller) ; + #endif + + do { + /* the thread wants to be woken up by signals as well */ + if (cy_as_device_controller->thread_sleep_flags & CYASDEVICE_THREAD_EXIT) { + #ifdef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR"%s:got SLEEP_T_EXIT flag\n", __func__); + #endif + break; + } + + #if 0 + if (signal_pending(current)) { + ret = -EINTR; + break; + } + #endif + + /* set_current_state(TASK_INTERRUPTIBLE); */ + /* yields to the next rdytorun proc, + * then goes back to sleep*/ + set_current_state(TASK_INTERRUPTIBLE); + up(&cy_as_device_controller->wq_sema); + if (cy_as_device_controller->thread_sleep_flags & CYASDEVICE_THREAD_TIMER) + ret = schedule_timeout(msecs_to_jiffies(CYASDEVICE_STANDBY_TIMEOUT)); + else + schedule(); + + set_current_state(TASK_RUNNING); + /* down(&cy_as_dev->thread_sleep_sem); */ + down(&cy_as_device_controller->wq_sema); + if (cy_as_device_controller->thread_sleep_enable) { + if (cy_as_device_controller->thread_sleep_flags & CYASDEVICE_THREAD_TIMER) { + if (!ret) { /*timeout */ + + if (prev_counter == cy_as_device_controller->thread_sleep_count) + check_retry++; + else + check_retry = 0; + + prev_counter = cy_as_device_controller->thread_sleep_count; + if (check_retry > 3) { + cy_as_device_controller->thread_sleep_flags = CYASDEVICE_THREAD_SUSPENDED; + #ifdef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR + "%s: chage mode to CYASDEVICE_THREAD_SUSPENDED\n", + __func__); + #endif + } + } else { /* wake_up */ + + if (cy_as_device_controller->cy_work->f_status == 0) { + #ifdef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR + "%s: chage mode to CYASDEVICE_THREAD_SUSPENDED\n", + __func__); + #endif + cy_as_device_controller->thread_sleep_flags = + CYASDEVICE_THREAD_SUSPENDED; + } + check_retry = 0; + } + } else { + if (cy_as_device_controller->cy_work->f_status) { + #ifdef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR + "%s: chage mode to CYASDEVICE_THREAD_TIMER\n", + __func__); + #endif + cy_as_device_controller->thread_sleep_flags = CYASDEVICE_THREAD_TIMER; + } + check_retry = 0; + } + } else { + check_retry = 0; + #ifdef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR + "%s: CYASDEVICE_THREAD diabled\n", __func__); + #endif + cy_as_device_controller->thread_sleep_flags = CYASDEVICE_THREAD_SUSPENDED; + } + + if (check_retry > 3) { + check_retry = 0; + /* to do for enter standby mode. */ + /* down(&cy_as_dev->wq_sema); */ + cyasdevice_enter_standby(); + msleep(20); + /* up(&cy_as_dev->wq_sema); */ + } + } while (1); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&cy_as_device_controller->thread_sleep_wq, &wait); + complete_and_exit(&cy_as_device_controller->thread_sleep_complete, 0); + + #ifndef WESTBRIDGE_NDEBUG + cy_as_hal_print_message(KERN_ERR"%s: is finished\n", __func__) ; + #endif + + return 0; +} +#endif +/*init Westbridge device driver **/ +static int __init cyasdevice_init(void) +{ + int retry_count = 3; + int ret_val = 0; + f_isStandby = cy_false; +/* Retry if Westbridge device is failed to initialize */ + do { + ret_val = cyasdevice_initialize(); + if (ret_val == 0) + break; + msleep(100); + } while (--retry_count); + + if (ret_val != 0) + return -ENODEV; + +#ifdef __USE_CYAS_AUTO_SUSPEND__ + { + int ret; + + init_completion( + &cy_as_device_controller->thread_sleep_complete); + init_waitqueue_head(&cy_as_device_controller->thread_sleep_wq); + /* init_MUTEX(&cy_as_device_controller->thread_sleep_sem); */ + sema_init(&cy_as_device_controller->thread_sleep_sem, 1); + + ret = kernel_thread(cyasdevice_sleep_thread, + cy_as_device_controller, CLONE_KERNEL); + if (ret >= 0) { + /* wait until the thread is spawned */ + wait_for_completion( + &cy_as_device_controller->thread_sleep_complete); + + /* reinitialize the completion */ + init_completion( + &cy_as_device_controller->thread_sleep_complete); + ret = 0; + } + } +#endif + + cy_as_device_controller->cy_wq = create_workqueue("wb_queue"); + if (cy_as_device_controller->cy_wq) { + cy_as_device_controller->cy_work = (cy_work_t *)kmalloc(sizeof(cy_work_t), + GFP_KERNEL); + if (cy_as_device_controller->cy_work) { + INIT_DELAYED_WORK( + &cy_as_device_controller->cy_work->work, + cy_wq_function); + cy_as_device_controller->cy_work->f_status = 0x0; + cy_as_device_controller->cy_work->f_reload = 1; + cy_as_device_controller->cy_work->f_isrunning = 0; + +#ifdef __USE_ISR_FOR_SD_DETECT__ + cy_as_hal_configure_sd_isr( + (void *)cy_as_device_controller->hal_tag, + (irq_handler_t) cyasdevice_int_handler); +#endif + if (cy_as_hal_detect_SD()) { + cy_as_hal_print_message( + "<1>%s: SD card scan:\n", __func__) ; + /* queue_delayed_work(cy_as_device_controller->cy_wq, + &cy_as_device_controller->cy_work->work, 10 * HZ); */ + } else { + queue_delayed_work( + cy_as_device_controller->cy_wq, + &cy_as_device_controller->cy_work->work, HZ); + } + + } + } + + return 0; +} + + +static void __exit cyasdevice_cleanup(void) +{ +#ifdef __USE_CYAS_AUTO_SUSPEND__ + cy_as_device_controller->thread_sleep_flags = CYASDEVICE_THREAD_EXIT; + wake_up(&cy_as_device_controller->thread_sleep_wq); + wait_for_completion(&cy_as_device_controller->thread_sleep_complete); +#endif + kfree(cy_as_device_controller->cy_work); + destroy_workqueue(cy_as_device_controller->cy_wq); + +#ifdef __USE_ISR_FOR_SD_DETECT__ + cy_as_hal_free_sd_isr(); +#endif + cyasdevice_deinit(cy_as_device_controller); +} + + +MODULE_DESCRIPTION("west bridge device driver"); +MODULE_AUTHOR("cypress semiconductor"); +MODULE_LICENSE("GPL"); + +module_init(cyasdevice_init); +module_exit(cyasdevice_cleanup); + +/*[]*/ |