linux/drivers/net/ethernet/qlogic/qed/qed_init_ops.c
Yuval Mintz fe56b9e6a8 qed: Add module with basic common support
The Qlogic Everest Driver is the backend module for the QL4xxx ethernet
products by Qlogic.

This module serves two main purposes:
 1. It's responsible to contain all the common code that will be shared
    between the various drivers that would be used with said line of
    products. Flows such as chip initialization and de-initialization
    fall under this category.

 2. It would abstract the protocol-specific HW & FW components, allowing
    the protocol drivers to have a clean APIs which is detached in its
    slowpath configuration from the actual HSI.

This adds a very basic module without any protocol-specific bits.
I.e., this adds a basic implementation that almost entirely falls under
the first category.

Signed-off-by: Yuval Mintz <Yuval.Mintz@qlogic.com>
Signed-off-by: Ariel Elior <Ariel.Elior@qlogic.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2015-10-27 19:34:42 -07:00

532 lines
13 KiB
C

/* QLogic qed NIC Driver
* Copyright (c) 2015 QLogic Corporation
*
* This software is available under the terms of the GNU General Public License
* (GPL) Version 2, available from the file COPYING in the main directory of
* this source tree.
*/
#include <linux/types.h>
#include <linux/io.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/string.h>
#include "qed.h"
#include "qed_hsi.h"
#include "qed_hw.h"
#include "qed_init_ops.h"
#include "qed_reg_addr.h"
#define QED_INIT_MAX_POLL_COUNT 100
#define QED_INIT_POLL_PERIOD_US 500
static u32 pxp_global_win[] = {
0,
0,
0x1c02, /* win 2: addr=0x1c02000, size=4096 bytes */
0x1c80, /* win 3: addr=0x1c80000, size=4096 bytes */
0x1d00, /* win 4: addr=0x1d00000, size=4096 bytes */
0x1d01, /* win 5: addr=0x1d01000, size=4096 bytes */
0x1d80, /* win 6: addr=0x1d80000, size=4096 bytes */
0x1d81, /* win 7: addr=0x1d81000, size=4096 bytes */
0x1d82, /* win 8: addr=0x1d82000, size=4096 bytes */
0x1e00, /* win 9: addr=0x1e00000, size=4096 bytes */
0x1e80, /* win 10: addr=0x1e80000, size=4096 bytes */
0x1f00, /* win 11: addr=0x1f00000, size=4096 bytes */
0,
0,
0,
0,
0,
0,
0,
};
void qed_init_iro_array(struct qed_dev *cdev)
{
cdev->iro_arr = iro_arr;
}
/* Runtime configuration helpers */
void qed_init_clear_rt_data(struct qed_hwfn *p_hwfn)
{
int i;
for (i = 0; i < RUNTIME_ARRAY_SIZE; i++)
p_hwfn->rt_data[i].b_valid = false;
}
void qed_init_store_rt_reg(struct qed_hwfn *p_hwfn,
u32 rt_offset,
u32 val)
{
p_hwfn->rt_data[rt_offset].init_val = val;
p_hwfn->rt_data[rt_offset].b_valid = true;
}
void qed_init_store_rt_agg(struct qed_hwfn *p_hwfn,
u32 rt_offset,
u32 *val,
size_t size)
{
size_t i;
for (i = 0; i < size / sizeof(u32); i++) {
p_hwfn->rt_data[rt_offset + i].init_val = val[i];
p_hwfn->rt_data[rt_offset + i].b_valid = true;
}
}
static void qed_init_rt(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr,
u32 rt_offset,
u32 size)
{
struct qed_rt_data *rt_data = p_hwfn->rt_data + rt_offset;
u32 i;
for (i = 0; i < size; i++) {
if (!rt_data[i].b_valid)
continue;
qed_wr(p_hwfn, p_ptt, addr + (i << 2), rt_data[i].init_val);
}
}
int qed_init_alloc(struct qed_hwfn *p_hwfn)
{
struct qed_rt_data *rt_data;
rt_data = kzalloc(sizeof(*rt_data) * RUNTIME_ARRAY_SIZE, GFP_ATOMIC);
if (!rt_data)
return -ENOMEM;
p_hwfn->rt_data = rt_data;
return 0;
}
void qed_init_free(struct qed_hwfn *p_hwfn)
{
kfree(p_hwfn->rt_data);
p_hwfn->rt_data = NULL;
}
static int qed_init_array_dmae(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr,
u32 dmae_data_offset,
u32 size,
const u32 *buf,
bool b_must_dmae,
bool b_can_dmae)
{
int rc = 0;
/* Perform DMAE only for lengthy enough sections or for wide-bus */
if (!b_can_dmae || (!b_must_dmae && (size < 16))) {
const u32 *data = buf + dmae_data_offset;
u32 i;
for (i = 0; i < size; i++)
qed_wr(p_hwfn, p_ptt, addr + (i << 2), data[i]);
} else {
rc = qed_dmae_host2grc(p_hwfn, p_ptt,
(uintptr_t)(buf + dmae_data_offset),
addr, size, 0);
}
return rc;
}
static int qed_init_fill_dmae(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr,
u32 fill,
u32 fill_count)
{
static u32 zero_buffer[DMAE_MAX_RW_SIZE];
memset(zero_buffer, 0, sizeof(u32) * DMAE_MAX_RW_SIZE);
/* invoke the DMAE virtual/physical buffer API with
* 1. DMAE init channel
* 2. addr,
* 3. p_hwfb->temp_data,
* 4. fill_count
*/
return qed_dmae_host2grc(p_hwfn, p_ptt,
(uintptr_t)(&zero_buffer[0]),
addr, fill_count,
QED_DMAE_FLAG_RW_REPL_SRC);
}
static void qed_init_fill(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
u32 addr,
u32 fill,
u32 fill_count)
{
u32 i;
for (i = 0; i < fill_count; i++, addr += sizeof(u32))
qed_wr(p_hwfn, p_ptt, addr, fill);
}
static int qed_init_cmd_array(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_write_op *cmd,
bool b_must_dmae,
bool b_can_dmae)
{
u32 data = le32_to_cpu(cmd->data);
u32 addr = GET_FIELD(data, INIT_WRITE_OP_ADDRESS) << 2;
u32 dmae_array_offset = le32_to_cpu(cmd->args.array_offset);
u32 offset, output_len, input_len, max_size;
struct qed_dev *cdev = p_hwfn->cdev;
union init_array_hdr *hdr;
const u32 *array_data;
int rc = 0;
u32 size;
array_data = cdev->fw_data->arr_data;
hdr = (union init_array_hdr *)(array_data +
dmae_array_offset);
data = le32_to_cpu(hdr->raw.data);
switch (GET_FIELD(data, INIT_ARRAY_RAW_HDR_TYPE)) {
case INIT_ARR_ZIPPED:
offset = dmae_array_offset + 1;
input_len = GET_FIELD(data,
INIT_ARRAY_ZIPPED_HDR_ZIPPED_SIZE);
max_size = MAX_ZIPPED_SIZE * 4;
memset(p_hwfn->unzip_buf, 0, max_size);
output_len = qed_unzip_data(p_hwfn, input_len,
(u8 *)&array_data[offset],
max_size, (u8 *)p_hwfn->unzip_buf);
if (output_len) {
rc = qed_init_array_dmae(p_hwfn, p_ptt, addr, 0,
output_len,
p_hwfn->unzip_buf,
b_must_dmae, b_can_dmae);
} else {
DP_NOTICE(p_hwfn, "Failed to unzip dmae data\n");
rc = -EINVAL;
}
break;
case INIT_ARR_PATTERN:
{
u32 repeats = GET_FIELD(data,
INIT_ARRAY_PATTERN_HDR_REPETITIONS);
u32 i;
size = GET_FIELD(data, INIT_ARRAY_PATTERN_HDR_PATTERN_SIZE);
for (i = 0; i < repeats; i++, addr += size << 2) {
rc = qed_init_array_dmae(p_hwfn, p_ptt, addr,
dmae_array_offset + 1,
size, array_data,
b_must_dmae, b_can_dmae);
if (rc)
break;
}
break;
}
case INIT_ARR_STANDARD:
size = GET_FIELD(data, INIT_ARRAY_STANDARD_HDR_SIZE);
rc = qed_init_array_dmae(p_hwfn, p_ptt, addr,
dmae_array_offset + 1,
size, array_data,
b_must_dmae, b_can_dmae);
break;
}
return rc;
}
/* init_ops write command */
static int qed_init_cmd_wr(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_write_op *cmd,
bool b_can_dmae)
{
u32 data = le32_to_cpu(cmd->data);
u32 addr = GET_FIELD(data, INIT_WRITE_OP_ADDRESS) << 2;
bool b_must_dmae = GET_FIELD(data, INIT_WRITE_OP_WIDE_BUS);
union init_write_args *arg = &cmd->args;
int rc = 0;
/* Sanitize */
if (b_must_dmae && !b_can_dmae) {
DP_NOTICE(p_hwfn,
"Need to write to %08x for Wide-bus but DMAE isn't allowed\n",
addr);
return -EINVAL;
}
switch (GET_FIELD(data, INIT_WRITE_OP_SOURCE)) {
case INIT_SRC_INLINE:
qed_wr(p_hwfn, p_ptt, addr,
le32_to_cpu(arg->inline_val));
break;
case INIT_SRC_ZEROS:
if (b_must_dmae ||
(b_can_dmae && (le32_to_cpu(arg->zeros_count) >= 64)))
rc = qed_init_fill_dmae(p_hwfn, p_ptt, addr, 0,
le32_to_cpu(arg->zeros_count));
else
qed_init_fill(p_hwfn, p_ptt, addr, 0,
le32_to_cpu(arg->zeros_count));
break;
case INIT_SRC_ARRAY:
rc = qed_init_cmd_array(p_hwfn, p_ptt, cmd,
b_must_dmae, b_can_dmae);
break;
case INIT_SRC_RUNTIME:
qed_init_rt(p_hwfn, p_ptt, addr,
le16_to_cpu(arg->runtime.offset),
le16_to_cpu(arg->runtime.size));
break;
}
return rc;
}
static inline bool comp_eq(u32 val, u32 expected_val)
{
return val == expected_val;
}
static inline bool comp_and(u32 val, u32 expected_val)
{
return (val & expected_val) == expected_val;
}
static inline bool comp_or(u32 val, u32 expected_val)
{
return (val | expected_val) > 0;
}
/* init_ops read/poll commands */
static void qed_init_cmd_rd(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_read_op *cmd)
{
u32 data = le32_to_cpu(cmd->op_data);
u32 addr = GET_FIELD(data, INIT_READ_OP_ADDRESS) << 2;
bool (*comp_check)(u32 val,
u32 expected_val);
u32 delay = QED_INIT_POLL_PERIOD_US, val;
val = qed_rd(p_hwfn, p_ptt, addr);
data = le32_to_cpu(cmd->op_data);
if (GET_FIELD(data, INIT_READ_OP_POLL)) {
int i;
switch (GET_FIELD(data, INIT_READ_OP_POLL_COMP)) {
case INIT_COMPARISON_EQ:
comp_check = comp_eq;
break;
case INIT_COMPARISON_OR:
comp_check = comp_or;
break;
case INIT_COMPARISON_AND:
comp_check = comp_and;
break;
default:
comp_check = NULL;
DP_ERR(p_hwfn, "Invalid poll comparison type %08x\n",
data);
return;
}
for (i = 0;
i < QED_INIT_MAX_POLL_COUNT &&
!comp_check(val, le32_to_cpu(cmd->expected_val));
i++) {
udelay(delay);
val = qed_rd(p_hwfn, p_ptt, addr);
}
if (i == QED_INIT_MAX_POLL_COUNT)
DP_ERR(p_hwfn,
"Timeout when polling reg: 0x%08x [ Waiting-for: %08x Got: %08x (comparsion %08x)]\n",
addr, le32_to_cpu(cmd->expected_val),
val, data);
}
}
/* init_ops callbacks entry point */
static void qed_init_cmd_cb(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
struct init_callback_op *p_cmd)
{
DP_NOTICE(p_hwfn, "Currently init values have no need of callbacks\n");
}
static u8 qed_init_cmd_mode_match(struct qed_hwfn *p_hwfn,
u16 *offset,
int modes)
{
struct qed_dev *cdev = p_hwfn->cdev;
const u8 *modes_tree_buf;
u8 arg1, arg2, tree_val;
modes_tree_buf = cdev->fw_data->modes_tree_buf;
tree_val = modes_tree_buf[(*offset)++];
switch (tree_val) {
case INIT_MODE_OP_NOT:
return qed_init_cmd_mode_match(p_hwfn, offset, modes) ^ 1;
case INIT_MODE_OP_OR:
arg1 = qed_init_cmd_mode_match(p_hwfn, offset, modes);
arg2 = qed_init_cmd_mode_match(p_hwfn, offset, modes);
return arg1 | arg2;
case INIT_MODE_OP_AND:
arg1 = qed_init_cmd_mode_match(p_hwfn, offset, modes);
arg2 = qed_init_cmd_mode_match(p_hwfn, offset, modes);
return arg1 & arg2;
default:
tree_val -= MAX_INIT_MODE_OPS;
return (modes & (1 << tree_val)) ? 1 : 0;
}
}
static u32 qed_init_cmd_mode(struct qed_hwfn *p_hwfn,
struct init_if_mode_op *p_cmd,
int modes)
{
u16 offset = le16_to_cpu(p_cmd->modes_buf_offset);
if (qed_init_cmd_mode_match(p_hwfn, &offset, modes))
return 0;
else
return GET_FIELD(le32_to_cpu(p_cmd->op_data),
INIT_IF_MODE_OP_CMD_OFFSET);
}
static u32 qed_init_cmd_phase(struct qed_hwfn *p_hwfn,
struct init_if_phase_op *p_cmd,
u32 phase,
u32 phase_id)
{
u32 data = le32_to_cpu(p_cmd->phase_data);
u32 op_data = le32_to_cpu(p_cmd->op_data);
if (!(GET_FIELD(data, INIT_IF_PHASE_OP_PHASE) == phase &&
(GET_FIELD(data, INIT_IF_PHASE_OP_PHASE_ID) == ANY_PHASE_ID ||
GET_FIELD(data, INIT_IF_PHASE_OP_PHASE_ID) == phase_id)))
return GET_FIELD(op_data, INIT_IF_PHASE_OP_CMD_OFFSET);
else
return 0;
}
int qed_init_run(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt,
int phase,
int phase_id,
int modes)
{
struct qed_dev *cdev = p_hwfn->cdev;
u32 cmd_num, num_init_ops;
union init_op *init_ops;
bool b_dmae = false;
int rc = 0;
num_init_ops = cdev->fw_data->init_ops_size;
init_ops = cdev->fw_data->init_ops;
p_hwfn->unzip_buf = kzalloc(MAX_ZIPPED_SIZE * 4, GFP_ATOMIC);
if (!p_hwfn->unzip_buf) {
DP_NOTICE(p_hwfn, "Failed to allocate unzip buffer\n");
return -ENOMEM;
}
for (cmd_num = 0; cmd_num < num_init_ops; cmd_num++) {
union init_op *cmd = &init_ops[cmd_num];
u32 data = le32_to_cpu(cmd->raw.op_data);
switch (GET_FIELD(data, INIT_CALLBACK_OP_OP)) {
case INIT_OP_WRITE:
rc = qed_init_cmd_wr(p_hwfn, p_ptt, &cmd->write,
b_dmae);
break;
case INIT_OP_READ:
qed_init_cmd_rd(p_hwfn, p_ptt, &cmd->read);
break;
case INIT_OP_IF_MODE:
cmd_num += qed_init_cmd_mode(p_hwfn, &cmd->if_mode,
modes);
break;
case INIT_OP_IF_PHASE:
cmd_num += qed_init_cmd_phase(p_hwfn, &cmd->if_phase,
phase, phase_id);
b_dmae = GET_FIELD(data, INIT_IF_PHASE_OP_DMAE_ENABLE);
break;
case INIT_OP_DELAY:
/* qed_init_run is always invoked from
* sleep-able context
*/
udelay(le32_to_cpu(cmd->delay.delay));
break;
case INIT_OP_CALLBACK:
qed_init_cmd_cb(p_hwfn, p_ptt, &cmd->callback);
break;
}
if (rc)
break;
}
kfree(p_hwfn->unzip_buf);
return rc;
}
void qed_gtt_init(struct qed_hwfn *p_hwfn)
{
u32 gtt_base;
u32 i;
/* Set the global windows */
gtt_base = PXP_PF_WINDOW_ADMIN_START + PXP_PF_WINDOW_ADMIN_GLOBAL_START;
for (i = 0; i < ARRAY_SIZE(pxp_global_win); i++)
if (pxp_global_win[i])
REG_WR(p_hwfn, gtt_base + i * PXP_GLOBAL_ENTRY_SIZE,
pxp_global_win[i]);
}
int qed_init_fw_data(struct qed_dev *cdev,
const u8 *data)
{
struct qed_fw_data *fw = cdev->fw_data;
struct bin_buffer_hdr *buf_hdr;
u32 offset, len;
if (!data) {
DP_NOTICE(cdev, "Invalid fw data\n");
return -EINVAL;
}
buf_hdr = (struct bin_buffer_hdr *)data;
offset = buf_hdr[BIN_BUF_INIT_CMD].offset;
fw->init_ops = (union init_op *)(data + offset);
offset = buf_hdr[BIN_BUF_INIT_VAL].offset;
fw->arr_data = (u32 *)(data + offset);
offset = buf_hdr[BIN_BUF_INIT_MODE_TREE].offset;
fw->modes_tree_buf = (u8 *)(data + offset);
len = buf_hdr[BIN_BUF_INIT_CMD].length;
fw->init_ops_size = len / sizeof(struct init_raw_op);
return 0;
}