/* ## 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 #include #include #include #include #include #include #include /* moved for staging location * update/patch submission #include #include #include */ #include "../include/linux/westbridge/cyastoria.h" #include "../include/linux/westbridge/cyashal.h" #include "../include/linux/westbridge/cyasregs.h" /* API exports include file */ #include "cyandevice_export.h" 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; unsigned long flags; } cyasdevice; /* 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"; #ifdef CONFIG_MACH_OMAP3_WESTBRIDGE_AST_PNAND_HAL extern void cy_as_hal_config_c_s_mux(void); #endif 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 } cy_as_hal_print_message("<1>_cy_as_device:HAL layer stopped\n"); 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() { spin_lock_irqsave(&cy_as_device_controller->common_lock, cy_as_device_controller->flags); } EXPORT_SYMBOL(cy_as_acquire_common_lock); void cy_as_release_common_lock() { spin_unlock_irqrestore(&cy_as_device_controller->common_lock, cy_as_device_controller->flags); } 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); /* below structures and functions primarily * implemented for firmware loading */ 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 struct platform_driver west_bridge_driver = { .probe = wb_probe, .remove = __devexit_p(wb_remove), .driver = { .name = "west_bridge_dev"}, }; /* 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: " "memmory 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 /* 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)) { platform_driver_unregister(&west_bridge_driver); return PTR_ERR(westbridge_pd); } /* 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); } 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 = 50; cy_as_hal_create_sleep_channel(&channel); while (!cy_as_device_init_done) { cy_as_hal_sleep_on(&channel, 100); 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); spin_lock_init(&cy_as_dev->common_lock); /* 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); /*init Westbridge device driver **/ static int __init cyasdevice_init(void) { if (cyasdevice_initialize() != 0) return ENODEV; return 0; } static void __exit cyasdevice_cleanup(void) { 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); /*[]*/