| /* |
| ## 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/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> |
| |
| /* 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" |
| |
| 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: " |
| "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 |
| |
| /* 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); |
| |
| /*[]*/ |