162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (c) 2013, Cisco Systems, Inc. All rights reserved.
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This software is available to you under a choice of one of two
562306a36Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the
862306a36Sopenharmony_ci * BSD license below:
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
1162306a36Sopenharmony_ci *     without modification, are permitted provided that the following
1262306a36Sopenharmony_ci *     conditions are met:
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci *      - Redistributions of source code must retain the above
1562306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
1662306a36Sopenharmony_ci *        disclaimer.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
1962306a36Sopenharmony_ci *        copyright notice, this list of conditions and the following
2062306a36Sopenharmony_ci *        disclaimer in the documentation and/or other materials
2162306a36Sopenharmony_ci *        provided with the distribution.
2262306a36Sopenharmony_ci *
2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
3062306a36Sopenharmony_ci * SOFTWARE.
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci */
3362306a36Sopenharmony_ci#include <linux/errno.h>
3462306a36Sopenharmony_ci#include <linux/pci.h>
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#include "usnic_ib.h"
3762306a36Sopenharmony_ci#include "vnic_resource.h"
3862306a36Sopenharmony_ci#include "usnic_log.h"
3962306a36Sopenharmony_ci#include "usnic_vnic.h"
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistruct usnic_vnic {
4262306a36Sopenharmony_ci	struct vnic_dev			*vdev;
4362306a36Sopenharmony_ci	struct vnic_dev_bar		bar[PCI_NUM_RESOURCES];
4462306a36Sopenharmony_ci	struct usnic_vnic_res_chunk	chunks[USNIC_VNIC_RES_TYPE_MAX];
4562306a36Sopenharmony_ci	spinlock_t			res_lock;
4662306a36Sopenharmony_ci};
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic enum vnic_res_type _to_vnic_res_type(enum usnic_vnic_res_type res_type)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci#define DEFINE_USNIC_VNIC_RES_AT(usnic_vnic_res_t, vnic_res_type, desc, val) \
5162306a36Sopenharmony_ci		vnic_res_type,
5262306a36Sopenharmony_ci#define DEFINE_USNIC_VNIC_RES(usnic_vnic_res_t, vnic_res_type, desc) \
5362306a36Sopenharmony_ci		vnic_res_type,
5462306a36Sopenharmony_ci	static enum vnic_res_type usnic_vnic_type_2_vnic_type[] = {
5562306a36Sopenharmony_ci						USNIC_VNIC_RES_TYPES};
5662306a36Sopenharmony_ci#undef DEFINE_USNIC_VNIC_RES
5762306a36Sopenharmony_ci#undef DEFINE_USNIC_VNIC_RES_AT
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	if (res_type >= USNIC_VNIC_RES_TYPE_MAX)
6062306a36Sopenharmony_ci		return RES_TYPE_MAX;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return usnic_vnic_type_2_vnic_type[res_type];
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ciconst char *usnic_vnic_res_type_to_str(enum usnic_vnic_res_type res_type)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci#define DEFINE_USNIC_VNIC_RES_AT(usnic_vnic_res_t, vnic_res_type, desc, val) \
6862306a36Sopenharmony_ci		desc,
6962306a36Sopenharmony_ci#define DEFINE_USNIC_VNIC_RES(usnic_vnic_res_t, vnic_res_type, desc) \
7062306a36Sopenharmony_ci		desc,
7162306a36Sopenharmony_ci	static const char * const usnic_vnic_res_type_desc[] = {
7262306a36Sopenharmony_ci						USNIC_VNIC_RES_TYPES};
7362306a36Sopenharmony_ci#undef DEFINE_USNIC_VNIC_RES
7462306a36Sopenharmony_ci#undef DEFINE_USNIC_VNIC_RES_AT
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (res_type >= USNIC_VNIC_RES_TYPE_MAX)
7762306a36Sopenharmony_ci		return "unknown";
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	return usnic_vnic_res_type_desc[res_type];
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ciconst char *usnic_vnic_pci_name(struct usnic_vnic *vnic)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	return pci_name(usnic_vnic_get_pdev(vnic));
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ciint usnic_vnic_dump(struct usnic_vnic *vnic, char *buf,
8962306a36Sopenharmony_ci			int buf_sz,
9062306a36Sopenharmony_ci			void *hdr_obj,
9162306a36Sopenharmony_ci			int (*printtitle)(void *, char*, int),
9262306a36Sopenharmony_ci			int (*printcols)(char *, int),
9362306a36Sopenharmony_ci			int (*printrow)(void *, char *, int))
9462306a36Sopenharmony_ci{
9562306a36Sopenharmony_ci	struct usnic_vnic_res_chunk *chunk;
9662306a36Sopenharmony_ci	struct usnic_vnic_res *res;
9762306a36Sopenharmony_ci	struct vnic_dev_bar *bar0;
9862306a36Sopenharmony_ci	int i, j, offset;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	offset = 0;
10162306a36Sopenharmony_ci	bar0 = usnic_vnic_get_bar(vnic, 0);
10262306a36Sopenharmony_ci	offset += scnprintf(buf + offset, buf_sz - offset,
10362306a36Sopenharmony_ci			"VF:%hu BAR0 bus_addr=%pa vaddr=0x%p size=%ld ",
10462306a36Sopenharmony_ci			usnic_vnic_get_index(vnic),
10562306a36Sopenharmony_ci			&bar0->bus_addr,
10662306a36Sopenharmony_ci			bar0->vaddr, bar0->len);
10762306a36Sopenharmony_ci	if (printtitle)
10862306a36Sopenharmony_ci		offset += printtitle(hdr_obj, buf + offset, buf_sz - offset);
10962306a36Sopenharmony_ci	offset += scnprintf(buf + offset, buf_sz - offset, "\n");
11062306a36Sopenharmony_ci	offset += scnprintf(buf + offset, buf_sz - offset,
11162306a36Sopenharmony_ci			"|RES\t|CTRL_PIN\t\t|IN_USE\t");
11262306a36Sopenharmony_ci	if (printcols)
11362306a36Sopenharmony_ci		offset += printcols(buf + offset, buf_sz - offset);
11462306a36Sopenharmony_ci	offset += scnprintf(buf + offset, buf_sz - offset, "\n");
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	spin_lock(&vnic->res_lock);
11762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vnic->chunks); i++) {
11862306a36Sopenharmony_ci		chunk = &vnic->chunks[i];
11962306a36Sopenharmony_ci		for (j = 0; j < chunk->cnt; j++) {
12062306a36Sopenharmony_ci			res = chunk->res[j];
12162306a36Sopenharmony_ci			offset += scnprintf(buf + offset, buf_sz - offset,
12262306a36Sopenharmony_ci					"|%s[%u]\t|0x%p\t|%u\t",
12362306a36Sopenharmony_ci					usnic_vnic_res_type_to_str(res->type),
12462306a36Sopenharmony_ci					res->vnic_idx, res->ctrl, !!res->owner);
12562306a36Sopenharmony_ci			if (printrow) {
12662306a36Sopenharmony_ci				offset += printrow(res->owner, buf + offset,
12762306a36Sopenharmony_ci							buf_sz - offset);
12862306a36Sopenharmony_ci			}
12962306a36Sopenharmony_ci			offset += scnprintf(buf + offset, buf_sz - offset,
13062306a36Sopenharmony_ci						"\n");
13162306a36Sopenharmony_ci		}
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci	spin_unlock(&vnic->res_lock);
13462306a36Sopenharmony_ci	return offset;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_civoid usnic_vnic_res_spec_update(struct usnic_vnic_res_spec *spec,
13862306a36Sopenharmony_ci				enum usnic_vnic_res_type trgt_type,
13962306a36Sopenharmony_ci				u16 cnt)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int i;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
14462306a36Sopenharmony_ci		if (spec->resources[i].type == trgt_type) {
14562306a36Sopenharmony_ci			spec->resources[i].cnt = cnt;
14662306a36Sopenharmony_ci			return;
14762306a36Sopenharmony_ci		}
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	WARN_ON(1);
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ciint usnic_vnic_res_spec_satisfied(const struct usnic_vnic_res_spec *min_spec,
15462306a36Sopenharmony_ci					struct usnic_vnic_res_spec *res_spec)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	int found, i, j;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
15962306a36Sopenharmony_ci		found = 0;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci		for (j = 0; j < USNIC_VNIC_RES_TYPE_MAX; j++) {
16262306a36Sopenharmony_ci			if (res_spec->resources[i].type !=
16362306a36Sopenharmony_ci				min_spec->resources[i].type)
16462306a36Sopenharmony_ci				continue;
16562306a36Sopenharmony_ci			found = 1;
16662306a36Sopenharmony_ci			if (min_spec->resources[i].cnt >
16762306a36Sopenharmony_ci					res_spec->resources[i].cnt)
16862306a36Sopenharmony_ci				return -EINVAL;
16962306a36Sopenharmony_ci			break;
17062306a36Sopenharmony_ci		}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci		if (!found)
17362306a36Sopenharmony_ci			return -EINVAL;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	return 0;
17662306a36Sopenharmony_ci}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ciint usnic_vnic_spec_dump(char *buf, int buf_sz,
17962306a36Sopenharmony_ci				struct usnic_vnic_res_spec *res_spec)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	enum usnic_vnic_res_type res_type;
18262306a36Sopenharmony_ci	int res_cnt;
18362306a36Sopenharmony_ci	int i;
18462306a36Sopenharmony_ci	int offset = 0;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
18762306a36Sopenharmony_ci		res_type = res_spec->resources[i].type;
18862306a36Sopenharmony_ci		res_cnt = res_spec->resources[i].cnt;
18962306a36Sopenharmony_ci		offset += scnprintf(buf + offset, buf_sz - offset,
19062306a36Sopenharmony_ci				"Res: %s Cnt: %d ",
19162306a36Sopenharmony_ci				usnic_vnic_res_type_to_str(res_type),
19262306a36Sopenharmony_ci				res_cnt);
19362306a36Sopenharmony_ci	}
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	return offset;
19662306a36Sopenharmony_ci}
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciint usnic_vnic_check_room(struct usnic_vnic *vnic,
19962306a36Sopenharmony_ci				struct usnic_vnic_res_spec *res_spec)
20062306a36Sopenharmony_ci{
20162306a36Sopenharmony_ci	int i;
20262306a36Sopenharmony_ci	enum usnic_vnic_res_type res_type;
20362306a36Sopenharmony_ci	int res_cnt;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	for (i = 0; i < USNIC_VNIC_RES_TYPE_MAX; i++) {
20662306a36Sopenharmony_ci		res_type = res_spec->resources[i].type;
20762306a36Sopenharmony_ci		res_cnt = res_spec->resources[i].cnt;
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_ci		if (res_type == USNIC_VNIC_RES_TYPE_EOL)
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci		if (res_cnt > usnic_vnic_res_free_cnt(vnic, res_type))
21362306a36Sopenharmony_ci			return -EBUSY;
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return 0;
21762306a36Sopenharmony_ci}
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ciint usnic_vnic_res_cnt(struct usnic_vnic *vnic,
22062306a36Sopenharmony_ci				enum usnic_vnic_res_type type)
22162306a36Sopenharmony_ci{
22262306a36Sopenharmony_ci	return vnic->chunks[type].cnt;
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ciint usnic_vnic_res_free_cnt(struct usnic_vnic *vnic,
22662306a36Sopenharmony_ci				enum usnic_vnic_res_type type)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	return vnic->chunks[type].free_cnt;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistruct usnic_vnic_res_chunk *
23262306a36Sopenharmony_ciusnic_vnic_get_resources(struct usnic_vnic *vnic, enum usnic_vnic_res_type type,
23362306a36Sopenharmony_ci				int cnt, void *owner)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct usnic_vnic_res_chunk *src, *ret;
23662306a36Sopenharmony_ci	struct usnic_vnic_res *res;
23762306a36Sopenharmony_ci	int i;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (usnic_vnic_res_free_cnt(vnic, type) < cnt || cnt < 0 || !owner)
24062306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	ret = kzalloc(sizeof(*ret), GFP_ATOMIC);
24362306a36Sopenharmony_ci	if (!ret)
24462306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	if (cnt > 0) {
24762306a36Sopenharmony_ci		ret->res = kcalloc(cnt, sizeof(*(ret->res)), GFP_ATOMIC);
24862306a36Sopenharmony_ci		if (!ret->res) {
24962306a36Sopenharmony_ci			kfree(ret);
25062306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
25162306a36Sopenharmony_ci		}
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		spin_lock(&vnic->res_lock);
25462306a36Sopenharmony_ci		src = &vnic->chunks[type];
25562306a36Sopenharmony_ci		for (i = 0; i < src->cnt && ret->cnt < cnt; i++) {
25662306a36Sopenharmony_ci			res = src->res[i];
25762306a36Sopenharmony_ci			if (!res->owner) {
25862306a36Sopenharmony_ci				src->free_cnt--;
25962306a36Sopenharmony_ci				res->owner = owner;
26062306a36Sopenharmony_ci				ret->res[ret->cnt++] = res;
26162306a36Sopenharmony_ci			}
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		spin_unlock(&vnic->res_lock);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci	ret->type = type;
26762306a36Sopenharmony_ci	ret->vnic = vnic;
26862306a36Sopenharmony_ci	WARN_ON(ret->cnt != cnt);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	return ret;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_civoid usnic_vnic_put_resources(struct usnic_vnic_res_chunk *chunk)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci	struct usnic_vnic_res *res;
27762306a36Sopenharmony_ci	int i;
27862306a36Sopenharmony_ci	struct usnic_vnic *vnic = chunk->vnic;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	if (chunk->cnt > 0) {
28162306a36Sopenharmony_ci		spin_lock(&vnic->res_lock);
28262306a36Sopenharmony_ci		while ((i = --chunk->cnt) >= 0) {
28362306a36Sopenharmony_ci			res = chunk->res[i];
28462306a36Sopenharmony_ci			chunk->res[i] = NULL;
28562306a36Sopenharmony_ci			res->owner = NULL;
28662306a36Sopenharmony_ci			vnic->chunks[res->type].free_cnt++;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci		spin_unlock(&vnic->res_lock);
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	kfree(chunk->res);
29262306a36Sopenharmony_ci	kfree(chunk);
29362306a36Sopenharmony_ci}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ciu16 usnic_vnic_get_index(struct usnic_vnic *vnic)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	return usnic_vnic_get_pdev(vnic)->devfn - 1;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cistatic int usnic_vnic_alloc_res_chunk(struct usnic_vnic *vnic,
30162306a36Sopenharmony_ci					enum usnic_vnic_res_type type,
30262306a36Sopenharmony_ci					struct usnic_vnic_res_chunk *chunk)
30362306a36Sopenharmony_ci{
30462306a36Sopenharmony_ci	int cnt, err, i;
30562306a36Sopenharmony_ci	struct usnic_vnic_res *res;
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_ci	cnt = vnic_dev_get_res_count(vnic->vdev, _to_vnic_res_type(type));
30862306a36Sopenharmony_ci	if (cnt < 1) {
30962306a36Sopenharmony_ci		usnic_err("Wrong res count with cnt %d\n", cnt);
31062306a36Sopenharmony_ci		return -EINVAL;
31162306a36Sopenharmony_ci	}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	chunk->cnt = chunk->free_cnt = cnt;
31462306a36Sopenharmony_ci	chunk->res = kcalloc(cnt, sizeof(*(chunk->res)), GFP_KERNEL);
31562306a36Sopenharmony_ci	if (!chunk->res)
31662306a36Sopenharmony_ci		return -ENOMEM;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	for (i = 0; i < cnt; i++) {
31962306a36Sopenharmony_ci		res = kzalloc(sizeof(*res), GFP_KERNEL);
32062306a36Sopenharmony_ci		if (!res) {
32162306a36Sopenharmony_ci			err = -ENOMEM;
32262306a36Sopenharmony_ci			goto fail;
32362306a36Sopenharmony_ci		}
32462306a36Sopenharmony_ci		res->type = type;
32562306a36Sopenharmony_ci		res->vnic_idx = i;
32662306a36Sopenharmony_ci		res->vnic = vnic;
32762306a36Sopenharmony_ci		res->ctrl = vnic_dev_get_res(vnic->vdev,
32862306a36Sopenharmony_ci						_to_vnic_res_type(type), i);
32962306a36Sopenharmony_ci		chunk->res[i] = res;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	chunk->vnic = vnic;
33362306a36Sopenharmony_ci	return 0;
33462306a36Sopenharmony_cifail:
33562306a36Sopenharmony_ci	for (i--; i >= 0; i--)
33662306a36Sopenharmony_ci		kfree(chunk->res[i]);
33762306a36Sopenharmony_ci	kfree(chunk->res);
33862306a36Sopenharmony_ci	return err;
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_cistatic void usnic_vnic_free_res_chunk(struct usnic_vnic_res_chunk *chunk)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	int i;
34462306a36Sopenharmony_ci	for (i = 0; i < chunk->cnt; i++)
34562306a36Sopenharmony_ci		kfree(chunk->res[i]);
34662306a36Sopenharmony_ci	kfree(chunk->res);
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int usnic_vnic_discover_resources(struct pci_dev *pdev,
35062306a36Sopenharmony_ci						struct usnic_vnic *vnic)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	enum usnic_vnic_res_type res_type;
35362306a36Sopenharmony_ci	int i;
35462306a36Sopenharmony_ci	int err = 0;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vnic->bar); i++) {
35762306a36Sopenharmony_ci		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
35862306a36Sopenharmony_ci			continue;
35962306a36Sopenharmony_ci		vnic->bar[i].len = pci_resource_len(pdev, i);
36062306a36Sopenharmony_ci		vnic->bar[i].vaddr = pci_iomap(pdev, i, vnic->bar[i].len);
36162306a36Sopenharmony_ci		if (!vnic->bar[i].vaddr) {
36262306a36Sopenharmony_ci			usnic_err("Cannot memory-map BAR %d, aborting\n",
36362306a36Sopenharmony_ci					i);
36462306a36Sopenharmony_ci			err = -ENODEV;
36562306a36Sopenharmony_ci			goto out_clean_bar;
36662306a36Sopenharmony_ci		}
36762306a36Sopenharmony_ci		vnic->bar[i].bus_addr = pci_resource_start(pdev, i);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	vnic->vdev = vnic_dev_register(NULL, pdev, pdev, vnic->bar,
37162306a36Sopenharmony_ci			ARRAY_SIZE(vnic->bar));
37262306a36Sopenharmony_ci	if (!vnic->vdev) {
37362306a36Sopenharmony_ci		usnic_err("Failed to register device %s\n",
37462306a36Sopenharmony_ci				pci_name(pdev));
37562306a36Sopenharmony_ci		err = -EINVAL;
37662306a36Sopenharmony_ci		goto out_clean_bar;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	for (res_type = USNIC_VNIC_RES_TYPE_EOL + 1;
38062306a36Sopenharmony_ci			res_type < USNIC_VNIC_RES_TYPE_MAX; res_type++) {
38162306a36Sopenharmony_ci		err = usnic_vnic_alloc_res_chunk(vnic, res_type,
38262306a36Sopenharmony_ci						&vnic->chunks[res_type]);
38362306a36Sopenharmony_ci		if (err)
38462306a36Sopenharmony_ci			goto out_clean_chunks;
38562306a36Sopenharmony_ci	}
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	return 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ciout_clean_chunks:
39062306a36Sopenharmony_ci	for (res_type--; res_type > USNIC_VNIC_RES_TYPE_EOL; res_type--)
39162306a36Sopenharmony_ci		usnic_vnic_free_res_chunk(&vnic->chunks[res_type]);
39262306a36Sopenharmony_ci	vnic_dev_unregister(vnic->vdev);
39362306a36Sopenharmony_ciout_clean_bar:
39462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vnic->bar); i++) {
39562306a36Sopenharmony_ci		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
39662306a36Sopenharmony_ci			continue;
39762306a36Sopenharmony_ci		if (!vnic->bar[i].vaddr)
39862306a36Sopenharmony_ci			break;
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci		iounmap(vnic->bar[i].vaddr);
40162306a36Sopenharmony_ci	}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	return err;
40462306a36Sopenharmony_ci}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_cistruct pci_dev *usnic_vnic_get_pdev(struct usnic_vnic *vnic)
40762306a36Sopenharmony_ci{
40862306a36Sopenharmony_ci	return vnic_dev_get_pdev(vnic->vdev);
40962306a36Sopenharmony_ci}
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_cistruct vnic_dev_bar *usnic_vnic_get_bar(struct usnic_vnic *vnic,
41262306a36Sopenharmony_ci				int bar_num)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	return (bar_num < ARRAY_SIZE(vnic->bar)) ? &vnic->bar[bar_num] : NULL;
41562306a36Sopenharmony_ci}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void usnic_vnic_release_resources(struct usnic_vnic *vnic)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	int i;
42062306a36Sopenharmony_ci	struct pci_dev *pdev;
42162306a36Sopenharmony_ci	enum usnic_vnic_res_type res_type;
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	pdev = usnic_vnic_get_pdev(vnic);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	for (res_type = USNIC_VNIC_RES_TYPE_EOL + 1;
42662306a36Sopenharmony_ci			res_type < USNIC_VNIC_RES_TYPE_MAX; res_type++)
42762306a36Sopenharmony_ci		usnic_vnic_free_res_chunk(&vnic->chunks[res_type]);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	vnic_dev_unregister(vnic->vdev);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vnic->bar); i++) {
43262306a36Sopenharmony_ci		if (!(pci_resource_flags(pdev, i) & IORESOURCE_MEM))
43362306a36Sopenharmony_ci			continue;
43462306a36Sopenharmony_ci		iounmap(vnic->bar[i].vaddr);
43562306a36Sopenharmony_ci	}
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_cistruct usnic_vnic *usnic_vnic_alloc(struct pci_dev *pdev)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	struct usnic_vnic *vnic;
44162306a36Sopenharmony_ci	int err = 0;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	if (!pci_is_enabled(pdev)) {
44462306a36Sopenharmony_ci		usnic_err("PCI dev %s is disabled\n", pci_name(pdev));
44562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	vnic = kzalloc(sizeof(*vnic), GFP_KERNEL);
44962306a36Sopenharmony_ci	if (!vnic)
45062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	spin_lock_init(&vnic->res_lock);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	err = usnic_vnic_discover_resources(pdev, vnic);
45562306a36Sopenharmony_ci	if (err) {
45662306a36Sopenharmony_ci		usnic_err("Failed to discover %s resources with err %d\n",
45762306a36Sopenharmony_ci				pci_name(pdev), err);
45862306a36Sopenharmony_ci		goto out_free_vnic;
45962306a36Sopenharmony_ci	}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	usnic_dbg("Allocated vnic for %s\n", usnic_vnic_pci_name(vnic));
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	return vnic;
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ciout_free_vnic:
46662306a36Sopenharmony_ci	kfree(vnic);
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	return ERR_PTR(err);
46962306a36Sopenharmony_ci}
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_civoid usnic_vnic_free(struct usnic_vnic *vnic)
47262306a36Sopenharmony_ci{
47362306a36Sopenharmony_ci	usnic_vnic_release_resources(vnic);
47462306a36Sopenharmony_ci	kfree(vnic);
47562306a36Sopenharmony_ci}
476