summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/mellanox/mlxsw/core_linecard_dev.c
blob: af37e650a8ad0c21d09ae56c21b863f634d8f9c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/* Copyright (c) 2022 NVIDIA Corporation and Mellanox Technologies. All rights reserved */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/types.h>
#include <linux/auxiliary_bus.h>
#include <linux/idr.h>
#include <linux/gfp.h>
#include <linux/slab.h>
#include <net/devlink.h>
#include "core.h"

#define MLXSW_LINECARD_DEV_ID_NAME "lc"

struct mlxsw_linecard_dev {
	struct mlxsw_linecard *linecard;
};

struct mlxsw_linecard_bdev {
	struct auxiliary_device adev;
	struct mlxsw_linecard *linecard;
	struct mlxsw_linecard_dev *linecard_dev;
};

static DEFINE_IDA(mlxsw_linecard_bdev_ida);

static int mlxsw_linecard_bdev_id_alloc(void)
{
	return ida_alloc(&mlxsw_linecard_bdev_ida, GFP_KERNEL);
}

static void mlxsw_linecard_bdev_id_free(int id)
{
	ida_free(&mlxsw_linecard_bdev_ida, id);
}

static void mlxsw_linecard_bdev_release(struct device *device)
{
	struct auxiliary_device *adev =
			container_of(device, struct auxiliary_device, dev);
	struct mlxsw_linecard_bdev *linecard_bdev =
			container_of(adev, struct mlxsw_linecard_bdev, adev);

	mlxsw_linecard_bdev_id_free(adev->id);
	kfree(linecard_bdev);
}

int mlxsw_linecard_bdev_add(struct mlxsw_linecard *linecard)
{
	struct mlxsw_linecard_bdev *linecard_bdev;
	int err;
	int id;

	id = mlxsw_linecard_bdev_id_alloc();
	if (id < 0)
		return id;

	linecard_bdev = kzalloc(sizeof(*linecard_bdev), GFP_KERNEL);
	if (!linecard_bdev) {
		mlxsw_linecard_bdev_id_free(id);
		return -ENOMEM;
	}
	linecard_bdev->adev.id = id;
	linecard_bdev->adev.name = MLXSW_LINECARD_DEV_ID_NAME;
	linecard_bdev->adev.dev.release = mlxsw_linecard_bdev_release;
	linecard_bdev->adev.dev.parent = linecard->linecards->bus_info->dev;
	linecard_bdev->linecard = linecard;

	err = auxiliary_device_init(&linecard_bdev->adev);
	if (err) {
		mlxsw_linecard_bdev_id_free(id);
		kfree(linecard_bdev);
		return err;
	}

	err = auxiliary_device_add(&linecard_bdev->adev);
	if (err) {
		auxiliary_device_uninit(&linecard_bdev->adev);
		return err;
	}

	linecard->bdev = linecard_bdev;
	return 0;
}

void mlxsw_linecard_bdev_del(struct mlxsw_linecard *linecard)
{
	struct mlxsw_linecard_bdev *linecard_bdev = linecard->bdev;

	if (!linecard_bdev)
		/* Unprovisioned line cards do not have an auxiliary device. */
		return;
	auxiliary_device_delete(&linecard_bdev->adev);
	auxiliary_device_uninit(&linecard_bdev->adev);
	linecard->bdev = NULL;
}

static int mlxsw_linecard_dev_devlink_info_get(struct devlink *devlink,
					       struct devlink_info_req *req,
					       struct netlink_ext_ack *extack)
{
	struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink);
	struct mlxsw_linecard *linecard = linecard_dev->linecard;

	return mlxsw_linecard_devlink_info_get(linecard, req, extack);
}

static int
mlxsw_linecard_dev_devlink_flash_update(struct devlink *devlink,
					struct devlink_flash_update_params *params,
					struct netlink_ext_ack *extack)
{
	struct mlxsw_linecard_dev *linecard_dev = devlink_priv(devlink);
	struct mlxsw_linecard *linecard = linecard_dev->linecard;

	return mlxsw_linecard_flash_update(devlink, linecard,
					   params->fw, extack);
}

static const struct devlink_ops mlxsw_linecard_dev_devlink_ops = {
	.info_get			= mlxsw_linecard_dev_devlink_info_get,
	.flash_update			= mlxsw_linecard_dev_devlink_flash_update,
};

static int mlxsw_linecard_bdev_probe(struct auxiliary_device *adev,
				     const struct auxiliary_device_id *id)
{
	struct mlxsw_linecard_bdev *linecard_bdev =
			container_of(adev, struct mlxsw_linecard_bdev, adev);
	struct mlxsw_linecard *linecard = linecard_bdev->linecard;
	struct mlxsw_linecard_dev *linecard_dev;
	struct devlink *devlink;

	devlink = devlink_alloc(&mlxsw_linecard_dev_devlink_ops,
				sizeof(*linecard_dev), &adev->dev);
	if (!devlink)
		return -ENOMEM;
	linecard_dev = devlink_priv(devlink);
	linecard_dev->linecard = linecard_bdev->linecard;
	linecard_bdev->linecard_dev = linecard_dev;

	devlink_register(devlink);
	devlink_linecard_nested_dl_set(linecard->devlink_linecard, devlink);
	return 0;
}

static void mlxsw_linecard_bdev_remove(struct auxiliary_device *adev)
{
	struct mlxsw_linecard_bdev *linecard_bdev =
			container_of(adev, struct mlxsw_linecard_bdev, adev);
	struct devlink *devlink = priv_to_devlink(linecard_bdev->linecard_dev);
	struct mlxsw_linecard *linecard = linecard_bdev->linecard;

	devlink_linecard_nested_dl_set(linecard->devlink_linecard, NULL);
	devlink_unregister(devlink);
	devlink_free(devlink);
}

static const struct auxiliary_device_id mlxsw_linecard_bdev_id_table[] = {
	{ .name = KBUILD_MODNAME "." MLXSW_LINECARD_DEV_ID_NAME },
	{},
};

MODULE_DEVICE_TABLE(auxiliary, mlxsw_linecard_bdev_id_table);

static struct auxiliary_driver mlxsw_linecard_driver = {
	.name = MLXSW_LINECARD_DEV_ID_NAME,
	.probe = mlxsw_linecard_bdev_probe,
	.remove = mlxsw_linecard_bdev_remove,
	.id_table = mlxsw_linecard_bdev_id_table,
};

int mlxsw_linecard_driver_register(void)
{
	return auxiliary_driver_register(&mlxsw_linecard_driver);
}

void mlxsw_linecard_driver_unregister(void)
{
	auxiliary_driver_unregister(&mlxsw_linecard_driver);
}