mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-08 09:08:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
2
drivers/block/rsxx/Makefile
Normal file
2
drivers/block/rsxx/Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
obj-$(CONFIG_BLK_DEV_RSXX) += rsxx.o
|
||||
rsxx-objs := config.o core.o cregs.o dev.o dma.o
|
211
drivers/block/rsxx/config.c
Normal file
211
drivers/block/rsxx/config.c
Normal file
|
@ -0,0 +1,211 @@
|
|||
/*
|
||||
* Filename: config.c
|
||||
*
|
||||
*
|
||||
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
|
||||
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2013 IBM 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/crc32.h>
|
||||
#include <linux/swab.h>
|
||||
|
||||
#include "rsxx_priv.h"
|
||||
#include "rsxx_cfg.h"
|
||||
|
||||
static void initialize_config(struct rsxx_card_cfg *cfg)
|
||||
{
|
||||
cfg->hdr.version = RSXX_CFG_VERSION;
|
||||
|
||||
cfg->data.block_size = RSXX_HW_BLK_SIZE;
|
||||
cfg->data.stripe_size = RSXX_HW_BLK_SIZE;
|
||||
cfg->data.vendor_id = RSXX_VENDOR_ID_IBM;
|
||||
cfg->data.cache_order = (-1);
|
||||
cfg->data.intr_coal.mode = RSXX_INTR_COAL_DISABLED;
|
||||
cfg->data.intr_coal.count = 0;
|
||||
cfg->data.intr_coal.latency = 0;
|
||||
}
|
||||
|
||||
static u32 config_data_crc32(struct rsxx_card_cfg *cfg)
|
||||
{
|
||||
/*
|
||||
* Return the compliment of the CRC to ensure compatibility
|
||||
* (i.e. this is how early rsxx drivers did it.)
|
||||
*/
|
||||
|
||||
return ~crc32(~0, &cfg->data, sizeof(cfg->data));
|
||||
}
|
||||
|
||||
|
||||
/*----------------- Config Byte Swap Functions -------------------*/
|
||||
static void config_hdr_be_to_cpu(struct card_cfg_hdr *hdr)
|
||||
{
|
||||
hdr->version = be32_to_cpu((__force __be32) hdr->version);
|
||||
hdr->crc = be32_to_cpu((__force __be32) hdr->crc);
|
||||
}
|
||||
|
||||
static void config_hdr_cpu_to_be(struct card_cfg_hdr *hdr)
|
||||
{
|
||||
hdr->version = (__force u32) cpu_to_be32(hdr->version);
|
||||
hdr->crc = (__force u32) cpu_to_be32(hdr->crc);
|
||||
}
|
||||
|
||||
static void config_data_swab(struct rsxx_card_cfg *cfg)
|
||||
{
|
||||
u32 *data = (u32 *) &cfg->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (sizeof(cfg->data) / 4); i++)
|
||||
data[i] = swab32(data[i]);
|
||||
}
|
||||
|
||||
static void config_data_le_to_cpu(struct rsxx_card_cfg *cfg)
|
||||
{
|
||||
u32 *data = (u32 *) &cfg->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (sizeof(cfg->data) / 4); i++)
|
||||
data[i] = le32_to_cpu((__force __le32) data[i]);
|
||||
}
|
||||
|
||||
static void config_data_cpu_to_le(struct rsxx_card_cfg *cfg)
|
||||
{
|
||||
u32 *data = (u32 *) &cfg->data;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < (sizeof(cfg->data) / 4); i++)
|
||||
data[i] = (__force u32) cpu_to_le32(data[i]);
|
||||
}
|
||||
|
||||
|
||||
/*----------------- Config Operations ------------------*/
|
||||
static int rsxx_save_config(struct rsxx_cardinfo *card)
|
||||
{
|
||||
struct rsxx_card_cfg cfg;
|
||||
int st;
|
||||
|
||||
memcpy(&cfg, &card->config, sizeof(cfg));
|
||||
|
||||
if (unlikely(cfg.hdr.version != RSXX_CFG_VERSION)) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Cannot save config with invalid version %d\n",
|
||||
cfg.hdr.version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Convert data to little endian for the CRC calculation. */
|
||||
config_data_cpu_to_le(&cfg);
|
||||
|
||||
cfg.hdr.crc = config_data_crc32(&cfg);
|
||||
|
||||
/*
|
||||
* Swap the data from little endian to big endian so it can be
|
||||
* stored.
|
||||
*/
|
||||
config_data_swab(&cfg);
|
||||
config_hdr_cpu_to_be(&cfg.hdr);
|
||||
|
||||
st = rsxx_creg_write(card, CREG_ADD_CONFIG, sizeof(cfg), &cfg, 1);
|
||||
if (st)
|
||||
return st;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsxx_load_config(struct rsxx_cardinfo *card)
|
||||
{
|
||||
int st;
|
||||
u32 crc;
|
||||
|
||||
st = rsxx_creg_read(card, CREG_ADD_CONFIG, sizeof(card->config),
|
||||
&card->config, 1);
|
||||
if (st) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Failed reading card config.\n");
|
||||
return st;
|
||||
}
|
||||
|
||||
config_hdr_be_to_cpu(&card->config.hdr);
|
||||
|
||||
if (card->config.hdr.version == RSXX_CFG_VERSION) {
|
||||
/*
|
||||
* We calculate the CRC with the data in little endian, because
|
||||
* early drivers did not take big endian CPUs into account.
|
||||
* The data is always stored in big endian, so we need to byte
|
||||
* swap it before calculating the CRC.
|
||||
*/
|
||||
|
||||
config_data_swab(&card->config);
|
||||
|
||||
/* Check the CRC */
|
||||
crc = config_data_crc32(&card->config);
|
||||
if (crc != card->config.hdr.crc) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Config corruption detected!\n");
|
||||
dev_info(CARD_TO_DEV(card),
|
||||
"CRC (sb x%08x is x%08x)\n",
|
||||
card->config.hdr.crc, crc);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Convert the data to CPU byteorder */
|
||||
config_data_le_to_cpu(&card->config);
|
||||
|
||||
} else if (card->config.hdr.version != 0) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Invalid config version %d.\n",
|
||||
card->config.hdr.version);
|
||||
/*
|
||||
* Config version changes require special handling from the
|
||||
* user
|
||||
*/
|
||||
return -EINVAL;
|
||||
} else {
|
||||
dev_info(CARD_TO_DEV(card),
|
||||
"Initializing card configuration.\n");
|
||||
initialize_config(&card->config);
|
||||
st = rsxx_save_config(card);
|
||||
if (st)
|
||||
return st;
|
||||
}
|
||||
|
||||
card->config_valid = 1;
|
||||
|
||||
dev_dbg(CARD_TO_DEV(card), "version: x%08x\n",
|
||||
card->config.hdr.version);
|
||||
dev_dbg(CARD_TO_DEV(card), "crc: x%08x\n",
|
||||
card->config.hdr.crc);
|
||||
dev_dbg(CARD_TO_DEV(card), "block_size: x%08x\n",
|
||||
card->config.data.block_size);
|
||||
dev_dbg(CARD_TO_DEV(card), "stripe_size: x%08x\n",
|
||||
card->config.data.stripe_size);
|
||||
dev_dbg(CARD_TO_DEV(card), "vendor_id: x%08x\n",
|
||||
card->config.data.vendor_id);
|
||||
dev_dbg(CARD_TO_DEV(card), "cache_order: x%08x\n",
|
||||
card->config.data.cache_order);
|
||||
dev_dbg(CARD_TO_DEV(card), "mode: x%08x\n",
|
||||
card->config.data.intr_coal.mode);
|
||||
dev_dbg(CARD_TO_DEV(card), "count: x%08x\n",
|
||||
card->config.data.intr_coal.count);
|
||||
dev_dbg(CARD_TO_DEV(card), "latency: x%08x\n",
|
||||
card->config.data.intr_coal.latency);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
1144
drivers/block/rsxx/core.c
Normal file
1144
drivers/block/rsxx/core.c
Normal file
File diff suppressed because it is too large
Load diff
804
drivers/block/rsxx/cregs.c
Normal file
804
drivers/block/rsxx/cregs.c
Normal file
|
@ -0,0 +1,804 @@
|
|||
/*
|
||||
* Filename: cregs.c
|
||||
*
|
||||
*
|
||||
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
|
||||
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2013 IBM 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "rsxx_priv.h"
|
||||
|
||||
#define CREG_TIMEOUT_MSEC 10000
|
||||
|
||||
typedef void (*creg_cmd_cb)(struct rsxx_cardinfo *card,
|
||||
struct creg_cmd *cmd,
|
||||
int st);
|
||||
|
||||
struct creg_cmd {
|
||||
struct list_head list;
|
||||
creg_cmd_cb cb;
|
||||
void *cb_private;
|
||||
unsigned int op;
|
||||
unsigned int addr;
|
||||
int cnt8;
|
||||
void *buf;
|
||||
unsigned int stream;
|
||||
unsigned int status;
|
||||
};
|
||||
|
||||
static struct kmem_cache *creg_cmd_pool;
|
||||
|
||||
|
||||
/*------------ Private Functions --------------*/
|
||||
|
||||
#if defined(__LITTLE_ENDIAN)
|
||||
#define LITTLE_ENDIAN 1
|
||||
#elif defined(__BIG_ENDIAN)
|
||||
#define LITTLE_ENDIAN 0
|
||||
#else
|
||||
#error Unknown endianess!!! Aborting...
|
||||
#endif
|
||||
|
||||
static int copy_to_creg_data(struct rsxx_cardinfo *card,
|
||||
int cnt8,
|
||||
void *buf,
|
||||
unsigned int stream)
|
||||
{
|
||||
int i = 0;
|
||||
u32 *data = buf;
|
||||
|
||||
if (unlikely(card->eeh_state))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; cnt8 > 0; i++, cnt8 -= 4) {
|
||||
/*
|
||||
* Firmware implementation makes it necessary to byte swap on
|
||||
* little endian processors.
|
||||
*/
|
||||
if (LITTLE_ENDIAN && stream)
|
||||
iowrite32be(data[i], card->regmap + CREG_DATA(i));
|
||||
else
|
||||
iowrite32(data[i], card->regmap + CREG_DATA(i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int copy_from_creg_data(struct rsxx_cardinfo *card,
|
||||
int cnt8,
|
||||
void *buf,
|
||||
unsigned int stream)
|
||||
{
|
||||
int i = 0;
|
||||
u32 *data = buf;
|
||||
|
||||
if (unlikely(card->eeh_state))
|
||||
return -EIO;
|
||||
|
||||
for (i = 0; cnt8 > 0; i++, cnt8 -= 4) {
|
||||
/*
|
||||
* Firmware implementation makes it necessary to byte swap on
|
||||
* little endian processors.
|
||||
*/
|
||||
if (LITTLE_ENDIAN && stream)
|
||||
data[i] = ioread32be(card->regmap + CREG_DATA(i));
|
||||
else
|
||||
data[i] = ioread32(card->regmap + CREG_DATA(i));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void creg_issue_cmd(struct rsxx_cardinfo *card, struct creg_cmd *cmd)
|
||||
{
|
||||
int st;
|
||||
|
||||
if (unlikely(card->eeh_state))
|
||||
return;
|
||||
|
||||
iowrite32(cmd->addr, card->regmap + CREG_ADD);
|
||||
iowrite32(cmd->cnt8, card->regmap + CREG_CNT);
|
||||
|
||||
if (cmd->op == CREG_OP_WRITE) {
|
||||
if (cmd->buf) {
|
||||
st = copy_to_creg_data(card, cmd->cnt8,
|
||||
cmd->buf, cmd->stream);
|
||||
if (st)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (unlikely(card->eeh_state))
|
||||
return;
|
||||
|
||||
/* Setting the valid bit will kick off the command. */
|
||||
iowrite32(cmd->op, card->regmap + CREG_CMD);
|
||||
}
|
||||
|
||||
static void creg_kick_queue(struct rsxx_cardinfo *card)
|
||||
{
|
||||
if (card->creg_ctrl.active || list_empty(&card->creg_ctrl.queue))
|
||||
return;
|
||||
|
||||
card->creg_ctrl.active = 1;
|
||||
card->creg_ctrl.active_cmd = list_first_entry(&card->creg_ctrl.queue,
|
||||
struct creg_cmd, list);
|
||||
list_del(&card->creg_ctrl.active_cmd->list);
|
||||
card->creg_ctrl.q_depth--;
|
||||
|
||||
/*
|
||||
* We have to set the timer before we push the new command. Otherwise,
|
||||
* we could create a race condition that would occur if the timer
|
||||
* was not canceled, and expired after the new command was pushed,
|
||||
* but before the command was issued to hardware.
|
||||
*/
|
||||
mod_timer(&card->creg_ctrl.cmd_timer,
|
||||
jiffies + msecs_to_jiffies(CREG_TIMEOUT_MSEC));
|
||||
|
||||
creg_issue_cmd(card, card->creg_ctrl.active_cmd);
|
||||
}
|
||||
|
||||
static int creg_queue_cmd(struct rsxx_cardinfo *card,
|
||||
unsigned int op,
|
||||
unsigned int addr,
|
||||
unsigned int cnt8,
|
||||
void *buf,
|
||||
int stream,
|
||||
creg_cmd_cb callback,
|
||||
void *cb_private)
|
||||
{
|
||||
struct creg_cmd *cmd;
|
||||
|
||||
/* Don't queue stuff up if we're halted. */
|
||||
if (unlikely(card->halt))
|
||||
return -EINVAL;
|
||||
|
||||
if (card->creg_ctrl.reset)
|
||||
return -EAGAIN;
|
||||
|
||||
if (cnt8 > MAX_CREG_DATA8)
|
||||
return -EINVAL;
|
||||
|
||||
cmd = kmem_cache_alloc(creg_cmd_pool, GFP_KERNEL);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&cmd->list);
|
||||
|
||||
cmd->op = op;
|
||||
cmd->addr = addr;
|
||||
cmd->cnt8 = cnt8;
|
||||
cmd->buf = buf;
|
||||
cmd->stream = stream;
|
||||
cmd->cb = callback;
|
||||
cmd->cb_private = cb_private;
|
||||
cmd->status = 0;
|
||||
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
list_add_tail(&cmd->list, &card->creg_ctrl.queue);
|
||||
card->creg_ctrl.q_depth++;
|
||||
creg_kick_queue(card);
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void creg_cmd_timed_out(unsigned long data)
|
||||
{
|
||||
struct rsxx_cardinfo *card = (struct rsxx_cardinfo *) data;
|
||||
struct creg_cmd *cmd;
|
||||
|
||||
spin_lock(&card->creg_ctrl.lock);
|
||||
cmd = card->creg_ctrl.active_cmd;
|
||||
card->creg_ctrl.active_cmd = NULL;
|
||||
spin_unlock(&card->creg_ctrl.lock);
|
||||
|
||||
if (cmd == NULL) {
|
||||
card->creg_ctrl.creg_stats.creg_timeout++;
|
||||
dev_warn(CARD_TO_DEV(card),
|
||||
"No active command associated with timeout!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->cb)
|
||||
cmd->cb(card, cmd, -ETIMEDOUT);
|
||||
|
||||
kmem_cache_free(creg_cmd_pool, cmd);
|
||||
|
||||
|
||||
spin_lock(&card->creg_ctrl.lock);
|
||||
card->creg_ctrl.active = 0;
|
||||
creg_kick_queue(card);
|
||||
spin_unlock(&card->creg_ctrl.lock);
|
||||
}
|
||||
|
||||
|
||||
static void creg_cmd_done(struct work_struct *work)
|
||||
{
|
||||
struct rsxx_cardinfo *card;
|
||||
struct creg_cmd *cmd;
|
||||
int st = 0;
|
||||
|
||||
card = container_of(work, struct rsxx_cardinfo,
|
||||
creg_ctrl.done_work);
|
||||
|
||||
/*
|
||||
* The timer could not be cancelled for some reason,
|
||||
* race to pop the active command.
|
||||
*/
|
||||
if (del_timer_sync(&card->creg_ctrl.cmd_timer) == 0)
|
||||
card->creg_ctrl.creg_stats.failed_cancel_timer++;
|
||||
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
cmd = card->creg_ctrl.active_cmd;
|
||||
card->creg_ctrl.active_cmd = NULL;
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
|
||||
if (cmd == NULL) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Spurious creg interrupt!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
card->creg_ctrl.creg_stats.stat = ioread32(card->regmap + CREG_STAT);
|
||||
cmd->status = card->creg_ctrl.creg_stats.stat;
|
||||
if ((cmd->status & CREG_STAT_STATUS_MASK) == 0) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Invalid status on creg command\n");
|
||||
/*
|
||||
* At this point we're probably reading garbage from HW. Don't
|
||||
* do anything else that could mess up the system and let
|
||||
* the sync function return an error.
|
||||
*/
|
||||
st = -EIO;
|
||||
goto creg_done;
|
||||
} else if (cmd->status & CREG_STAT_ERROR) {
|
||||
st = -EIO;
|
||||
}
|
||||
|
||||
if ((cmd->op == CREG_OP_READ)) {
|
||||
unsigned int cnt8 = ioread32(card->regmap + CREG_CNT);
|
||||
|
||||
/* Paranoid Sanity Checks */
|
||||
if (!cmd->buf) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Buffer not given for read.\n");
|
||||
st = -EIO;
|
||||
goto creg_done;
|
||||
}
|
||||
if (cnt8 != cmd->cnt8) {
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"count mismatch\n");
|
||||
st = -EIO;
|
||||
goto creg_done;
|
||||
}
|
||||
|
||||
st = copy_from_creg_data(card, cnt8, cmd->buf, cmd->stream);
|
||||
}
|
||||
|
||||
creg_done:
|
||||
if (cmd->cb)
|
||||
cmd->cb(card, cmd, st);
|
||||
|
||||
kmem_cache_free(creg_cmd_pool, cmd);
|
||||
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
card->creg_ctrl.active = 0;
|
||||
creg_kick_queue(card);
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
}
|
||||
|
||||
static void creg_reset(struct rsxx_cardinfo *card)
|
||||
{
|
||||
struct creg_cmd *cmd = NULL;
|
||||
struct creg_cmd *tmp;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* mutex_trylock is used here because if reset_lock is taken then a
|
||||
* reset is already happening. So, we can just go ahead and return.
|
||||
*/
|
||||
if (!mutex_trylock(&card->creg_ctrl.reset_lock))
|
||||
return;
|
||||
|
||||
card->creg_ctrl.reset = 1;
|
||||
spin_lock_irqsave(&card->irq_lock, flags);
|
||||
rsxx_disable_ier_and_isr(card, CR_INTR_CREG | CR_INTR_EVENT);
|
||||
spin_unlock_irqrestore(&card->irq_lock, flags);
|
||||
|
||||
dev_warn(CARD_TO_DEV(card),
|
||||
"Resetting creg interface for recovery\n");
|
||||
|
||||
/* Cancel outstanding commands */
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) {
|
||||
list_del(&cmd->list);
|
||||
card->creg_ctrl.q_depth--;
|
||||
if (cmd->cb)
|
||||
cmd->cb(card, cmd, -ECANCELED);
|
||||
kmem_cache_free(creg_cmd_pool, cmd);
|
||||
}
|
||||
|
||||
cmd = card->creg_ctrl.active_cmd;
|
||||
card->creg_ctrl.active_cmd = NULL;
|
||||
if (cmd) {
|
||||
if (timer_pending(&card->creg_ctrl.cmd_timer))
|
||||
del_timer_sync(&card->creg_ctrl.cmd_timer);
|
||||
|
||||
if (cmd->cb)
|
||||
cmd->cb(card, cmd, -ECANCELED);
|
||||
kmem_cache_free(creg_cmd_pool, cmd);
|
||||
|
||||
card->creg_ctrl.active = 0;
|
||||
}
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
|
||||
card->creg_ctrl.reset = 0;
|
||||
spin_lock_irqsave(&card->irq_lock, flags);
|
||||
rsxx_enable_ier_and_isr(card, CR_INTR_CREG | CR_INTR_EVENT);
|
||||
spin_unlock_irqrestore(&card->irq_lock, flags);
|
||||
|
||||
mutex_unlock(&card->creg_ctrl.reset_lock);
|
||||
}
|
||||
|
||||
/* Used for synchronous accesses */
|
||||
struct creg_completion {
|
||||
struct completion *cmd_done;
|
||||
int st;
|
||||
u32 creg_status;
|
||||
};
|
||||
|
||||
static void creg_cmd_done_cb(struct rsxx_cardinfo *card,
|
||||
struct creg_cmd *cmd,
|
||||
int st)
|
||||
{
|
||||
struct creg_completion *cmd_completion;
|
||||
|
||||
cmd_completion = cmd->cb_private;
|
||||
BUG_ON(!cmd_completion);
|
||||
|
||||
cmd_completion->st = st;
|
||||
cmd_completion->creg_status = cmd->status;
|
||||
complete(cmd_completion->cmd_done);
|
||||
}
|
||||
|
||||
static int __issue_creg_rw(struct rsxx_cardinfo *card,
|
||||
unsigned int op,
|
||||
unsigned int addr,
|
||||
unsigned int cnt8,
|
||||
void *buf,
|
||||
int stream,
|
||||
unsigned int *hw_stat)
|
||||
{
|
||||
DECLARE_COMPLETION_ONSTACK(cmd_done);
|
||||
struct creg_completion completion;
|
||||
unsigned long timeout;
|
||||
int st;
|
||||
|
||||
completion.cmd_done = &cmd_done;
|
||||
completion.st = 0;
|
||||
completion.creg_status = 0;
|
||||
|
||||
st = creg_queue_cmd(card, op, addr, cnt8, buf, stream, creg_cmd_done_cb,
|
||||
&completion);
|
||||
if (st)
|
||||
return st;
|
||||
|
||||
/*
|
||||
* This timeout is necessary for unresponsive hardware. The additional
|
||||
* 20 seconds to used to guarantee that each cregs requests has time to
|
||||
* complete.
|
||||
*/
|
||||
timeout = msecs_to_jiffies(CREG_TIMEOUT_MSEC *
|
||||
card->creg_ctrl.q_depth + 20000);
|
||||
|
||||
/*
|
||||
* The creg interface is guaranteed to complete. It has a timeout
|
||||
* mechanism that will kick in if hardware does not respond.
|
||||
*/
|
||||
st = wait_for_completion_timeout(completion.cmd_done, timeout);
|
||||
if (st == 0) {
|
||||
/*
|
||||
* This is really bad, because the kernel timer did not
|
||||
* expire and notify us of a timeout!
|
||||
*/
|
||||
dev_crit(CARD_TO_DEV(card),
|
||||
"cregs timer failed\n");
|
||||
creg_reset(card);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
*hw_stat = completion.creg_status;
|
||||
|
||||
if (completion.st) {
|
||||
/*
|
||||
* This read is needed to verify that there has not been any
|
||||
* extreme errors that might have occurred, i.e. EEH. The
|
||||
* function iowrite32 will not detect EEH errors, so it is
|
||||
* necessary that we recover if such an error is the reason
|
||||
* for the timeout. This is a dummy read.
|
||||
*/
|
||||
ioread32(card->regmap + SCRATCH);
|
||||
|
||||
dev_warn(CARD_TO_DEV(card),
|
||||
"creg command failed(%d x%08x)\n",
|
||||
completion.st, addr);
|
||||
return completion.st;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int issue_creg_rw(struct rsxx_cardinfo *card,
|
||||
u32 addr,
|
||||
unsigned int size8,
|
||||
void *data,
|
||||
int stream,
|
||||
int read)
|
||||
{
|
||||
unsigned int hw_stat;
|
||||
unsigned int xfer;
|
||||
unsigned int op;
|
||||
int st;
|
||||
|
||||
op = read ? CREG_OP_READ : CREG_OP_WRITE;
|
||||
|
||||
do {
|
||||
xfer = min_t(unsigned int, size8, MAX_CREG_DATA8);
|
||||
|
||||
st = __issue_creg_rw(card, op, addr, xfer,
|
||||
data, stream, &hw_stat);
|
||||
if (st)
|
||||
return st;
|
||||
|
||||
data = (char *)data + xfer;
|
||||
addr += xfer;
|
||||
size8 -= xfer;
|
||||
} while (size8);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ---------------------------- Public API ---------------------------------- */
|
||||
int rsxx_creg_write(struct rsxx_cardinfo *card,
|
||||
u32 addr,
|
||||
unsigned int size8,
|
||||
void *data,
|
||||
int byte_stream)
|
||||
{
|
||||
return issue_creg_rw(card, addr, size8, data, byte_stream, 0);
|
||||
}
|
||||
|
||||
int rsxx_creg_read(struct rsxx_cardinfo *card,
|
||||
u32 addr,
|
||||
unsigned int size8,
|
||||
void *data,
|
||||
int byte_stream)
|
||||
{
|
||||
return issue_creg_rw(card, addr, size8, data, byte_stream, 1);
|
||||
}
|
||||
|
||||
int rsxx_get_card_state(struct rsxx_cardinfo *card, unsigned int *state)
|
||||
{
|
||||
return rsxx_creg_read(card, CREG_ADD_CARD_STATE,
|
||||
sizeof(*state), state, 0);
|
||||
}
|
||||
|
||||
int rsxx_get_card_size8(struct rsxx_cardinfo *card, u64 *size8)
|
||||
{
|
||||
unsigned int size;
|
||||
int st;
|
||||
|
||||
st = rsxx_creg_read(card, CREG_ADD_CARD_SIZE,
|
||||
sizeof(size), &size, 0);
|
||||
if (st)
|
||||
return st;
|
||||
|
||||
*size8 = (u64)size * RSXX_HW_BLK_SIZE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int rsxx_get_num_targets(struct rsxx_cardinfo *card,
|
||||
unsigned int *n_targets)
|
||||
{
|
||||
return rsxx_creg_read(card, CREG_ADD_NUM_TARGETS,
|
||||
sizeof(*n_targets), n_targets, 0);
|
||||
}
|
||||
|
||||
int rsxx_get_card_capabilities(struct rsxx_cardinfo *card,
|
||||
u32 *capabilities)
|
||||
{
|
||||
return rsxx_creg_read(card, CREG_ADD_CAPABILITIES,
|
||||
sizeof(*capabilities), capabilities, 0);
|
||||
}
|
||||
|
||||
int rsxx_issue_card_cmd(struct rsxx_cardinfo *card, u32 cmd)
|
||||
{
|
||||
return rsxx_creg_write(card, CREG_ADD_CARD_CMD,
|
||||
sizeof(cmd), &cmd, 0);
|
||||
}
|
||||
|
||||
|
||||
/*----------------- HW Log Functions -------------------*/
|
||||
static void hw_log_msg(struct rsxx_cardinfo *card, const char *str, int len)
|
||||
{
|
||||
static char level;
|
||||
|
||||
/*
|
||||
* New messages start with "<#>", where # is the log level. Messages
|
||||
* that extend past the log buffer will use the previous level
|
||||
*/
|
||||
if ((len > 3) && (str[0] == '<') && (str[2] == '>')) {
|
||||
level = str[1];
|
||||
str += 3; /* Skip past the log level. */
|
||||
len -= 3;
|
||||
}
|
||||
|
||||
switch (level) {
|
||||
case '0':
|
||||
dev_emerg(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '1':
|
||||
dev_alert(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '2':
|
||||
dev_crit(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '3':
|
||||
dev_err(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '4':
|
||||
dev_warn(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '5':
|
||||
dev_notice(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '6':
|
||||
dev_info(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
case '7':
|
||||
dev_dbg(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
default:
|
||||
dev_info(CARD_TO_DEV(card), "HW: %.*s", len, str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* The substrncpy function copies the src string (which includes the
|
||||
* terminating '\0' character), up to the count into the dest pointer.
|
||||
* Returns the number of bytes copied to dest.
|
||||
*/
|
||||
static int substrncpy(char *dest, const char *src, int count)
|
||||
{
|
||||
int max_cnt = count;
|
||||
|
||||
while (count) {
|
||||
count--;
|
||||
*dest = *src;
|
||||
if (*dest == '\0')
|
||||
break;
|
||||
src++;
|
||||
dest++;
|
||||
}
|
||||
return max_cnt - count;
|
||||
}
|
||||
|
||||
|
||||
static void read_hw_log_done(struct rsxx_cardinfo *card,
|
||||
struct creg_cmd *cmd,
|
||||
int st)
|
||||
{
|
||||
char *buf;
|
||||
char *log_str;
|
||||
int cnt;
|
||||
int len;
|
||||
int off;
|
||||
|
||||
buf = cmd->buf;
|
||||
off = 0;
|
||||
|
||||
/* Failed getting the log message */
|
||||
if (st)
|
||||
return;
|
||||
|
||||
while (off < cmd->cnt8) {
|
||||
log_str = &card->log.buf[card->log.buf_len];
|
||||
cnt = min(cmd->cnt8 - off, LOG_BUF_SIZE8 - card->log.buf_len);
|
||||
len = substrncpy(log_str, &buf[off], cnt);
|
||||
|
||||
off += len;
|
||||
card->log.buf_len += len;
|
||||
|
||||
/*
|
||||
* Flush the log if we've hit the end of a message or if we've
|
||||
* run out of buffer space.
|
||||
*/
|
||||
if ((log_str[len - 1] == '\0') ||
|
||||
(card->log.buf_len == LOG_BUF_SIZE8)) {
|
||||
if (card->log.buf_len != 1) /* Don't log blank lines. */
|
||||
hw_log_msg(card, card->log.buf,
|
||||
card->log.buf_len);
|
||||
card->log.buf_len = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (cmd->status & CREG_STAT_LOG_PENDING)
|
||||
rsxx_read_hw_log(card);
|
||||
}
|
||||
|
||||
int rsxx_read_hw_log(struct rsxx_cardinfo *card)
|
||||
{
|
||||
int st;
|
||||
|
||||
st = creg_queue_cmd(card, CREG_OP_READ, CREG_ADD_LOG,
|
||||
sizeof(card->log.tmp), card->log.tmp,
|
||||
1, read_hw_log_done, NULL);
|
||||
if (st)
|
||||
dev_err(CARD_TO_DEV(card),
|
||||
"Failed getting log text\n");
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
/*-------------- IOCTL REG Access ------------------*/
|
||||
static int issue_reg_cmd(struct rsxx_cardinfo *card,
|
||||
struct rsxx_reg_access *cmd,
|
||||
int read)
|
||||
{
|
||||
unsigned int op = read ? CREG_OP_READ : CREG_OP_WRITE;
|
||||
|
||||
return __issue_creg_rw(card, op, cmd->addr, cmd->cnt, cmd->data,
|
||||
cmd->stream, &cmd->stat);
|
||||
}
|
||||
|
||||
int rsxx_reg_access(struct rsxx_cardinfo *card,
|
||||
struct rsxx_reg_access __user *ucmd,
|
||||
int read)
|
||||
{
|
||||
struct rsxx_reg_access cmd;
|
||||
int st;
|
||||
|
||||
st = copy_from_user(&cmd, ucmd, sizeof(cmd));
|
||||
if (st)
|
||||
return -EFAULT;
|
||||
|
||||
if (cmd.cnt > RSXX_MAX_REG_CNT)
|
||||
return -EFAULT;
|
||||
|
||||
st = issue_reg_cmd(card, &cmd, read);
|
||||
if (st)
|
||||
return st;
|
||||
|
||||
st = put_user(cmd.stat, &ucmd->stat);
|
||||
if (st)
|
||||
return -EFAULT;
|
||||
|
||||
if (read) {
|
||||
st = copy_to_user(ucmd->data, cmd.data, cmd.cnt);
|
||||
if (st)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsxx_eeh_save_issued_creg(struct rsxx_cardinfo *card)
|
||||
{
|
||||
struct creg_cmd *cmd = NULL;
|
||||
|
||||
cmd = card->creg_ctrl.active_cmd;
|
||||
card->creg_ctrl.active_cmd = NULL;
|
||||
|
||||
if (cmd) {
|
||||
del_timer_sync(&card->creg_ctrl.cmd_timer);
|
||||
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
list_add(&cmd->list, &card->creg_ctrl.queue);
|
||||
card->creg_ctrl.q_depth++;
|
||||
card->creg_ctrl.active = 0;
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
}
|
||||
}
|
||||
|
||||
void rsxx_kick_creg_queue(struct rsxx_cardinfo *card)
|
||||
{
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
if (!list_empty(&card->creg_ctrl.queue))
|
||||
creg_kick_queue(card);
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
}
|
||||
|
||||
/*------------ Initialization & Setup --------------*/
|
||||
int rsxx_creg_setup(struct rsxx_cardinfo *card)
|
||||
{
|
||||
card->creg_ctrl.active_cmd = NULL;
|
||||
|
||||
card->creg_ctrl.creg_wq =
|
||||
create_singlethread_workqueue(DRIVER_NAME"_creg");
|
||||
if (!card->creg_ctrl.creg_wq)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_WORK(&card->creg_ctrl.done_work, creg_cmd_done);
|
||||
mutex_init(&card->creg_ctrl.reset_lock);
|
||||
INIT_LIST_HEAD(&card->creg_ctrl.queue);
|
||||
spin_lock_init(&card->creg_ctrl.lock);
|
||||
setup_timer(&card->creg_ctrl.cmd_timer, creg_cmd_timed_out,
|
||||
(unsigned long) card);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsxx_creg_destroy(struct rsxx_cardinfo *card)
|
||||
{
|
||||
struct creg_cmd *cmd;
|
||||
struct creg_cmd *tmp;
|
||||
int cnt = 0;
|
||||
|
||||
/* Cancel outstanding commands */
|
||||
spin_lock_bh(&card->creg_ctrl.lock);
|
||||
list_for_each_entry_safe(cmd, tmp, &card->creg_ctrl.queue, list) {
|
||||
list_del(&cmd->list);
|
||||
if (cmd->cb)
|
||||
cmd->cb(card, cmd, -ECANCELED);
|
||||
kmem_cache_free(creg_cmd_pool, cmd);
|
||||
cnt++;
|
||||
}
|
||||
|
||||
if (cnt)
|
||||
dev_info(CARD_TO_DEV(card),
|
||||
"Canceled %d queue creg commands\n", cnt);
|
||||
|
||||
cmd = card->creg_ctrl.active_cmd;
|
||||
card->creg_ctrl.active_cmd = NULL;
|
||||
if (cmd) {
|
||||
if (timer_pending(&card->creg_ctrl.cmd_timer))
|
||||
del_timer_sync(&card->creg_ctrl.cmd_timer);
|
||||
|
||||
if (cmd->cb)
|
||||
cmd->cb(card, cmd, -ECANCELED);
|
||||
dev_info(CARD_TO_DEV(card),
|
||||
"Canceled active creg command\n");
|
||||
kmem_cache_free(creg_cmd_pool, cmd);
|
||||
}
|
||||
spin_unlock_bh(&card->creg_ctrl.lock);
|
||||
|
||||
cancel_work_sync(&card->creg_ctrl.done_work);
|
||||
}
|
||||
|
||||
|
||||
int rsxx_creg_init(void)
|
||||
{
|
||||
creg_cmd_pool = KMEM_CACHE(creg_cmd, SLAB_HWCACHE_ALIGN);
|
||||
if (!creg_cmd_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsxx_creg_cleanup(void)
|
||||
{
|
||||
kmem_cache_destroy(creg_cmd_pool);
|
||||
}
|
361
drivers/block/rsxx/dev.c
Normal file
361
drivers/block/rsxx/dev.c
Normal file
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* Filename: dev.c
|
||||
*
|
||||
*
|
||||
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
|
||||
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2013 IBM 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/hdreg.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/bio.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "rsxx_priv.h"
|
||||
|
||||
static unsigned int blkdev_minors = 64;
|
||||
module_param(blkdev_minors, uint, 0444);
|
||||
MODULE_PARM_DESC(blkdev_minors, "Number of minors(partitions)");
|
||||
|
||||
/*
|
||||
* For now I'm making this tweakable in case any applications hit this limit.
|
||||
* If you see a "bio too big" error in the log you will need to raise this
|
||||
* value.
|
||||
*/
|
||||
static unsigned int blkdev_max_hw_sectors = 1024;
|
||||
module_param(blkdev_max_hw_sectors, uint, 0444);
|
||||
MODULE_PARM_DESC(blkdev_max_hw_sectors, "Max hw sectors for a single BIO");
|
||||
|
||||
static unsigned int enable_blkdev = 1;
|
||||
module_param(enable_blkdev , uint, 0444);
|
||||
MODULE_PARM_DESC(enable_blkdev, "Enable block device interfaces");
|
||||
|
||||
|
||||
struct rsxx_bio_meta {
|
||||
struct bio *bio;
|
||||
atomic_t pending_dmas;
|
||||
atomic_t error;
|
||||
unsigned long start_time;
|
||||
};
|
||||
|
||||
static struct kmem_cache *bio_meta_pool;
|
||||
|
||||
/*----------------- Block Device Operations -----------------*/
|
||||
static int rsxx_blkdev_ioctl(struct block_device *bdev,
|
||||
fmode_t mode,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct rsxx_cardinfo *card = bdev->bd_disk->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case RSXX_GETREG:
|
||||
return rsxx_reg_access(card, (void __user *)arg, 1);
|
||||
case RSXX_SETREG:
|
||||
return rsxx_reg_access(card, (void __user *)arg, 0);
|
||||
}
|
||||
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static int rsxx_getgeo(struct block_device *bdev, struct hd_geometry *geo)
|
||||
{
|
||||
struct rsxx_cardinfo *card = bdev->bd_disk->private_data;
|
||||
u64 blocks = card->size8 >> 9;
|
||||
|
||||
/*
|
||||
* get geometry: Fake it. I haven't found any drivers that set
|
||||
* geo->start, so we won't either.
|
||||
*/
|
||||
if (card->size8) {
|
||||
geo->heads = 64;
|
||||
geo->sectors = 16;
|
||||
do_div(blocks, (geo->heads * geo->sectors));
|
||||
geo->cylinders = blocks;
|
||||
} else {
|
||||
geo->heads = 0;
|
||||
geo->sectors = 0;
|
||||
geo->cylinders = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct block_device_operations rsxx_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.getgeo = rsxx_getgeo,
|
||||
.ioctl = rsxx_blkdev_ioctl,
|
||||
};
|
||||
|
||||
static void disk_stats_start(struct rsxx_cardinfo *card, struct bio *bio)
|
||||
{
|
||||
struct hd_struct *part0 = &card->gendisk->part0;
|
||||
int rw = bio_data_dir(bio);
|
||||
int cpu;
|
||||
|
||||
cpu = part_stat_lock();
|
||||
|
||||
part_round_stats(cpu, part0);
|
||||
part_inc_in_flight(part0, rw);
|
||||
|
||||
part_stat_unlock();
|
||||
}
|
||||
|
||||
static void disk_stats_complete(struct rsxx_cardinfo *card,
|
||||
struct bio *bio,
|
||||
unsigned long start_time)
|
||||
{
|
||||
struct hd_struct *part0 = &card->gendisk->part0;
|
||||
unsigned long duration = jiffies - start_time;
|
||||
int rw = bio_data_dir(bio);
|
||||
int cpu;
|
||||
|
||||
cpu = part_stat_lock();
|
||||
|
||||
part_stat_add(cpu, part0, sectors[rw], bio_sectors(bio));
|
||||
part_stat_inc(cpu, part0, ios[rw]);
|
||||
part_stat_add(cpu, part0, ticks[rw], duration);
|
||||
|
||||
part_round_stats(cpu, part0);
|
||||
part_dec_in_flight(part0, rw);
|
||||
|
||||
part_stat_unlock();
|
||||
}
|
||||
|
||||
static void bio_dma_done_cb(struct rsxx_cardinfo *card,
|
||||
void *cb_data,
|
||||
unsigned int error)
|
||||
{
|
||||
struct rsxx_bio_meta *meta = cb_data;
|
||||
|
||||
if (error)
|
||||
atomic_set(&meta->error, 1);
|
||||
|
||||
if (atomic_dec_and_test(&meta->pending_dmas)) {
|
||||
if (!card->eeh_state && card->gendisk)
|
||||
disk_stats_complete(card, meta->bio, meta->start_time);
|
||||
|
||||
bio_endio(meta->bio, atomic_read(&meta->error) ? -EIO : 0);
|
||||
kmem_cache_free(bio_meta_pool, meta);
|
||||
}
|
||||
}
|
||||
|
||||
static void rsxx_make_request(struct request_queue *q, struct bio *bio)
|
||||
{
|
||||
struct rsxx_cardinfo *card = q->queuedata;
|
||||
struct rsxx_bio_meta *bio_meta;
|
||||
int st = -EINVAL;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!card)
|
||||
goto req_err;
|
||||
|
||||
if (bio_end_sector(bio) > get_capacity(card->gendisk))
|
||||
goto req_err;
|
||||
|
||||
if (unlikely(card->halt)) {
|
||||
st = -EFAULT;
|
||||
goto req_err;
|
||||
}
|
||||
|
||||
if (unlikely(card->dma_fault)) {
|
||||
st = (-EFAULT);
|
||||
goto req_err;
|
||||
}
|
||||
|
||||
if (bio->bi_iter.bi_size == 0) {
|
||||
dev_err(CARD_TO_DEV(card), "size zero BIO!\n");
|
||||
goto req_err;
|
||||
}
|
||||
|
||||
bio_meta = kmem_cache_alloc(bio_meta_pool, GFP_KERNEL);
|
||||
if (!bio_meta) {
|
||||
st = -ENOMEM;
|
||||
goto req_err;
|
||||
}
|
||||
|
||||
bio_meta->bio = bio;
|
||||
atomic_set(&bio_meta->error, 0);
|
||||
atomic_set(&bio_meta->pending_dmas, 0);
|
||||
bio_meta->start_time = jiffies;
|
||||
|
||||
if (!unlikely(card->halt))
|
||||
disk_stats_start(card, bio);
|
||||
|
||||
dev_dbg(CARD_TO_DEV(card), "BIO[%c]: meta: %p addr8: x%llx size: %d\n",
|
||||
bio_data_dir(bio) ? 'W' : 'R', bio_meta,
|
||||
(u64)bio->bi_iter.bi_sector << 9, bio->bi_iter.bi_size);
|
||||
|
||||
st = rsxx_dma_queue_bio(card, bio, &bio_meta->pending_dmas,
|
||||
bio_dma_done_cb, bio_meta);
|
||||
if (st)
|
||||
goto queue_err;
|
||||
|
||||
return;
|
||||
|
||||
queue_err:
|
||||
kmem_cache_free(bio_meta_pool, bio_meta);
|
||||
req_err:
|
||||
bio_endio(bio, st);
|
||||
}
|
||||
|
||||
/*----------------- Device Setup -------------------*/
|
||||
static bool rsxx_discard_supported(struct rsxx_cardinfo *card)
|
||||
{
|
||||
unsigned char pci_rev;
|
||||
|
||||
pci_read_config_byte(card->dev, PCI_REVISION_ID, &pci_rev);
|
||||
|
||||
return (pci_rev >= RSXX_DISCARD_SUPPORT);
|
||||
}
|
||||
|
||||
int rsxx_attach_dev(struct rsxx_cardinfo *card)
|
||||
{
|
||||
mutex_lock(&card->dev_lock);
|
||||
|
||||
/* The block device requires the stripe size from the config. */
|
||||
if (enable_blkdev) {
|
||||
if (card->config_valid)
|
||||
set_capacity(card->gendisk, card->size8 >> 9);
|
||||
else
|
||||
set_capacity(card->gendisk, 0);
|
||||
add_disk(card->gendisk);
|
||||
|
||||
card->bdev_attached = 1;
|
||||
}
|
||||
|
||||
mutex_unlock(&card->dev_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsxx_detach_dev(struct rsxx_cardinfo *card)
|
||||
{
|
||||
mutex_lock(&card->dev_lock);
|
||||
|
||||
if (card->bdev_attached) {
|
||||
del_gendisk(card->gendisk);
|
||||
card->bdev_attached = 0;
|
||||
}
|
||||
|
||||
mutex_unlock(&card->dev_lock);
|
||||
}
|
||||
|
||||
int rsxx_setup_dev(struct rsxx_cardinfo *card)
|
||||
{
|
||||
unsigned short blk_size;
|
||||
|
||||
mutex_init(&card->dev_lock);
|
||||
|
||||
if (!enable_blkdev)
|
||||
return 0;
|
||||
|
||||
card->major = register_blkdev(0, DRIVER_NAME);
|
||||
if (card->major < 0) {
|
||||
dev_err(CARD_TO_DEV(card), "Failed to get major number\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->queue = blk_alloc_queue(GFP_KERNEL);
|
||||
if (!card->queue) {
|
||||
dev_err(CARD_TO_DEV(card), "Failed queue alloc\n");
|
||||
unregister_blkdev(card->major, DRIVER_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
card->gendisk = alloc_disk(blkdev_minors);
|
||||
if (!card->gendisk) {
|
||||
dev_err(CARD_TO_DEV(card), "Failed disk alloc\n");
|
||||
blk_cleanup_queue(card->queue);
|
||||
unregister_blkdev(card->major, DRIVER_NAME);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (card->config_valid) {
|
||||
blk_size = card->config.data.block_size;
|
||||
blk_queue_dma_alignment(card->queue, blk_size - 1);
|
||||
blk_queue_logical_block_size(card->queue, blk_size);
|
||||
}
|
||||
|
||||
blk_queue_make_request(card->queue, rsxx_make_request);
|
||||
blk_queue_bounce_limit(card->queue, BLK_BOUNCE_ANY);
|
||||
blk_queue_max_hw_sectors(card->queue, blkdev_max_hw_sectors);
|
||||
blk_queue_physical_block_size(card->queue, RSXX_HW_BLK_SIZE);
|
||||
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, card->queue);
|
||||
queue_flag_clear_unlocked(QUEUE_FLAG_ADD_RANDOM, card->queue);
|
||||
if (rsxx_discard_supported(card)) {
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, card->queue);
|
||||
blk_queue_max_discard_sectors(card->queue,
|
||||
RSXX_HW_BLK_SIZE >> 9);
|
||||
card->queue->limits.discard_granularity = RSXX_HW_BLK_SIZE;
|
||||
card->queue->limits.discard_alignment = RSXX_HW_BLK_SIZE;
|
||||
card->queue->limits.discard_zeroes_data = 1;
|
||||
}
|
||||
|
||||
card->queue->queuedata = card;
|
||||
|
||||
snprintf(card->gendisk->disk_name, sizeof(card->gendisk->disk_name),
|
||||
"rsxx%d", card->disk_id);
|
||||
card->gendisk->driverfs_dev = &card->dev->dev;
|
||||
card->gendisk->major = card->major;
|
||||
card->gendisk->first_minor = 0;
|
||||
card->gendisk->fops = &rsxx_fops;
|
||||
card->gendisk->private_data = card;
|
||||
card->gendisk->queue = card->queue;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsxx_destroy_dev(struct rsxx_cardinfo *card)
|
||||
{
|
||||
if (!enable_blkdev)
|
||||
return;
|
||||
|
||||
put_disk(card->gendisk);
|
||||
card->gendisk = NULL;
|
||||
|
||||
blk_cleanup_queue(card->queue);
|
||||
card->queue->queuedata = NULL;
|
||||
unregister_blkdev(card->major, DRIVER_NAME);
|
||||
}
|
||||
|
||||
int rsxx_dev_init(void)
|
||||
{
|
||||
bio_meta_pool = KMEM_CACHE(rsxx_bio_meta, SLAB_HWCACHE_ALIGN);
|
||||
if (!bio_meta_pool)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void rsxx_dev_cleanup(void)
|
||||
{
|
||||
kmem_cache_destroy(bio_meta_pool);
|
||||
}
|
||||
|
||||
|
1104
drivers/block/rsxx/dma.c
Normal file
1104
drivers/block/rsxx/dma.c
Normal file
File diff suppressed because it is too large
Load diff
47
drivers/block/rsxx/rsxx.h
Normal file
47
drivers/block/rsxx/rsxx.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Filename: rsxx.h
|
||||
*
|
||||
*
|
||||
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
|
||||
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2013 IBM 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __RSXX_H__
|
||||
#define __RSXX_H__
|
||||
|
||||
/*----------------- IOCTL Definitions -------------------*/
|
||||
|
||||
#define RSXX_MAX_DATA 8
|
||||
|
||||
struct rsxx_reg_access {
|
||||
__u32 addr;
|
||||
__u32 cnt;
|
||||
__u32 stat;
|
||||
__u32 stream;
|
||||
__u32 data[RSXX_MAX_DATA];
|
||||
};
|
||||
|
||||
#define RSXX_MAX_REG_CNT (RSXX_MAX_DATA * (sizeof(__u32)))
|
||||
|
||||
#define RSXX_IOC_MAGIC 'r'
|
||||
|
||||
#define RSXX_GETREG _IOWR(RSXX_IOC_MAGIC, 0x20, struct rsxx_reg_access)
|
||||
#define RSXX_SETREG _IOWR(RSXX_IOC_MAGIC, 0x21, struct rsxx_reg_access)
|
||||
|
||||
#endif /* __RSXX_H_ */
|
72
drivers/block/rsxx/rsxx_cfg.h
Normal file
72
drivers/block/rsxx/rsxx_cfg.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Filename: rsXX_cfg.h
|
||||
*
|
||||
*
|
||||
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
|
||||
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2013 IBM 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __RSXX_CFG_H__
|
||||
#define __RSXX_CFG_H__
|
||||
|
||||
/* NOTE: Config values will be saved in network byte order (i.e. Big endian) */
|
||||
#include <linux/types.h>
|
||||
|
||||
/*
|
||||
* The card config version must match the driver's expected version. If it does
|
||||
* not, the DMA interfaces will not be attached and the user will need to
|
||||
* initialize/upgrade the card configuration using the card config utility.
|
||||
*/
|
||||
#define RSXX_CFG_VERSION 4
|
||||
|
||||
struct card_cfg_hdr {
|
||||
__u32 version;
|
||||
__u32 crc;
|
||||
};
|
||||
|
||||
struct card_cfg_data {
|
||||
__u32 block_size;
|
||||
__u32 stripe_size;
|
||||
__u32 vendor_id;
|
||||
__u32 cache_order;
|
||||
struct {
|
||||
__u32 mode; /* Disabled, manual, auto-tune... */
|
||||
__u32 count; /* Number of intr to coalesce */
|
||||
__u32 latency;/* Max wait time (in ns) */
|
||||
} intr_coal;
|
||||
};
|
||||
|
||||
struct rsxx_card_cfg {
|
||||
struct card_cfg_hdr hdr;
|
||||
struct card_cfg_data data;
|
||||
};
|
||||
|
||||
/* Vendor ID Values */
|
||||
#define RSXX_VENDOR_ID_IBM 0
|
||||
#define RSXX_VENDOR_ID_DSI 1
|
||||
#define RSXX_VENDOR_COUNT 2
|
||||
|
||||
/* Interrupt Coalescing Values */
|
||||
#define RSXX_INTR_COAL_DISABLED 0
|
||||
#define RSXX_INTR_COAL_EXPLICIT 1
|
||||
#define RSXX_INTR_COAL_AUTO_TUNE 2
|
||||
|
||||
|
||||
#endif /* __RSXX_CFG_H__ */
|
||||
|
434
drivers/block/rsxx/rsxx_priv.h
Normal file
434
drivers/block/rsxx/rsxx_priv.h
Normal file
|
@ -0,0 +1,434 @@
|
|||
/*
|
||||
* Filename: rsxx_priv.h
|
||||
*
|
||||
*
|
||||
* Authors: Joshua Morris <josh.h.morris@us.ibm.com>
|
||||
* Philip Kelleher <pjk1939@linux.vnet.ibm.com>
|
||||
*
|
||||
* (C) Copyright 2013 IBM 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef __RSXX_PRIV_H__
|
||||
#define __RSXX_PRIV_H__
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/semaphore.h>
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/sysfs.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/bio.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#include "rsxx.h"
|
||||
#include "rsxx_cfg.h"
|
||||
|
||||
struct proc_cmd;
|
||||
|
||||
#define PCI_DEVICE_ID_FS70_FLASH 0x04A9
|
||||
#define PCI_DEVICE_ID_FS80_FLASH 0x04AA
|
||||
|
||||
#define RS70_PCI_REV_SUPPORTED 4
|
||||
|
||||
#define DRIVER_NAME "rsxx"
|
||||
#define DRIVER_VERSION "4.0.3.2516"
|
||||
|
||||
/* Block size is 4096 */
|
||||
#define RSXX_HW_BLK_SHIFT 12
|
||||
#define RSXX_HW_BLK_SIZE (1 << RSXX_HW_BLK_SHIFT)
|
||||
#define RSXX_HW_BLK_MASK (RSXX_HW_BLK_SIZE - 1)
|
||||
|
||||
#define MAX_CREG_DATA8 32
|
||||
#define LOG_BUF_SIZE8 128
|
||||
|
||||
#define RSXX_MAX_OUTSTANDING_CMDS 255
|
||||
#define RSXX_CS_IDX_MASK 0xff
|
||||
|
||||
#define STATUS_BUFFER_SIZE8 4096
|
||||
#define COMMAND_BUFFER_SIZE8 4096
|
||||
|
||||
#define RSXX_MAX_TARGETS 8
|
||||
|
||||
struct dma_tracker_list;
|
||||
|
||||
/* DMA Command/Status Buffer structure */
|
||||
struct rsxx_cs_buffer {
|
||||
dma_addr_t dma_addr;
|
||||
void *buf;
|
||||
u32 idx;
|
||||
};
|
||||
|
||||
struct rsxx_dma_stats {
|
||||
u32 crc_errors;
|
||||
u32 hard_errors;
|
||||
u32 soft_errors;
|
||||
u32 writes_issued;
|
||||
u32 writes_failed;
|
||||
u32 reads_issued;
|
||||
u32 reads_failed;
|
||||
u32 reads_retried;
|
||||
u32 discards_issued;
|
||||
u32 discards_failed;
|
||||
u32 done_rescheduled;
|
||||
u32 issue_rescheduled;
|
||||
u32 dma_sw_err;
|
||||
u32 dma_hw_fault;
|
||||
u32 dma_cancelled;
|
||||
u32 sw_q_depth; /* Number of DMAs on the SW queue. */
|
||||
atomic_t hw_q_depth; /* Number of DMAs queued to HW. */
|
||||
};
|
||||
|
||||
struct rsxx_dma_ctrl {
|
||||
struct rsxx_cardinfo *card;
|
||||
int id;
|
||||
void __iomem *regmap;
|
||||
struct rsxx_cs_buffer status;
|
||||
struct rsxx_cs_buffer cmd;
|
||||
u16 e_cnt;
|
||||
spinlock_t queue_lock;
|
||||
struct list_head queue;
|
||||
struct workqueue_struct *issue_wq;
|
||||
struct work_struct issue_dma_work;
|
||||
struct workqueue_struct *done_wq;
|
||||
struct work_struct dma_done_work;
|
||||
struct timer_list activity_timer;
|
||||
struct dma_tracker_list *trackers;
|
||||
struct rsxx_dma_stats stats;
|
||||
struct mutex work_lock;
|
||||
};
|
||||
|
||||
struct rsxx_cardinfo {
|
||||
struct pci_dev *dev;
|
||||
unsigned int halt;
|
||||
unsigned int eeh_state;
|
||||
|
||||
void __iomem *regmap;
|
||||
spinlock_t irq_lock;
|
||||
unsigned int isr_mask;
|
||||
unsigned int ier_mask;
|
||||
|
||||
struct rsxx_card_cfg config;
|
||||
int config_valid;
|
||||
|
||||
/* Embedded CPU Communication */
|
||||
struct {
|
||||
spinlock_t lock;
|
||||
bool active;
|
||||
struct creg_cmd *active_cmd;
|
||||
struct workqueue_struct *creg_wq;
|
||||
struct work_struct done_work;
|
||||
struct list_head queue;
|
||||
unsigned int q_depth;
|
||||
/* Cache the creg status to prevent ioreads */
|
||||
struct {
|
||||
u32 stat;
|
||||
u32 failed_cancel_timer;
|
||||
u32 creg_timeout;
|
||||
} creg_stats;
|
||||
struct timer_list cmd_timer;
|
||||
struct mutex reset_lock;
|
||||
int reset;
|
||||
} creg_ctrl;
|
||||
|
||||
struct {
|
||||
char tmp[MAX_CREG_DATA8];
|
||||
char buf[LOG_BUF_SIZE8]; /* terminated */
|
||||
int buf_len;
|
||||
} log;
|
||||
|
||||
struct workqueue_struct *event_wq;
|
||||
struct work_struct event_work;
|
||||
unsigned int state;
|
||||
u64 size8;
|
||||
|
||||
/* Lock the device attach/detach function */
|
||||
struct mutex dev_lock;
|
||||
|
||||
/* Block Device Variables */
|
||||
bool bdev_attached;
|
||||
int disk_id;
|
||||
int major;
|
||||
struct request_queue *queue;
|
||||
struct gendisk *gendisk;
|
||||
struct {
|
||||
/* Used to convert a byte address to a device address. */
|
||||
u64 lower_mask;
|
||||
u64 upper_shift;
|
||||
u64 upper_mask;
|
||||
u64 target_mask;
|
||||
u64 target_shift;
|
||||
} _stripe;
|
||||
unsigned int dma_fault;
|
||||
|
||||
int scrub_hard;
|
||||
|
||||
int n_targets;
|
||||
struct rsxx_dma_ctrl *ctrl;
|
||||
|
||||
struct dentry *debugfs_dir;
|
||||
};
|
||||
|
||||
enum rsxx_pci_regmap {
|
||||
HWID = 0x00, /* Hardware Identification Register */
|
||||
SCRATCH = 0x04, /* Scratch/Debug Register */
|
||||
RESET = 0x08, /* Reset Register */
|
||||
ISR = 0x10, /* Interrupt Status Register */
|
||||
IER = 0x14, /* Interrupt Enable Register */
|
||||
IPR = 0x18, /* Interrupt Poll Register */
|
||||
CB_ADD_LO = 0x20, /* Command Host Buffer Address [31:0] */
|
||||
CB_ADD_HI = 0x24, /* Command Host Buffer Address [63:32]*/
|
||||
HW_CMD_IDX = 0x28, /* Hardware Processed Command Index */
|
||||
SW_CMD_IDX = 0x2C, /* Software Processed Command Index */
|
||||
SB_ADD_LO = 0x30, /* Status Host Buffer Address [31:0] */
|
||||
SB_ADD_HI = 0x34, /* Status Host Buffer Address [63:32] */
|
||||
HW_STATUS_CNT = 0x38, /* Hardware Status Counter */
|
||||
SW_STATUS_CNT = 0x3C, /* Deprecated */
|
||||
CREG_CMD = 0x40, /* CPU Command Register */
|
||||
CREG_ADD = 0x44, /* CPU Address Register */
|
||||
CREG_CNT = 0x48, /* CPU Count Register */
|
||||
CREG_STAT = 0x4C, /* CPU Status Register */
|
||||
CREG_DATA0 = 0x50, /* CPU Data Registers */
|
||||
CREG_DATA1 = 0x54,
|
||||
CREG_DATA2 = 0x58,
|
||||
CREG_DATA3 = 0x5C,
|
||||
CREG_DATA4 = 0x60,
|
||||
CREG_DATA5 = 0x64,
|
||||
CREG_DATA6 = 0x68,
|
||||
CREG_DATA7 = 0x6c,
|
||||
INTR_COAL = 0x70, /* Interrupt Coalescing Register */
|
||||
HW_ERROR = 0x74, /* Card Error Register */
|
||||
PCI_DEBUG0 = 0x78, /* PCI Debug Registers */
|
||||
PCI_DEBUG1 = 0x7C,
|
||||
PCI_DEBUG2 = 0x80,
|
||||
PCI_DEBUG3 = 0x84,
|
||||
PCI_DEBUG4 = 0x88,
|
||||
PCI_DEBUG5 = 0x8C,
|
||||
PCI_DEBUG6 = 0x90,
|
||||
PCI_DEBUG7 = 0x94,
|
||||
PCI_POWER_THROTTLE = 0x98,
|
||||
PERF_CTRL = 0x9c,
|
||||
PERF_TIMER_LO = 0xa0,
|
||||
PERF_TIMER_HI = 0xa4,
|
||||
PERF_RD512_LO = 0xa8,
|
||||
PERF_RD512_HI = 0xac,
|
||||
PERF_WR512_LO = 0xb0,
|
||||
PERF_WR512_HI = 0xb4,
|
||||
PCI_RECONFIG = 0xb8,
|
||||
};
|
||||
|
||||
enum rsxx_intr {
|
||||
CR_INTR_DMA0 = 0x00000001,
|
||||
CR_INTR_CREG = 0x00000002,
|
||||
CR_INTR_DMA1 = 0x00000004,
|
||||
CR_INTR_EVENT = 0x00000008,
|
||||
CR_INTR_DMA2 = 0x00000010,
|
||||
CR_INTR_DMA3 = 0x00000020,
|
||||
CR_INTR_DMA4 = 0x00000040,
|
||||
CR_INTR_DMA5 = 0x00000080,
|
||||
CR_INTR_DMA6 = 0x00000100,
|
||||
CR_INTR_DMA7 = 0x00000200,
|
||||
CR_INTR_ALL_C = 0x0000003f,
|
||||
CR_INTR_ALL_G = 0x000003ff,
|
||||
CR_INTR_DMA_ALL = 0x000003f5,
|
||||
CR_INTR_ALL = 0xffffffff,
|
||||
};
|
||||
|
||||
static inline int CR_INTR_DMA(int N)
|
||||
{
|
||||
static const unsigned int _CR_INTR_DMA[] = {
|
||||
CR_INTR_DMA0, CR_INTR_DMA1, CR_INTR_DMA2, CR_INTR_DMA3,
|
||||
CR_INTR_DMA4, CR_INTR_DMA5, CR_INTR_DMA6, CR_INTR_DMA7
|
||||
};
|
||||
return _CR_INTR_DMA[N];
|
||||
}
|
||||
enum rsxx_pci_reset {
|
||||
DMA_QUEUE_RESET = 0x00000001,
|
||||
};
|
||||
|
||||
enum rsxx_hw_fifo_flush {
|
||||
RSXX_FLUSH_BUSY = 0x00000002,
|
||||
RSXX_FLUSH_TIMEOUT = 0x00000004,
|
||||
};
|
||||
|
||||
enum rsxx_pci_revision {
|
||||
RSXX_DISCARD_SUPPORT = 2,
|
||||
RSXX_EEH_SUPPORT = 3,
|
||||
};
|
||||
|
||||
enum rsxx_creg_cmd {
|
||||
CREG_CMD_TAG_MASK = 0x0000FF00,
|
||||
CREG_OP_WRITE = 0x000000C0,
|
||||
CREG_OP_READ = 0x000000E0,
|
||||
};
|
||||
|
||||
enum rsxx_creg_addr {
|
||||
CREG_ADD_CARD_CMD = 0x80001000,
|
||||
CREG_ADD_CARD_STATE = 0x80001004,
|
||||
CREG_ADD_CARD_SIZE = 0x8000100c,
|
||||
CREG_ADD_CAPABILITIES = 0x80001050,
|
||||
CREG_ADD_LOG = 0x80002000,
|
||||
CREG_ADD_NUM_TARGETS = 0x80003000,
|
||||
CREG_ADD_CRAM = 0xA0000000,
|
||||
CREG_ADD_CONFIG = 0xB0000000,
|
||||
};
|
||||
|
||||
enum rsxx_creg_card_cmd {
|
||||
CARD_CMD_STARTUP = 1,
|
||||
CARD_CMD_SHUTDOWN = 2,
|
||||
CARD_CMD_LOW_LEVEL_FORMAT = 3,
|
||||
CARD_CMD_FPGA_RECONFIG_BR = 4,
|
||||
CARD_CMD_FPGA_RECONFIG_MAIN = 5,
|
||||
CARD_CMD_BACKUP = 6,
|
||||
CARD_CMD_RESET = 7,
|
||||
CARD_CMD_deprecated = 8,
|
||||
CARD_CMD_UNINITIALIZE = 9,
|
||||
CARD_CMD_DSTROY_EMERGENCY = 10,
|
||||
CARD_CMD_DSTROY_NORMAL = 11,
|
||||
CARD_CMD_DSTROY_EXTENDED = 12,
|
||||
CARD_CMD_DSTROY_ABORT = 13,
|
||||
};
|
||||
|
||||
enum rsxx_card_state {
|
||||
CARD_STATE_SHUTDOWN = 0x00000001,
|
||||
CARD_STATE_STARTING = 0x00000002,
|
||||
CARD_STATE_FORMATTING = 0x00000004,
|
||||
CARD_STATE_UNINITIALIZED = 0x00000008,
|
||||
CARD_STATE_GOOD = 0x00000010,
|
||||
CARD_STATE_SHUTTING_DOWN = 0x00000020,
|
||||
CARD_STATE_FAULT = 0x00000040,
|
||||
CARD_STATE_RD_ONLY_FAULT = 0x00000080,
|
||||
CARD_STATE_DSTROYING = 0x00000100,
|
||||
};
|
||||
|
||||
enum rsxx_led {
|
||||
LED_DEFAULT = 0x0,
|
||||
LED_IDENTIFY = 0x1,
|
||||
LED_SOAK = 0x2,
|
||||
};
|
||||
|
||||
enum rsxx_creg_flash_lock {
|
||||
CREG_FLASH_LOCK = 1,
|
||||
CREG_FLASH_UNLOCK = 2,
|
||||
};
|
||||
|
||||
enum rsxx_card_capabilities {
|
||||
CARD_CAP_SUBPAGE_WRITES = 0x00000080,
|
||||
};
|
||||
|
||||
enum rsxx_creg_stat {
|
||||
CREG_STAT_STATUS_MASK = 0x00000003,
|
||||
CREG_STAT_SUCCESS = 0x1,
|
||||
CREG_STAT_ERROR = 0x2,
|
||||
CREG_STAT_CHAR_PENDING = 0x00000004, /* Character I/O pending bit */
|
||||
CREG_STAT_LOG_PENDING = 0x00000008, /* HW log message pending bit */
|
||||
CREG_STAT_TAG_MASK = 0x0000ff00,
|
||||
};
|
||||
|
||||
enum rsxx_dma_finish {
|
||||
FREE_DMA = 0x0,
|
||||
COMPLETE_DMA = 0x1,
|
||||
};
|
||||
|
||||
static inline unsigned int CREG_DATA(int N)
|
||||
{
|
||||
return CREG_DATA0 + (N << 2);
|
||||
}
|
||||
|
||||
/*----------------- Convenient Log Wrappers -------------------*/
|
||||
#define CARD_TO_DEV(__CARD) (&(__CARD)->dev->dev)
|
||||
|
||||
/***** config.c *****/
|
||||
int rsxx_load_config(struct rsxx_cardinfo *card);
|
||||
|
||||
/***** core.c *****/
|
||||
void rsxx_enable_ier(struct rsxx_cardinfo *card, unsigned int intr);
|
||||
void rsxx_disable_ier(struct rsxx_cardinfo *card, unsigned int intr);
|
||||
void rsxx_enable_ier_and_isr(struct rsxx_cardinfo *card,
|
||||
unsigned int intr);
|
||||
void rsxx_disable_ier_and_isr(struct rsxx_cardinfo *card,
|
||||
unsigned int intr);
|
||||
|
||||
/***** dev.c *****/
|
||||
int rsxx_attach_dev(struct rsxx_cardinfo *card);
|
||||
void rsxx_detach_dev(struct rsxx_cardinfo *card);
|
||||
int rsxx_setup_dev(struct rsxx_cardinfo *card);
|
||||
void rsxx_destroy_dev(struct rsxx_cardinfo *card);
|
||||
int rsxx_dev_init(void);
|
||||
void rsxx_dev_cleanup(void);
|
||||
|
||||
/***** dma.c ****/
|
||||
typedef void (*rsxx_dma_cb)(struct rsxx_cardinfo *card,
|
||||
void *cb_data,
|
||||
unsigned int status);
|
||||
int rsxx_dma_setup(struct rsxx_cardinfo *card);
|
||||
void rsxx_dma_destroy(struct rsxx_cardinfo *card);
|
||||
int rsxx_dma_init(void);
|
||||
int rsxx_cleanup_dma_queue(struct rsxx_dma_ctrl *ctrl,
|
||||
struct list_head *q,
|
||||
unsigned int done);
|
||||
int rsxx_dma_cancel(struct rsxx_dma_ctrl *ctrl);
|
||||
void rsxx_dma_cleanup(void);
|
||||
void rsxx_dma_queue_reset(struct rsxx_cardinfo *card);
|
||||
int rsxx_dma_configure(struct rsxx_cardinfo *card);
|
||||
int rsxx_dma_queue_bio(struct rsxx_cardinfo *card,
|
||||
struct bio *bio,
|
||||
atomic_t *n_dmas,
|
||||
rsxx_dma_cb cb,
|
||||
void *cb_data);
|
||||
int rsxx_hw_buffers_init(struct pci_dev *dev, struct rsxx_dma_ctrl *ctrl);
|
||||
int rsxx_eeh_save_issued_dmas(struct rsxx_cardinfo *card);
|
||||
int rsxx_eeh_remap_dmas(struct rsxx_cardinfo *card);
|
||||
|
||||
/***** cregs.c *****/
|
||||
int rsxx_creg_write(struct rsxx_cardinfo *card, u32 addr,
|
||||
unsigned int size8,
|
||||
void *data,
|
||||
int byte_stream);
|
||||
int rsxx_creg_read(struct rsxx_cardinfo *card,
|
||||
u32 addr,
|
||||
unsigned int size8,
|
||||
void *data,
|
||||
int byte_stream);
|
||||
int rsxx_read_hw_log(struct rsxx_cardinfo *card);
|
||||
int rsxx_get_card_state(struct rsxx_cardinfo *card,
|
||||
unsigned int *state);
|
||||
int rsxx_get_card_size8(struct rsxx_cardinfo *card, u64 *size8);
|
||||
int rsxx_get_num_targets(struct rsxx_cardinfo *card,
|
||||
unsigned int *n_targets);
|
||||
int rsxx_get_card_capabilities(struct rsxx_cardinfo *card,
|
||||
u32 *capabilities);
|
||||
int rsxx_issue_card_cmd(struct rsxx_cardinfo *card, u32 cmd);
|
||||
int rsxx_creg_setup(struct rsxx_cardinfo *card);
|
||||
void rsxx_creg_destroy(struct rsxx_cardinfo *card);
|
||||
int rsxx_creg_init(void);
|
||||
void rsxx_creg_cleanup(void);
|
||||
int rsxx_reg_access(struct rsxx_cardinfo *card,
|
||||
struct rsxx_reg_access __user *ucmd,
|
||||
int read);
|
||||
void rsxx_eeh_save_issued_creg(struct rsxx_cardinfo *card);
|
||||
void rsxx_kick_creg_queue(struct rsxx_cardinfo *card);
|
||||
|
||||
|
||||
|
||||
#endif /* __DRIVERS_BLOCK_RSXX_H__ */
|
Loading…
Add table
Add a link
Reference in a new issue