/* * Device driver for SpursEngine * * (C) Copyright 2008 TOSHIBA CORPORATION * * 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. */ #undef DEBUG #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spurs_define.h" #include "spurs_ioctl.h" #include "spurs_cmd.h" #include "spurs_session.h" #include "spurs_boot.h" #include "spurs_event.h" #include "spurs_timer.h" #include "spurs_driver.h" /* Module parameters */ static int major_dev_number = SPD_DEVICE_MAJOR; static int reset_phase = 11; static uint32_t tag_id_num = SPD_TID_MAX_NUM; #ifdef DEBUG #define DEFAULT_DEBUG_SUPPORT SPD_DBG_SUPPORT #else #define DEFAULT_DEBUG_SUPPORT SPD_DBG_NOT_SUPPORT #endif static struct spd_system_config default_system_config = { .clk_mode = SPD_CLK_MODE_8, .clk_mode_limit = SPD_CLK_MODE_1, .d3_disable = 0, .d3_delay = 120000, .d3_delay_limit = 10000, .cg_disable = 0, .cg_delay = 120, .cg_delay_limit = 10, .wdt_disable = 0, .max_count = 16, .total_count = 64, .dbg_support = DEFAULT_DEBUG_SUPPORT, .cache_type = 0, .boot_timeout = 30000, .sp3_info = 0, .pci_inorder = 0, .wdt_watch_cycle = 1000, .wdt_api_switch = 0, .gpio_d3hot = 1, .xdr_test = 0, .spd_trace_port = 0, }; module_param(major_dev_number, int, 0); module_param(reset_phase, int, 0); module_param(tag_id_num, uint, 0); #define module_param_SYSTEM_CONFIG(name) \ module_param_named(name, default_system_config.name, uint, 0) module_param_SYSTEM_CONFIG(clk_mode); module_param_SYSTEM_CONFIG(clk_mode_limit); module_param_SYSTEM_CONFIG(d3_disable); module_param_SYSTEM_CONFIG(d3_delay); module_param_SYSTEM_CONFIG(d3_delay_limit); module_param_SYSTEM_CONFIG(cg_disable); module_param_SYSTEM_CONFIG(cg_delay); module_param_SYSTEM_CONFIG(cg_delay_limit); module_param_SYSTEM_CONFIG(wdt_disable); module_param_SYSTEM_CONFIG(max_count); module_param_SYSTEM_CONFIG(total_count); module_param_SYSTEM_CONFIG(dbg_support); module_param_SYSTEM_CONFIG(cache_type); module_param_SYSTEM_CONFIG(boot_timeout); module_param_SYSTEM_CONFIG(sp3_info); module_param_SYSTEM_CONFIG(pci_inorder); module_param_SYSTEM_CONFIG(wdt_watch_cycle); module_param_SYSTEM_CONFIG(wdt_api_switch); module_param_SYSTEM_CONFIG(gpio_d3hot); module_param_SYSTEM_CONFIG(xdr_test); module_param_SYSTEM_CONFIG(spd_trace_port); /* Module description and license */ MODULE_DESCRIPTION("Linux SPURS driver"); MODULE_LICENSE("GPL"); static struct spd_dev *spd_device[SPD_DEVICE_MAX]; static struct cdev spd_cdev; #define SPD_PROC_DEVINFO "driver/spursinfo" static DECLARE_MUTEX(proc_spurs_sem); static struct proc_dir_entry *spurs_proc_entry; static struct class *spurs_class = NULL; #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) static irqreturn_t spurs_isr(int, void *); #else static irqreturn_t spurs_isr(int, void *, struct pt_regs *); #endif static int spurs_read_proc(char *page, char **start, off_t offset, int count, int *eof, void *data) { int i, len = 0, num; if (down_interruptible(&proc_spurs_sem)) { __spd_dbg("down_interruptible error\n"); return -ERESTARTSYS; } len += sprintf(page + len, "NAME %s\n", SPD_DRIVER_NAME); num = 0; for (i = 0; i < SPD_DEVICE_MAX; i++) if (spd_device[i] != NULL) num++; len += sprintf(page + len, "COUNT %d\n", num); len += sprintf(page + len, "MAJIOR %d\n", major_dev_number); up(&proc_spurs_sem); *eof = 1; return len; } void spurs_io_write(const struct spd_dev *dev, unsigned int off, uint32_t data) { writel(swab32(data), dev->io_base + off); } void spurs_io_read(const struct spd_dev *dev, unsigned int off, uint32_t *val) { *val = swab32(readl(dev->io_base + off)); } void *spurs_get_dma_mem_area(enum spd_area_type area, struct spd_dev *dev) { unsigned long offset; int index; if ((area >= SPD_EVENT) && (area <= SPD_TRACE)) { switch (area) { case SPD_EVENT: offset = dev->boot_info.evt_offset; break; case SPD_COMMAND: offset = dev->boot_info.cmd_offset; break; case SPD_RESPONSE: offset = dev->boot_info.res_offset; break; case SPD_TRACE: offset = dev->boot_info.trc_offset; if (offset == 0) return NULL; break; default: spd_dbg(dev, "Unknown buffer.\n"); return NULL; } return dev->dma_packet.base_addr + offset; } else if ((area >= SPD_SLB0) && (area <= SPD_SLB7)) { index = area - SPD_SLB0; if (dev->slb[index].base_addr == NULL) spd_dbg(dev, "SLB[%d] is not used.\n", index); return dev->slb[index].base_addr; } else spd_dbg(dev, "Unknown buffer.\n"); return NULL; } void spurs_get_rxmbox(unsigned int no, uint32_t *val, struct spd_dev *dev) { BUG_ON(no > SPD_MBOX7); spurs_io_read(dev, OFS_SCP_HOMND(no), val); } void spurs_set_rxmbox(unsigned int no, uint32_t val, struct spd_dev *dev) { BUG_ON(no > SPD_MBOX7); spurs_io_write(dev, OFS_SCP_HIMND(no), val); } int spurs_soft_reset(uint32_t cmd, struct spd_dev *dev) { uint32_t scp_state; if (IS_FW_HALTED(dev) || IS_FW_BOOTING(dev)) { spd_err(dev, "FW state is not running.\n"); return 0; } if (IS_FW_PANIC(dev)) { spd_err(dev, "FW state is panic\n"); return 0; } switch (cmd) { case SOFT_RESET_CMD: case D3HOT_REQUIRE_CMD: spd_dbg(dev, "Software reset start.\n"); spurs_io_read(dev, OFS_SY_SCPRST, &scp_state); if (scp_state == 0xFFFFFFFF) { spd_err(dev, "Invalid SCP state(0x%08x)\n", scp_state); if (cmd == SOFT_RESET_CMD) spurs_resume_device_idle(dev); return -ETIME; } if (dev->reset_state != RESET_STATE_COMPLETED) { spd_dbg(dev, "software reset already required.\n"); return 0; } /* Stop WDT reset check timer */ spurs_stop_wdt_check_timer(&dev->timer_info); /* Stop panic check timer */ spurs_stop_panic_check_timer(&dev->timer_info); /* Start software reset */ dev->reset_state = RESET_STATE_REQUIRE; /* Request software reset */ spurs_set_rxmbox(SPD_MBOX7, cmd, dev); /* Wait to complete reset */ wait_event_interruptible_timeout(dev->soft_reset_que, (dev->reset_state == RESET_STATE_COMPLETED), msecs_to_jiffies(SPD_SWRST_TIMEOUT)); if (dev->reset_state != RESET_STATE_COMPLETED) spd_err(dev, "Software reset failed.\n"); else spd_dbg(dev, "Software reset complete.\n"); break; default: spd_dbg(dev, "Invalid soft reset command(0x%x)\n", cmd); return -EINVAL; } return 0; } void spurs_enter_panic(uint32_t value, struct spd_dev *dev) { unsigned long irqflag; switch (value) { case SOFT_RESET_ACK: spd_dbg(dev, "soft reset require returned ack\n"); dev->reset_state = RESET_STATE_EXECUTE; return; case SOFT_RESET_CMD_COMPLETE: spd_dbg(dev, "soft reset require returned complete\n"); dev->reset_state = RESET_STATE_COMPLETED; wake_up_interruptible(&dev->soft_reset_que); return; case D3HOT_REQUIRE_ACK: spd_dbg(dev, "D3hot require returned ack\n"); dev->reset_state = RESET_STATE_COMPLETED; wake_up_interruptible(&dev->soft_reset_que); return; default: spd_dbg(dev, "FW panic occured(value=0x%08x).\n", value); spurs_stop_panic_check_timer(&dev->timer_info); if (IS_FW_BOOTING(dev) || IS_FW_INITTING(dev)) spurs_stop_boot_timer(&dev->timer_info); if (!IS_FW_HALTED(dev) && !IS_FW_PANIC(dev)) spurs_stop_wdt_check_timer(&dev->timer_info); if (IS_FW_BOOTING(dev)) { dev->boot_info.status = -ECANCELED; wake_up_interruptible(&dev->boot_info.boot_que); } if (value == SPD_SOFT_RESET) { spurs_soft_reset(SOFT_RESET_CMD, dev); spurs_stop_scp(dev); /* Invalidate firmware cache */ dev->boot_info.cache_state = SPD_FW_CACHE_NONE; dev->boot_info.fw_size = 0; } else spurs_stop_scp(dev); spin_lock_irqsave(&dev->spinlock, irqflag); spurs_purge_cmd_queue(SPD_USER_CMD_QUEUE_FLAG, ECANCELED, 1, dev); if (IS_FW_INITTING(dev) || IS_FW_RUNNING(dev)) spurs_purge_cmd_queue(SPD_SYSTEM_CMD_QUEUE_FLAG, ECANCELED, 1, dev); dev->dev_state.fw_state = SPD_FW_STATE_PANIC; spurs_set_command_reject(dev); spurs_abort_all_wait_event_request(dev); spurs_notify_all_change_device_state(dev, 0); spin_unlock_irqrestore(&dev->spinlock, irqflag); spurs_resume_device_idle(dev); } } int spurs_stop_scp(struct spd_dev *dev) { uint32_t regval; int status = 0; spd_dbg(dev, "Stop SCP\n"); spurs_io_read(dev, OFS_SY_SCPRST, ®val); if ((regval & OFS_SY_SCPRST_RESET) != 0) { spd_err(dev, "SCP already stops.\n"); return 0; } spd_spin_lock_irq(&dev->spinlock); spurs_io_write(dev, OFS_SY_SCPRST, SPD_SCP_STOP_VALUE); spd_spin_unlock_irq(&dev->spinlock); mdelay(1000); spurs_io_read(dev, OFS_SY_SCPRST, ®val); if ((regval & OFS_SY_SCPRST_RESET) == 1) { spd_dbg(dev, "SCP stopped\n"); } else { spd_err(dev, "Stopping SCP is timeout.\n"); status = -ETIME; } return status; } static void spurs_set_scp_svr(struct spd_dev *dev) { /* Set program counter of SCP */ spurs_io_write(dev, OFS_SY_SVR0, 0xd8084000); spurs_io_write(dev, OFS_SY_SVR1, 0xd8284000); spurs_io_write(dev, OFS_SY_SVR2, 0xd8c84000); } int spurs_start_scp(struct spd_dev *dev) { uint32_t scp_state; spd_dbg(dev, "Start SCP\n"); spurs_io_read(dev, OFS_SY_SCPRST, &scp_state); if (scp_state == 0xFFFFFFFF) { spd_err(dev, "SCP reset register is invalid.\n"); return -EIO; } if (IS_SY_SCPRST_RESET(scp_state)) { /* Reset SVR */ spurs_set_scp_svr(dev); /* SCP Reset */ spurs_io_write(dev, SPD_RESET_PHASE, reset_phase); spurs_io_write(dev, OFS_SY_SCPRST, SPD_SCP_START); return 0; } return -EBUSY; } void spurs_stop_device_idle(struct spd_dev *dev) { mutex_lock(&dev->pm_mutex); switch (dev->power_state) { case SPD_POWER_D0: break; case SPD_POWER_MOVE_D3: spurs_stop_move_d3_timer(&dev->timer_info); dev->power_state = SPD_POWER_D0; break; case SPD_POWER_D3: mutex_unlock(&dev->pm_mutex); spurs_enter_d0(dev); mutex_lock(&dev->pm_mutex); break; default: spd_dbg(dev, "Power state is unknown.\n"); break; } mutex_unlock(&dev->pm_mutex); } void spurs_resume_device_idle(struct spd_dev *dev) { if ((dev->power_state == SPD_POWER_MOVE_D3) || (dev->power_state == SPD_POWER_D3)) return; dev->power_state = SPD_POWER_MOVE_D3; if (!timer_pending(&dev->timer_info.move_d3_timer.timer)) spurs_start_move_d3_timer(&dev->timer_info); } void spurs_reset_d3(struct spd_dev *dev) { mutex_lock(&dev->pm_mutex); if (dev->power_state != SPD_POWER_D3) { mutex_unlock(&dev->pm_mutex); spurs_enter_d3(dev); } else { mutex_unlock(&dev->pm_mutex); } } static void __spurs_reset_d3_task(struct spd_dev *dev) { spurs_reset_d3(dev); } DEFINE_WORK_TASK(spurs_reset_d3_task, __spurs_reset_d3_task, reset_d3_task); int spurs_notify_d3_enter(struct spd_dev *dev, uint16_t *status) { spd_ioctl_request_syscmd *cmd = NULL, *res = NULL; int ret; spd_dbg(dev, "SP3_SC_ENTER_D3 request.\n"); ret = -ENOMEM; cmd = kzalloc(sizeof(spd_ioctl_request_syscmd), GFP_KERNEL); res = kzalloc(sizeof(spd_ioctl_request_syscmd), GFP_KERNEL); if ((cmd == NULL) || (res == NULL)) { spd_err(dev, "Failed to alloc memory(cmd=%p, res=%p\n", cmd, res); goto end; } cmd->op = SP3_SC_ENTER_D3; ret = spurs_request_system_command(cmd, res, dev); if (ret == 0) *status = res->status; spd_dbg(dev, "End SP3_SC_ENTER_D3 (status = 0x%04x\n", *status); end: if (cmd != NULL) kfree(cmd); if (res != NULL) kfree(res); return ret; } static int spurs_open(struct inode *inode, struct file *file) { struct spd_dev *dev; struct spd_session *session; int minor, ret; minor = MINOR(inode->i_rdev); if ((minor < 0) || (minor >= SPD_DEVICE_MAX)) return -ENODEV; if (spd_device[minor] == NULL) return -ENODEV; __spd_dbg("Open device(minor=%d)\n", minor); dev = spd_device[minor]; ret = spurs_create_session(dev, &session); if (ret) { spd_err(dev, "Failed to create new session.\n"); return ret; } file->private_data = session; spd_dbg(dev, "Create new session[ID=%d]\n", GET_SID(session)); return 0; } int spurs_enter_d3(struct spd_dev *dev) { uint16_t status; int time, ret; if (dev == NULL) return -ENODEV; spd_dbg(dev, "enter D3 state.\n"); mutex_lock(&dev->pm_mutex); if (dev->power_state == SPD_POWER_D3) { mutex_unlock(&dev->pm_mutex); return 0; } dev->enter_d3 = SPD_ENTER_D3; spurs_stop_wdt_check_timer(&dev->timer_info); spurs_stop_panic_check_timer(&dev->timer_info); time = 0; while (IS_FW_BOOTING(dev) && (time < 1000)) { spd_err(dev, "wait boot up\n"); msleep(100); time += 100; } if (IS_FW_BOOTING(dev) || IS_FW_INITTING(dev)) { spurs_stop_boot_timer(&dev->timer_info); if (IS_FW_BOOTING(dev)) { spurs_wake_up_boot_process(-ECANCELED, dev); spurs_stop_scp(dev); } } if (!IS_FW_RUNNING(dev) && !IS_FW_INITTING(dev)) goto enter_d3; spd_spin_lock_irq(&dev->spinlock); /* set reject flag */ spurs_set_command_reject(dev); /* purge all user command queue */ spurs_purge_cmd_queue(SPD_USER_CMD_QUEUE_FLAG, ECANCELED, 1, dev); spd_spin_unlock_irq(&dev->spinlock); /* notify FW to enter d3 state */ ret = spurs_notify_d3_enter(dev, &status); if ((ret != 0) || (status != 0)) spd_err(dev, "Failed to notify to enter D3 to FW.\n"); spd_spin_lock_irq(&dev->spinlock); /* purge system command queue */ spurs_purge_cmd_queue(SPD_SYSTEM_CMD_QUEUE_FLAG, ECANCELED, 1, dev); spd_spin_unlock_irq(&dev->spinlock); /* FW reset */ spurs_soft_reset(D3HOT_REQUIRE_CMD, dev); spurs_stop_scp(dev); spd_spin_lock_irq(&dev->spinlock); /* complete wait session event request */ spurs_abort_all_wait_event_request(dev); /* reset command flag */ dev->cmd_info.request_flag = 0; atomic_set(&dev->cmd_info.trans_count, 0); /* clean up all ioctl request */ spurs_cleanup_all_ioctl_request(dev); spd_spin_unlock_irq(&dev->spinlock); enter_d3: dev->dev_state.fw_state = SPD_FW_STATE_HALTED; /* complete get device state request */ spd_spin_lock_irq(&dev->spinlock); spurs_notify_all_change_device_state(dev, 0); spd_spin_unlock_irq(&dev->spinlock); /* Disable irq */ free_irq(dev->pci_dev->irq, dev); dev->irq = 0; /* suspending device */ pci_save_state(dev->pci_dev); pci_disable_device(dev->pci_dev); pci_set_power_state(dev->pci_dev, PCI_D3hot); dev->power_state = SPD_POWER_D3; dev->enter_d3 = SPD_NO_ENTER_D3; mutex_unlock(&dev->pm_mutex); return 0; } int spurs_enter_d0(struct spd_dev *dev) { int ret; if (dev == NULL) return -ENODEV; spd_dbg(dev, "enter D0 state.\n"); mutex_lock(&dev->pm_mutex); if (dev->power_state == SPD_POWER_D0) { mutex_unlock(&dev->pm_mutex); return 0; } /* pci_enable_device calls pci_set_power_state(PCI_D0) */ ret = pci_enable_device(dev->pci_dev); if (ret) { spd_err(dev, "pci_enable_device() failed.\n"); mutex_unlock(&dev->pm_mutex); return ret; } mdelay(30); dev->power_state = SPD_POWER_D0; pci_restore_state(dev->pci_dev); ret = request_irq(dev->pci_dev->irq, spurs_isr, IRQF_SHARED, SPD_DRIVER_NAME, (void *)dev); if (ret) { spd_err(dev, "request_irq() failed.\n"); pci_save_state(dev->pci_dev); pci_disable_device(dev->pci_dev); pci_set_power_state(dev->pci_dev, PCI_D3hot); dev->power_state = SPD_POWER_D3; } dev->irq = dev->pci_dev->irq; mutex_unlock(&dev->pm_mutex); return ret; } static int spurs_suspend(struct pci_dev *pdev, pm_message_t state) { struct spd_dev *dev = pci_get_drvdata(pdev); return spurs_enter_d3(dev); } static int spurs_resume(struct pci_dev *pdev) { struct spd_dev *dev = pci_get_drvdata(pdev); return spurs_enter_d0(dev); } #undef SCP_STOP static int spurs_close(struct inode *ino, struct file *file) { struct spd_session *session = file->private_data; struct spd_dev *dev = session->dev; #ifdef SCP_STOP int stop_flag; #endif /* SCP_STOP */ spd_dbg(dev, "Close device\n"); spurs_delete_session(session, dev); mutex_lock(&dev->mutex); file->private_data = NULL; mutex_unlock(&dev->mutex); #ifdef SCP_STOP spd_spin_lock_irq(&dev->spinlock); stop_flag = list_empty(&dev->session_info.session); spd_spin_unlock_irq(&dev->spinlock); if (stop_flag) { /* If all file closed, stop SCP. */ spurs_soft_reset(SOFT_RESET_CMD, dev); (void)spurs_stop_scp(dev); spurs_stop_panic_check_timer(&dev->timer_info); spurs_stop_wdt_check_timer(&dev->timer_info); dev->dev_state.fw_state = SPD_FW_STATE_HALTED; } #endif /* SCP_STOP */ return 0; } static void spurs_print_trace_message(uint32_t data, struct spd_dev *dev) { uint32_t offset, num, size, i; void *buf; uint16_t header, rec_size; char *msg; int j; if (!IS_FW_RUNNING(dev) && !IS_FW_INITTING(dev)) { spd_dbg(dev, "Invalid firmware state.\n"); goto trace_ack; } num = data >> 16; size = data & 0x0000FFFF; buf = spurs_get_dma_mem_area(SPD_TRACE, dev); if (buf == NULL) { spd_err(dev, "No memory for trace message.\n"); goto trace_ack; } /* Complete receive check */ offset = 0; for (i = 0; i < num; i++) { header = *((uint16_t *)(buf + offset)); if (header == 0) { spd_dbg(dev, "Receiving trace data incompleted.\n"); spurs_set_rxmbox(SPD_MBOX6, data | 0x80000000, dev); goto trace_ack; } header = be16_to_cpu(header); offset += (header & 0x0FFF) + 2; } offset = 0; spd_print(dev, "******** SPURS%d TRACE ********\n", dev->id); for (i = 0; i < num; i++) { header = *((uint16_t *)(buf + offset)); header = be16_to_cpu(header); rec_size = header & 0x0FFF; msg = (char *)(buf + offset + 2); for (j = 0; j < rec_size; j++) spd_print(dev, "%c", msg[j]); __spd_print("\n"); offset += rec_size + 2; } spd_print(dev, "******************************\n"); trace_ack: spurs_set_rxmbox(SPD_MBOX6, data, dev); } #ifdef DEBUG #define PRINT_DATA_SIZE 1024 static unsigned char print_data[PRINT_DATA_SIZE]; static uint32_t data_index; static void spurs_scp_debug_print(uint32_t data, struct spd_dev *dev) { unsigned char ch; ch = (unsigned char)(data & 0x000000FF); print_data[data_index] = ch; data_index++; if ((ch == 0x0a) || (data_index >= (PRINT_DATA_SIZE - 1))) { print_data[data_index] = '\0'; spd_dbg(dev, "SCP : %s", print_data); memset(print_data, 0, PRINT_DATA_SIZE); data_index = 0; } } #endif /* DEBUG */ void spurs_interrupt_mask(uint32_t mask, struct spd_dev *dev) { if (!IS_FW_RUNNING(dev) && !IS_FW_INITTING(dev)) return; if (mask & (1 << SPD_MBOX7)) return; mask |= SPD_MBOX_MASK_REQUEST | (1 << SPD_MBOX4) | (1 << SPD_MBOX5); spurs_set_rxmbox(SPD_MBOX7, mask, dev); } static spinlock_t isr_lock = SPIN_LOCK_UNLOCKED; static void spurs_isr_tasklet(unsigned long data) { struct spd_dev *dev = (struct spd_dev *)data; uint32_t val[SPD_MBOX_NUM], mbox_flag; int mbox; unsigned long irq_flag; if (IS_FW_HALTED(dev)) return; spin_lock_irqsave(&isr_lock, irq_flag); for (mbox = 0; mbox < SPD_MBOX_NUM; mbox++) if ((dev->mbox_flag & (1 << mbox)) != 0) val[mbox] = dev->mbox_val[mbox]; else val[mbox] = 0; mbox_flag = dev->mbox_flag; dev->mbox_flag = 0; if ((mbox_flag & (1 << SPD_MBOX7)) == 0) spurs_interrupt_mask(0x00000000, dev); spin_unlock_irqrestore(&isr_lock, irq_flag); if (val[SPD_MBOX0] != 0) { /* Outbound Mailbox0 : Receive ack for command */ if (IS_FW_RUNNING(dev)) spurs_receive_command_ack(val[SPD_MBOX0], dev); else if (IS_FW_BOOTING(dev)) { spd_dbg(dev, "Received boot status in MBOX0=0x%08x\n", val[SPD_MBOX0]); dev->boot_info.boot_status[0] = val[SPD_MBOX0]; spurs_boot_process(val[SPD_MBOX0], dev); } else { spd_err(dev, "Received mailbox0=0x%08x, but " "FW state is not running.\n", val[SPD_MBOX0]); spurs_set_rxmbox(SPD_MBOX0, val[SPD_MBOX0], dev); } } if (val[SPD_MBOX1] != 0) { /* Outbound Mailbox1 : Receive response */ if (IS_FW_RUNNING(dev)) spurs_receive_response(val[SPD_MBOX1], dev); else { spd_err(dev, "Received mailbox1=0x%08x, but " "FW state is not running.\n", val[SPD_MBOX1]); spurs_set_rxmbox(SPD_MBOX1, val[SPD_MBOX1], dev); } } if (val[SPD_MBOX2] != 0) { /* Outbound Mailbox2 : Event from spurs */ spurs_receive_event(val[SPD_MBOX2], dev); } if (val[SPD_MBOX3] != 0) { /* Outbound Mailbox3 : primitive debug printf */ #ifdef DEBUG spurs_scp_debug_print(val[SPD_MBOX3], dev); #endif /* DEBUG */ spurs_set_rxmbox(SPD_MBOX3, val[SPD_MBOX3], dev); } if (val[SPD_MBOX4] != 0) { /* Outbound Mailbox4 : Reserved for VIP */ spd_err(dev, "Received mailbox4=0x%08x," "but it is reserved for VIP.\n", val[SPD_MBOX4]); spurs_set_rxmbox(SPD_MBOX4, val[SPD_MBOX4], dev); } if (val[SPD_MBOX5] != 0) { /* Outbound Mailbox5 : Boot status or Reserved VIP */ if (IS_FW_BOOTING(dev)) { spd_dbg(dev, "Received boot status in MBOX5=0x%08x\n", val[SPD_MBOX5]); dev->boot_info.boot_status[1] = val[SPD_MBOX5]; } else spd_err(dev, "Received mailbox5=0x%08x, but FW " "state is not booting.\n", val[SPD_MBOX5]); spurs_set_rxmbox(SPD_MBOX5, val[SPD_MBOX5], dev); } if (val[SPD_MBOX6] != 0) { /* Outbound Mailbox6 : print trace message */ spurs_print_trace_message(val[SPD_MBOX6], dev); } if (val[SPD_MBOX7] != 0) { /* Outbound Mailbox7 : panic */ spurs_enter_panic(val[SPD_MBOX7], dev); } spin_lock_irqsave(&isr_lock, irq_flag); dev->mbox_isr_flag = 0; spin_unlock_irqrestore(&isr_lock, irq_flag); } static uint32_t __spurs_isr(int irq, void *arg) { struct spd_dev *dev = (struct spd_dev *)arg; uint32_t stat; int mbox; spurs_io_read(dev, OFS_SCP_HOSTAT, &stat); if (stat == 0) return 0; spin_lock(&isr_lock); if (dev->mbox_isr_flag == 1) { spin_unlock(&isr_lock); return stat; } dev->mbox_flag |= stat; for (mbox = 0; mbox < SPD_MBOX_NUM; mbox++) if (stat & (1 << mbox)) spurs_io_read(dev, OFS_SCP_HOMD(mbox), &dev->mbox_val[mbox]); else dev->mbox_val[mbox] = 0; dev->mbox_isr_flag = 1; if ((stat & (1 << SPD_MBOX7)) == 0) spurs_interrupt_mask(0x0000007F, dev); spin_unlock(&isr_lock); /* start tasklet */ tasklet_schedule(&dev->tasklet); return dev->mbox_flag; } #if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19)) static irqreturn_t spurs_isr(int irq, void *arg) { return IRQ_RETVAL(__spurs_isr(irq, arg)); } #else static irqreturn_t spurs_isr(int irq, void *arg, struct pt_regs *regs) { return IRQ_RETVAL(__spurs_isr(irq, arg)); } #endif static void __spurs_free_dma_mem(struct spd_dma_info *dma, struct pci_dev *pdev) { if (dma->base_addr == NULL) return; dma_free_coherent(&pdev->dev, dma->size, dma->base_addr, dma->dma_addr); dma->base_addr = NULL; dma->dma_addr = 0; dma->size = 0; } static void spurs_free_dma_mem(struct spd_dev *dev) { struct pci_dev *pdev = dev->pci_dev; int i; __spurs_free_dma_mem(&dev->dma_packet, pdev); for (i = 0; i < SLB_SEG_NUM; i++) __spurs_free_dma_mem(&dev->slb[i], pdev); } static int __spurs_alloc_dma_mem(struct spd_dma_info *dma, unsigned long size, struct pci_dev *pdev) { if (size == 0) return -EINVAL; memset(dma, 0, sizeof(*dma)); dma->size = size; dma->base_addr = dma_alloc_coherent(&pdev->dev, dma->size, &dma->dma_addr, GFP_KERNEL); if (dma->base_addr == NULL) return -ENOMEM; return 0; } static unsigned long slb_seg_size[SLB_SEG_NUM] = { SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM0), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM1), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM2), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM3), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM4), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM5), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM6), SLB_SEG_BUF_SIZE_8MB(SLB_BUF_NUM7), }; static int spurs_alloc_dma_mem(struct spd_dev *dev) { struct pci_dev *pdev = dev->pci_dev; int i, ret; ret = __spurs_alloc_dma_mem(&dev->dma_packet, CMD_DMA_BUF_SIZE, pdev); if (ret < 0) { spd_dbg(dev, "Failed to allocate memory for packets.\n"); goto fail; } for (i = 0; i < SLB_SEG_NUM; i++) { if (slb_seg_size[i] == 0) continue; ret = __spurs_alloc_dma_mem(&dev->slb[i], slb_seg_size[i], pdev); if (ret < 0) { spd_err(dev, "Failed to allocate DMA memory for SLB.\n"); goto fail; } } return 0; fail: spurs_free_dma_mem(dev); return ret; } static void spurs_print_system_config(struct spd_system_config *sys, struct spd_dev *dev) { #ifdef DEBUG spd_dbg(dev, "clk_mode : %d\n", sys->clk_mode); spd_dbg(dev, "clk_mode_limit : %d\n", sys->clk_mode_limit); spd_dbg(dev, "d3_disable : %d\n", sys->d3_disable); spd_dbg(dev, "d3_delay : %d\n", sys->d3_delay); spd_dbg(dev, "d3_delay_limit : %d\n", sys->d3_delay_limit); spd_dbg(dev, "cg_disable : %d\n", sys->cg_disable); spd_dbg(dev, "cg_delay : %d\n", sys->cg_delay); spd_dbg(dev, "cg_delay_limit : %d\n", sys->cg_delay_limit); spd_dbg(dev, "wdt_disable : %d\n", sys->wdt_disable); spd_dbg(dev, "max_count : %d\n", sys->max_count); spd_dbg(dev, "total_count : %d\n", sys->total_count); spd_dbg(dev, "dbg_support : %d\n", sys->dbg_support); spd_dbg(dev, "cache_type : %d\n", sys->cache_type); spd_dbg(dev, "boot_timeout : %d\n", sys->boot_timeout); spd_dbg(dev, "sp3_info : %x\n", sys->sp3_info); spd_dbg(dev, "pci_inorder : %d\n", sys->pci_inorder); spd_dbg(dev, "wdt_watch_cycle : %d\n", sys->wdt_watch_cycle); spd_dbg(dev, "gpio_d3hot : %d\n", sys->gpio_d3hot); spd_dbg(dev, "xdr_test : %d\n", sys->xdr_test); spd_dbg(dev, "spd_trace_port : %d\n", sys->spd_trace_port); #endif /* DEBUG */ } static __devinit void spurs_init_system_config(struct spd_dev *dev) { dev->sys_config_init = dev->sys_config = default_system_config; spurs_print_system_config(&dev->sys_config, dev); } static int __devinit spurs_pci_init(struct pci_dev *pci_dev, const struct pci_device_id *ent) { struct spd_dev *dev = NULL; int status = 0; int idx; struct class_device *class_dev; for (idx = 0; idx < SPD_DEVICE_MAX; idx++) { if (spd_device[idx] == NULL) break; } if (idx == SPD_DEVICE_MAX) { __spd_err("Found no available device.\n"); return -ENOMEM; } dev = kzalloc(sizeof(struct spd_dev), GFP_KERNEL); if (dev == NULL) { __spd_err("Failed to allocate device data.\n"); return -ENOMEM; } spd_device[idx] = dev; dev->id = idx; dev->dev_id = MKDEV(major_dev_number, dev->id); if ((status = pci_enable_device(pci_dev))) { spd_err(dev, "pci_enable_device() failed.\n"); goto fail; } status = pci_request_regions(pci_dev, SPD_DRIVER_NAME); if (status) { spd_err(dev, "pci_request_regions() failed.\n"); goto fail; } pci_set_master(pci_dev); dev->pci_dev = pci_dev; pci_set_drvdata(pci_dev, dev); pci_read_config_dword(pci_dev, PCI_BASE_ADDRESS_0, &dev->pci_bar0); dev->io_base_ba = pci_resource_start(pci_dev, 0); dev->io_size = pci_resource_len(pci_dev, 0); dev->io_base = ioremap(dev->io_base_ba, dev->io_size); if (dev->io_base == NULL) { spd_err(dev, "ioremap() failed.\n"); status = -ENOMEM; goto fail; } spd_dbg(dev, "IO registers=%p\n", dev->io_base); /* Allocate DMA memory */ if (spurs_alloc_dma_mem(dev)) { status = -ENOMEM; goto fail; } mutex_init(&dev->mutex); mutex_init(&dev->pm_mutex); spin_lock_init(&dev->spinlock); /* Set System config */ spurs_init_system_config(dev); /* Set FW boot state */ dev->dev_state.clk_mode = dev->sys_config.clk_mode; dev->dev_state.fw_state = SPD_FW_STATE_HALTED; dev->reset_state = RESET_STATE_COMPLETED; /* Init wait queue for software reset */ init_waitqueue_head(&dev->soft_reset_que); /* Init boot parameter */ status = spurs_init_boot_info(dev); if (status != 0) goto fail; /* Init session */ spurs_init_session_info(dev); /* Init workqueue */ dev->work_queue = create_workqueue("spurs"); if (dev->work_queue == NULL) { status = -ENOMEM; goto fail; } INIT_WORK_TASK(reset_d3_task, spurs_reset_d3_task, dev); tasklet_init(&dev->tasklet, spurs_isr_tasklet, (unsigned long)dev); /* Init timer */ spurs_init_timer_info(dev); /* Init ioctl tag */ status = spurs_init_ioctl_tag_info(tag_id_num, dev); if (status < 0) goto fail; /* Setup IRQ */ dev->irq = pci_dev->irq; status = request_irq(dev->irq, spurs_isr, IRQF_SHARED, SPD_DRIVER_NAME, (void *)dev); if (status < 0) { spd_err(dev, "request_irq() failed.\n"); goto fail; } spd_dbg(dev, "IRQ=%d.\n", pci_dev->irq); status = pci_set_power_state(dev->pci_dev, PCI_D0); if (status < 0) { spd_err(dev, "pci_set_power_state() failed\n"); goto fail; } dev->power_state = SPD_POWER_D0; dev->enter_d3 = SPD_NO_ENTER_D3; dev->mbox_flag = 0; dev->mbox_isr_flag = 0; /* Fixed by T.Nagai 2009/11/02 */ class_dev = device_create_drvdata(spurs_class, NULL, dev->dev_id, &dev->pci_dev->dev, "%s%d", SPD_DRIVER_NAME, MINOR(dev->dev_id)); if (IS_ERR(class_dev)) { status = PTR_ERR(class_dev); /* Fixed by T.Nagai 2009/11/02 */ spd_err(dev, "device_create_drvdata() failed(%d)\n", status); goto fail; } dev->class_dev = class_dev; return status; fail: spurs_cleanup_boot_info(dev); spurs_free_dma_mem(dev); if (dev->io_base) iounmap(dev->io_base); pci_release_regions(pci_dev); if (dev->irq != 0) { free_irq(dev->irq, (void *)dev); dev->irq = 0; } if (dev) { spd_device[dev->id] = NULL; kfree(dev); } pci_set_drvdata(pci_dev, NULL); pci_disable_device(pci_dev); return status; } static __devexit void spurs_remove(struct pci_dev *pci_dev) { struct spd_dev *dev = NULL; dev = pci_get_drvdata(pci_dev); if (dev != NULL) { spd_dbg(dev, "remove spurs%d.\n", dev->id); spurs_stop_move_d3_timer(&dev->timer_info); if (IS_FW_RUNNING(dev) || IS_FW_INITTING(dev)) { /* Stop firmware */ spurs_stop_panic_check_timer(&dev->timer_info); spurs_stop_wdt_check_timer(&dev->timer_info); spurs_soft_reset(SOFT_RESET_CMD, dev); spurs_stop_scp(dev); dev->dev_state.fw_state = SPD_FW_STATE_HALTED; } /* Fixed by T.Nagai 2009/11/02 */ device_destroy(spurs_class, dev->dev_id); flush_workqueue(dev->work_queue); destroy_workqueue(dev->work_queue); tasklet_kill(&dev->tasklet); if (dev->irq != 0) { free_irq(dev->irq, (void *)dev); dev->irq = 0; } spurs_cleanup_boot_info(dev); if (dev->io_base) iounmap(dev->io_base); pci_release_regions(pci_dev); spurs_cleanup_ioctl_tag_info(dev); spurs_free_dma_mem(dev); pci_set_drvdata(pci_dev, NULL); spd_device[dev->id] = NULL; kfree(dev); pci_disable_device(pci_dev); } } static struct pci_device_id spurs_id_table[] __devinitdata = { { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_SPURS, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, { 0, }, }; MODULE_DEVICE_TABLE(pci, spurs_id_table); static struct file_operations spurs_fops = { .owner = THIS_MODULE, .llseek = no_llseek, .open = spurs_open, .ioctl = spurs_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = spurs_compat_ioctl, #endif .release = spurs_close }; static struct pci_driver spurs_driver = { .name = SPD_DRIVER_NAME, .id_table = spurs_id_table, .probe = spurs_pci_init, .remove = __devexit_p(spurs_remove), .suspend = spurs_suspend, .resume = spurs_resume, }; static int __init spurs_board_init(void) { int ret = 0; dev_t dev_id; print_version(); __spd_dbg2(">>> enter\n"); if (reset_phase < 0) { __spd_dbg("reset_phase is invalid.\n"); return -EINVAL; } memset(spd_device, 0, sizeof(spd_device)); spurs_class = class_create(THIS_MODULE, SPD_DRIVER_NAME); if (IS_ERR(spurs_class)) { __spd_err("Failed to create udev class.\n"); return PTR_ERR(spurs_class); } if (major_dev_number) { dev_id = MKDEV(major_dev_number, 0); ret = register_chrdev_region(dev_id, SPD_DEVICE_MAX, SPD_DRIVER_NAME); } else { ret = alloc_chrdev_region(&dev_id, 0, SPD_DEVICE_MAX, SPD_DRIVER_NAME); major_dev_number = MAJOR(dev_id); } __spd_dbg("major_dev_number = %d\n", major_dev_number); if (ret) { __spd_err("Failed to allocate device ID.\n"); class_destroy(spurs_class); return ret; } cdev_init(&spd_cdev, &spurs_fops); ret = cdev_add(&spd_cdev, dev_id, SPD_DEVICE_MAX); if (ret) { __spd_err("Failed to add character driver.\n"); unregister_chrdev_region(dev_id, SPD_DEVICE_MAX); class_destroy(spurs_class); return ret; } ret = pci_register_driver(&spurs_driver); if (ret < 0) { __spd_err("Failed to register PCI driver.\n"); cdev_del(&spd_cdev); unregister_chrdev_region(dev_id, SPD_DEVICE_MAX); class_destroy(spurs_class); return ret; } spurs_proc_entry = create_proc_read_entry(SPD_PROC_DEVINFO, 0, NULL, spurs_read_proc, NULL); if (spurs_proc_entry == NULL) { __spd_err("Failed to create /proc%s\n", SPD_PROC_DEVINFO); cdev_del(&spd_cdev); unregister_chrdev_region(dev_id, SPD_DEVICE_MAX); pci_unregister_driver(&spurs_driver); class_destroy(spurs_class); return -ENOMEM; } return ret; } static void spurs_board_cleanup(void) { dev_t dev_id = MKDEV(major_dev_number, 0); if (spurs_proc_entry != NULL) remove_proc_entry(SPD_PROC_DEVINFO, NULL); pci_unregister_driver(&spurs_driver); cdev_del(&spd_cdev); unregister_chrdev_region(dev_id, SPD_DEVICE_MAX); if (spurs_class != NULL) class_destroy(spurs_class); } module_init(spurs_board_init); module_exit(spurs_board_cleanup);