mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-28 18:05:00 -04:00
The .driver_data member of the struct pci_device_id array were initialized by a list expressions to zero without making use of that value. In this case it's better to not specify a value at all and let the compiler fill in the zeros. Same for the list terminator that can better be completely empty. This patch doesn't introduce changes to the compiled array. Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com> Reviewed-by: Breno Leitao <leitao@debian.org> Acked-by: Alexandra Winter <wintera@linux.ibm.com> Link: https://patch.msgid.link/20260522153010.777081-2-u.kleine-koenig@baylibre.com Signed-off-by: Paolo Abeni <pabeni@redhat.com>
721 lines
15 KiB
C
721 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* ISM driver for s390.
|
|
*
|
|
* Copyright IBM Corp. 2018
|
|
*/
|
|
#define pr_fmt(fmt) "ism: " fmt
|
|
|
|
#include <linux/export.h>
|
|
#include <linux/module.h>
|
|
#include <linux/types.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/device.h>
|
|
#include <linux/err.h>
|
|
#include <linux/ctype.h>
|
|
#include <linux/processor.h>
|
|
|
|
#include "ism.h"
|
|
|
|
MODULE_DESCRIPTION("ISM driver for s390");
|
|
MODULE_LICENSE("GPL");
|
|
|
|
#define DRV_NAME "ism"
|
|
|
|
static const struct pci_device_id ism_device_table[] = {
|
|
{ PCI_VDEVICE(IBM, PCI_DEVICE_ID_IBM_ISM) },
|
|
{ }
|
|
};
|
|
MODULE_DEVICE_TABLE(pci, ism_device_table);
|
|
|
|
static debug_info_t *ism_debug_info;
|
|
|
|
static int ism_cmd(struct ism_dev *ism, void *cmd)
|
|
{
|
|
struct ism_req_hdr *req = cmd;
|
|
struct ism_resp_hdr *resp = cmd;
|
|
|
|
spin_lock(&ism->cmd_lock);
|
|
__ism_write_cmd(ism, req + 1, sizeof(*req), req->len - sizeof(*req));
|
|
__ism_write_cmd(ism, req, 0, sizeof(*req));
|
|
|
|
WRITE_ONCE(resp->ret, ISM_ERROR);
|
|
|
|
__ism_read_cmd(ism, resp, 0, sizeof(*resp));
|
|
if (resp->ret) {
|
|
debug_text_event(ism_debug_info, 0, "cmd failure");
|
|
debug_event(ism_debug_info, 0, resp, sizeof(*resp));
|
|
goto out;
|
|
}
|
|
__ism_read_cmd(ism, resp + 1, sizeof(*resp), resp->len - sizeof(*resp));
|
|
out:
|
|
spin_unlock(&ism->cmd_lock);
|
|
return resp->ret;
|
|
}
|
|
|
|
static int ism_cmd_simple(struct ism_dev *ism, u32 cmd_code)
|
|
{
|
|
union ism_cmd_simple cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = cmd_code;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
return ism_cmd(ism, &cmd);
|
|
}
|
|
|
|
static int query_info(struct ism_dev *ism)
|
|
{
|
|
union ism_qi cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_QUERY_INFO;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
if (ism_cmd(ism, &cmd))
|
|
goto out;
|
|
|
|
debug_text_event(ism_debug_info, 3, "query info");
|
|
debug_event(ism_debug_info, 3, &cmd.response, sizeof(cmd.response));
|
|
out:
|
|
return 0;
|
|
}
|
|
|
|
static int register_sba(struct ism_dev *ism)
|
|
{
|
|
union ism_reg_sba cmd;
|
|
dma_addr_t dma_handle;
|
|
struct ism_sba *sba;
|
|
|
|
sba = dma_alloc_coherent(&ism->pdev->dev, PAGE_SIZE, &dma_handle,
|
|
GFP_KERNEL);
|
|
if (!sba)
|
|
return -ENOMEM;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_REG_SBA;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
cmd.request.sba = dma_handle;
|
|
|
|
if (ism_cmd(ism, &cmd)) {
|
|
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, sba, dma_handle);
|
|
return -EIO;
|
|
}
|
|
|
|
ism->sba = sba;
|
|
ism->sba_dma_addr = dma_handle;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int register_ieq(struct ism_dev *ism)
|
|
{
|
|
union ism_reg_ieq cmd;
|
|
dma_addr_t dma_handle;
|
|
struct ism_eq *ieq;
|
|
|
|
ieq = dma_alloc_coherent(&ism->pdev->dev, PAGE_SIZE, &dma_handle,
|
|
GFP_KERNEL);
|
|
if (!ieq)
|
|
return -ENOMEM;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_REG_IEQ;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
cmd.request.ieq = dma_handle;
|
|
cmd.request.len = sizeof(*ieq);
|
|
|
|
if (ism_cmd(ism, &cmd)) {
|
|
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE, ieq, dma_handle);
|
|
return -EIO;
|
|
}
|
|
|
|
ism->ieq = ieq;
|
|
ism->ieq_idx = -1;
|
|
ism->ieq_dma_addr = dma_handle;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int unregister_sba(struct ism_dev *ism)
|
|
{
|
|
int ret;
|
|
|
|
if (!ism->sba)
|
|
return 0;
|
|
|
|
ret = ism_cmd_simple(ism, ISM_UNREG_SBA);
|
|
if (ret && ret != ISM_ERROR)
|
|
return -EIO;
|
|
|
|
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE,
|
|
ism->sba, ism->sba_dma_addr);
|
|
|
|
ism->sba = NULL;
|
|
ism->sba_dma_addr = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int unregister_ieq(struct ism_dev *ism)
|
|
{
|
|
int ret;
|
|
|
|
if (!ism->ieq)
|
|
return 0;
|
|
|
|
ret = ism_cmd_simple(ism, ISM_UNREG_IEQ);
|
|
if (ret && ret != ISM_ERROR)
|
|
return -EIO;
|
|
|
|
dma_free_coherent(&ism->pdev->dev, PAGE_SIZE,
|
|
ism->ieq, ism->ieq_dma_addr);
|
|
|
|
ism->ieq = NULL;
|
|
ism->ieq_dma_addr = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ism_read_local_gid(struct dibs_dev *dibs)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_read_gid cmd;
|
|
int ret;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_READ_GID;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
ret = ism_cmd(ism, &cmd);
|
|
if (ret)
|
|
goto out;
|
|
|
|
memset(&dibs->gid, 0, sizeof(dibs->gid));
|
|
memcpy(&dibs->gid, &cmd.response.gid, sizeof(cmd.response.gid));
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int ism_query_rgid(struct dibs_dev *dibs, const uuid_t *rgid,
|
|
u32 vid_valid, u32 vid)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_query_rgid cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_QUERY_RGID;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
memcpy(&cmd.request.rgid, rgid, sizeof(cmd.request.rgid));
|
|
cmd.request.vlan_valid = vid_valid;
|
|
cmd.request.vlan_id = vid;
|
|
|
|
return ism_cmd(ism, &cmd);
|
|
}
|
|
|
|
static int ism_max_dmbs(void)
|
|
{
|
|
return ISM_NR_DMBS;
|
|
}
|
|
|
|
static void ism_free_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)
|
|
{
|
|
clear_bit(dmb->idx, ism->sba_bitmap);
|
|
dma_unmap_page(&ism->pdev->dev, dmb->dma_addr, dmb->dmb_len,
|
|
DMA_FROM_DEVICE);
|
|
folio_put(virt_to_folio(dmb->cpu_addr));
|
|
}
|
|
|
|
static int ism_alloc_dmb(struct ism_dev *ism, struct dibs_dmb *dmb)
|
|
{
|
|
struct folio *folio;
|
|
unsigned long bit;
|
|
int rc;
|
|
|
|
if (PAGE_ALIGN(dmb->dmb_len) > dma_get_max_seg_size(&ism->pdev->dev))
|
|
return -EINVAL;
|
|
|
|
if (!dmb->idx) {
|
|
bit = find_next_zero_bit(ism->sba_bitmap, ISM_NR_DMBS,
|
|
ISM_DMB_BIT_OFFSET);
|
|
if (bit == ISM_NR_DMBS)
|
|
return -ENOSPC;
|
|
|
|
dmb->idx = bit;
|
|
}
|
|
if (dmb->idx < ISM_DMB_BIT_OFFSET ||
|
|
test_and_set_bit(dmb->idx, ism->sba_bitmap))
|
|
return -EINVAL;
|
|
|
|
folio = folio_alloc(GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC |
|
|
__GFP_NORETRY, get_order(dmb->dmb_len));
|
|
|
|
if (!folio) {
|
|
rc = -ENOMEM;
|
|
goto out_bit;
|
|
}
|
|
|
|
dmb->cpu_addr = folio_address(folio);
|
|
dmb->dma_addr = dma_map_page(&ism->pdev->dev,
|
|
virt_to_page(dmb->cpu_addr), 0,
|
|
dmb->dmb_len, DMA_FROM_DEVICE);
|
|
if (dma_mapping_error(&ism->pdev->dev, dmb->dma_addr)) {
|
|
rc = -ENOMEM;
|
|
goto out_free;
|
|
}
|
|
|
|
return 0;
|
|
|
|
out_free:
|
|
kfree(dmb->cpu_addr);
|
|
out_bit:
|
|
clear_bit(dmb->idx, ism->sba_bitmap);
|
|
return rc;
|
|
}
|
|
|
|
static int ism_register_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb,
|
|
struct dibs_client *client)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_reg_dmb cmd;
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
ret = ism_alloc_dmb(ism, dmb);
|
|
if (ret)
|
|
goto out;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_REG_DMB;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
cmd.request.dmb = dmb->dma_addr;
|
|
cmd.request.dmb_len = dmb->dmb_len;
|
|
cmd.request.sba_idx = dmb->idx;
|
|
cmd.request.vlan_valid = dmb->vlan_valid;
|
|
cmd.request.vlan_id = dmb->vlan_id;
|
|
memcpy(&cmd.request.rgid, &dmb->rgid, sizeof(u64));
|
|
|
|
ret = ism_cmd(ism, &cmd);
|
|
if (ret) {
|
|
ism_free_dmb(ism, dmb);
|
|
goto out;
|
|
}
|
|
dmb->dmb_tok = cmd.response.dmb_tok;
|
|
spin_lock_irqsave(&dibs->lock, flags);
|
|
dibs->dmb_clientid_arr[dmb->idx - ISM_DMB_BIT_OFFSET] = client->id;
|
|
spin_unlock_irqrestore(&dibs->lock, flags);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int ism_unregister_dmb(struct dibs_dev *dibs, struct dibs_dmb *dmb)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_unreg_dmb cmd;
|
|
unsigned long flags;
|
|
int ret;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_UNREG_DMB;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
cmd.request.dmb_tok = dmb->dmb_tok;
|
|
|
|
spin_lock_irqsave(&dibs->lock, flags);
|
|
dibs->dmb_clientid_arr[dmb->idx - ISM_DMB_BIT_OFFSET] = NO_DIBS_CLIENT;
|
|
spin_unlock_irqrestore(&dibs->lock, flags);
|
|
|
|
ret = ism_cmd(ism, &cmd);
|
|
if (ret && ret != ISM_ERROR)
|
|
goto out;
|
|
|
|
ism_free_dmb(ism, dmb);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int ism_add_vlan_id(struct dibs_dev *dibs, u64 vlan_id)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_set_vlan_id cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_ADD_VLAN_ID;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
cmd.request.vlan_id = vlan_id;
|
|
|
|
return ism_cmd(ism, &cmd);
|
|
}
|
|
|
|
static int ism_del_vlan_id(struct dibs_dev *dibs, u64 vlan_id)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_set_vlan_id cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_DEL_VLAN_ID;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
cmd.request.vlan_id = vlan_id;
|
|
|
|
return ism_cmd(ism, &cmd);
|
|
}
|
|
|
|
static int ism_signal_ieq(struct dibs_dev *dibs, const uuid_t *rgid,
|
|
u32 trigger_irq, u32 event_code, u64 info)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
union ism_sig_ieq cmd;
|
|
|
|
memset(&cmd, 0, sizeof(cmd));
|
|
cmd.request.hdr.cmd = ISM_SIGNAL_IEQ;
|
|
cmd.request.hdr.len = sizeof(cmd.request);
|
|
|
|
memcpy(&cmd.request.rgid, rgid, sizeof(cmd.request.rgid));
|
|
cmd.request.trigger_irq = trigger_irq;
|
|
cmd.request.event_code = event_code;
|
|
cmd.request.info = info;
|
|
|
|
return ism_cmd(ism, &cmd);
|
|
}
|
|
|
|
static unsigned int max_bytes(unsigned int start, unsigned int len,
|
|
unsigned int boundary)
|
|
{
|
|
return min(boundary - (start & (boundary - 1)), len);
|
|
}
|
|
|
|
static int ism_move(struct dibs_dev *dibs, u64 dmb_tok, unsigned int idx,
|
|
bool sf, unsigned int offset, void *data,
|
|
unsigned int size)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
unsigned int bytes;
|
|
u64 dmb_req;
|
|
int ret;
|
|
|
|
while (size) {
|
|
bytes = max_bytes(offset, size, PAGE_SIZE);
|
|
dmb_req = ISM_CREATE_REQ(dmb_tok, idx, size == bytes ? sf : 0,
|
|
offset);
|
|
|
|
ret = __ism_move(ism, dmb_req, data, bytes);
|
|
if (ret)
|
|
return ret;
|
|
|
|
size -= bytes;
|
|
data += bytes;
|
|
offset += bytes;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static u16 ism_get_chid(struct dibs_dev *dibs)
|
|
{
|
|
struct ism_dev *ism = dibs->drv_priv;
|
|
|
|
if (!ism || !ism->pdev)
|
|
return 0;
|
|
|
|
return to_zpci(ism->pdev)->pchid;
|
|
}
|
|
|
|
static int ism_match_event_type(u32 s390_event_type)
|
|
{
|
|
switch (s390_event_type) {
|
|
case ISM_EVENT_BUF:
|
|
return DIBS_BUF_EVENT;
|
|
case ISM_EVENT_DEV:
|
|
return DIBS_DEV_EVENT;
|
|
case ISM_EVENT_SWR:
|
|
return DIBS_SW_EVENT;
|
|
default:
|
|
return DIBS_OTHER_TYPE;
|
|
}
|
|
}
|
|
|
|
static int ism_match_event_subtype(u32 s390_event_subtype)
|
|
{
|
|
switch (s390_event_subtype) {
|
|
case ISM_BUF_DMB_UNREGISTERED:
|
|
return DIBS_BUF_UNREGISTERED;
|
|
case ISM_DEV_GID_DISABLED:
|
|
return DIBS_DEV_DISABLED;
|
|
case ISM_DEV_GID_ERR_STATE:
|
|
return DIBS_DEV_ERR_STATE;
|
|
default:
|
|
return DIBS_OTHER_SUBTYPE;
|
|
}
|
|
}
|
|
|
|
static void ism_handle_event(struct ism_dev *ism)
|
|
{
|
|
struct dibs_dev *dibs = ism->dibs;
|
|
struct dibs_event event;
|
|
struct ism_event *entry;
|
|
struct dibs_client *clt;
|
|
int i;
|
|
|
|
while ((ism->ieq_idx + 1) != READ_ONCE(ism->ieq->header.idx)) {
|
|
if (++ism->ieq_idx == ARRAY_SIZE(ism->ieq->entry))
|
|
ism->ieq_idx = 0;
|
|
|
|
entry = &ism->ieq->entry[ism->ieq_idx];
|
|
debug_event(ism_debug_info, 2, entry, sizeof(*entry));
|
|
__memset(&event, 0, sizeof(event));
|
|
event.type = ism_match_event_type(entry->type);
|
|
if (event.type == DIBS_SW_EVENT)
|
|
event.subtype = entry->code;
|
|
else
|
|
event.subtype = ism_match_event_subtype(entry->code);
|
|
event.time = entry->time;
|
|
event.data = entry->info;
|
|
switch (event.type) {
|
|
case DIBS_BUF_EVENT:
|
|
event.buffer_tok = entry->tok;
|
|
break;
|
|
case DIBS_DEV_EVENT:
|
|
case DIBS_SW_EVENT:
|
|
memcpy(&event.gid, &entry->tok, sizeof(u64));
|
|
}
|
|
for (i = 0; i < MAX_DIBS_CLIENTS; ++i) {
|
|
clt = dibs->subs[i];
|
|
if (clt)
|
|
clt->ops->handle_event(dibs, &event);
|
|
}
|
|
}
|
|
}
|
|
|
|
static irqreturn_t ism_handle_irq(int irq, void *data)
|
|
{
|
|
struct ism_dev *ism = data;
|
|
unsigned long bit, end;
|
|
struct dibs_dev *dibs;
|
|
unsigned long *bv;
|
|
u16 dmbemask;
|
|
u8 client_id;
|
|
|
|
dibs = ism->dibs;
|
|
|
|
bv = (void *) &ism->sba->dmb_bits[ISM_DMB_WORD_OFFSET];
|
|
end = sizeof(ism->sba->dmb_bits) * BITS_PER_BYTE - ISM_DMB_BIT_OFFSET;
|
|
|
|
spin_lock(&dibs->lock);
|
|
ism->sba->s = 0;
|
|
barrier();
|
|
for (bit = 0;;) {
|
|
bit = find_next_bit_inv(bv, end, bit);
|
|
if (bit >= end)
|
|
break;
|
|
|
|
clear_bit_inv(bit, bv);
|
|
dmbemask = ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET];
|
|
ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0;
|
|
barrier();
|
|
client_id = dibs->dmb_clientid_arr[bit];
|
|
if (unlikely(client_id == NO_DIBS_CLIENT ||
|
|
!dibs->subs[client_id]))
|
|
continue;
|
|
dibs->subs[client_id]->ops->handle_irq(dibs,
|
|
bit + ISM_DMB_BIT_OFFSET,
|
|
dmbemask);
|
|
}
|
|
|
|
if (ism->sba->e) {
|
|
ism->sba->e = 0;
|
|
barrier();
|
|
ism_handle_event(ism);
|
|
}
|
|
spin_unlock(&dibs->lock);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static const struct dibs_dev_ops ism_ops = {
|
|
.get_fabric_id = ism_get_chid,
|
|
.query_remote_gid = ism_query_rgid,
|
|
.max_dmbs = ism_max_dmbs,
|
|
.register_dmb = ism_register_dmb,
|
|
.unregister_dmb = ism_unregister_dmb,
|
|
.move_data = ism_move,
|
|
.add_vlan_id = ism_add_vlan_id,
|
|
.del_vlan_id = ism_del_vlan_id,
|
|
.signal_event = ism_signal_ieq,
|
|
};
|
|
|
|
static int ism_dev_init(struct ism_dev *ism)
|
|
{
|
|
struct pci_dev *pdev = ism->pdev;
|
|
int ret;
|
|
|
|
ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI);
|
|
if (ret <= 0)
|
|
goto out;
|
|
|
|
ret = request_irq(pci_irq_vector(pdev, 0), ism_handle_irq, 0,
|
|
pci_name(pdev), ism);
|
|
if (ret)
|
|
goto free_vectors;
|
|
|
|
ret = register_sba(ism);
|
|
if (ret)
|
|
goto free_irq;
|
|
|
|
ret = register_ieq(ism);
|
|
if (ret)
|
|
goto unreg_sba;
|
|
|
|
query_info(ism);
|
|
return 0;
|
|
|
|
unreg_sba:
|
|
unregister_sba(ism);
|
|
free_irq:
|
|
free_irq(pci_irq_vector(pdev, 0), ism);
|
|
free_vectors:
|
|
pci_free_irq_vectors(pdev);
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static void ism_dev_exit(struct ism_dev *ism)
|
|
{
|
|
struct pci_dev *pdev = ism->pdev;
|
|
|
|
unregister_ieq(ism);
|
|
unregister_sba(ism);
|
|
free_irq(pci_irq_vector(pdev, 0), ism);
|
|
pci_free_irq_vectors(pdev);
|
|
}
|
|
|
|
static int ism_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|
{
|
|
struct dibs_dev *dibs;
|
|
struct zpci_dev *zdev;
|
|
struct ism_dev *ism;
|
|
int ret;
|
|
|
|
ism = kzalloc_obj(*ism);
|
|
if (!ism)
|
|
return -ENOMEM;
|
|
|
|
spin_lock_init(&ism->cmd_lock);
|
|
dev_set_drvdata(&pdev->dev, ism);
|
|
ism->pdev = pdev;
|
|
|
|
ret = pci_enable_device_mem(pdev);
|
|
if (ret)
|
|
goto err_dev;
|
|
|
|
ret = pci_request_mem_regions(pdev, DRV_NAME);
|
|
if (ret)
|
|
goto err_disable;
|
|
|
|
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
|
|
if (ret)
|
|
goto err_resource;
|
|
|
|
dma_set_seg_boundary(&pdev->dev, SZ_1M - 1);
|
|
dma_set_max_seg_size(&pdev->dev, SZ_1M);
|
|
pci_set_master(pdev);
|
|
|
|
dibs = dibs_dev_alloc();
|
|
if (!dibs) {
|
|
ret = -ENOMEM;
|
|
goto err_resource;
|
|
}
|
|
/* set this up before we enable interrupts */
|
|
ism->dibs = dibs;
|
|
dibs->drv_priv = ism;
|
|
dibs->ops = &ism_ops;
|
|
|
|
/* enable ism device, but any interrupts and events will be ignored
|
|
* before dibs_dev_add() adds it to any clients.
|
|
*/
|
|
ret = ism_dev_init(ism);
|
|
if (ret)
|
|
goto err_dibs;
|
|
|
|
/* after ism_dev_init() we can call ism function to set gid */
|
|
ret = ism_read_local_gid(dibs);
|
|
if (ret)
|
|
goto err_ism;
|
|
|
|
dibs->dev.parent = &pdev->dev;
|
|
|
|
zdev = to_zpci(pdev);
|
|
dev_set_name(&dibs->dev, "ism%x", zdev->uid ? zdev->uid : zdev->fid);
|
|
|
|
ret = dibs_dev_add(dibs);
|
|
if (ret)
|
|
goto err_ism;
|
|
|
|
return 0;
|
|
|
|
err_ism:
|
|
ism_dev_exit(ism);
|
|
err_dibs:
|
|
/* pairs with dibs_dev_alloc() */
|
|
put_device(&dibs->dev);
|
|
err_resource:
|
|
pci_release_mem_regions(pdev);
|
|
err_disable:
|
|
pci_disable_device(pdev);
|
|
err_dev:
|
|
dev_set_drvdata(&pdev->dev, NULL);
|
|
kfree(ism);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void ism_remove(struct pci_dev *pdev)
|
|
{
|
|
struct ism_dev *ism = dev_get_drvdata(&pdev->dev);
|
|
struct dibs_dev *dibs = ism->dibs;
|
|
|
|
dibs_dev_del(dibs);
|
|
ism_dev_exit(ism);
|
|
/* pairs with dibs_dev_alloc() */
|
|
put_device(&dibs->dev);
|
|
|
|
pci_release_mem_regions(pdev);
|
|
pci_disable_device(pdev);
|
|
dev_set_drvdata(&pdev->dev, NULL);
|
|
kfree(ism);
|
|
}
|
|
|
|
static struct pci_driver ism_driver = {
|
|
.name = DRV_NAME,
|
|
.id_table = ism_device_table,
|
|
.probe = ism_probe,
|
|
.remove = ism_remove,
|
|
};
|
|
|
|
static int __init ism_init(void)
|
|
{
|
|
int ret;
|
|
|
|
ism_debug_info = debug_register("ism", 2, 1, 16);
|
|
if (!ism_debug_info)
|
|
return -ENODEV;
|
|
|
|
debug_register_view(ism_debug_info, &debug_hex_ascii_view);
|
|
ret = pci_register_driver(&ism_driver);
|
|
if (ret)
|
|
debug_unregister(ism_debug_info);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void __exit ism_exit(void)
|
|
{
|
|
pci_unregister_driver(&ism_driver);
|
|
debug_unregister(ism_debug_info);
|
|
}
|
|
|
|
module_init(ism_init);
|
|
module_exit(ism_exit);
|