2 * Configuration layer for MediaTek MT753x gigabit switch
4 * Copyright (C) 2018 MediaTek Inc. All Rights Reserved.
6 * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>
8 * SPDX-License-Identifier: GPL-2.0+
11 #include <linux/types.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <net/genetlink.h>
18 #include "mt753x_nl.h"
20 #define MT753X_NL_CMD_REQ_ATTRS(attr) \
21 .required_attrs = attr, \
22 .nr_required_attrs = ARRAY_SIZE(attr),
24 struct mt753x_nl_cmd_item
{
27 int (*process
)(struct genl_info
*info
, struct gsw_mt753x
*gsw
);
28 u32 nr_required_attrs
;
29 const enum mt753x_attr
*required_attrs
;
32 static int mt753x_nl_response(struct sk_buff
*skb
, struct genl_info
*info
);
34 static const struct nla_policy mt753x_nl_cmd_policy
[] = {
35 [MT753X_ATTR_TYPE_MESG
] = { .type
= NLA_STRING
},
36 [MT753X_ATTR_TYPE_PHY
] = { .type
= NLA_S32
},
37 [MT753X_ATTR_TYPE_REG
] = { .type
= NLA_S32
},
38 [MT753X_ATTR_TYPE_VAL
] = { .type
= NLA_S32
},
39 [MT753X_ATTR_TYPE_DEV_NAME
] = { .type
= NLA_S32
},
40 [MT753X_ATTR_TYPE_DEV_ID
] = { .type
= NLA_S32
},
41 [MT753X_ATTR_TYPE_DEVAD
] = { .type
= NLA_S32
},
44 static const struct genl_ops mt753x_nl_ops
[] = {
46 .cmd
= MT753X_CMD_REQUEST
,
47 .doit
= mt753x_nl_response
,
48 .policy
= mt753x_nl_cmd_policy
,
49 .flags
= GENL_ADMIN_PERM
,
51 .cmd
= MT753X_CMD_READ
,
52 .doit
= mt753x_nl_response
,
53 .policy
= mt753x_nl_cmd_policy
,
54 .flags
= GENL_ADMIN_PERM
,
56 .cmd
= MT753X_CMD_WRITE
,
57 .doit
= mt753x_nl_response
,
58 .policy
= mt753x_nl_cmd_policy
,
59 .flags
= GENL_ADMIN_PERM
,
63 static struct genl_family mt753x_nl_family
= {
64 // .id = GENL_ID_GENERATE,
65 .name
= MT753X_GENL_NAME
,
66 .version
= MT753X_GENL_VERSION
,
67 .maxattr
= MT753X_NR_ATTR_TYPE
,
69 .n_ops
= ARRAY_SIZE(mt753x_nl_ops
),
72 static int mt753x_nl_list_devs(char *buff
, int size
)
74 struct gsw_mt753x
*gsw
;
78 memset(buff
, 0, size
);
82 list_for_each_entry(gsw
, &mt753x_devs
, list
) {
83 len
= snprintf(buf
, sizeof(buf
),
84 "id: %d, model: %s, node: %s\n",
85 gsw
->id
, gsw
->name
, gsw
->dev
->of_node
->name
);
86 strncat(buff
, buf
, size
- total
);
95 static int mt753x_nl_prepare_reply(struct genl_info
*info
, u8 cmd
,
96 struct sk_buff
**skbp
)
104 msg
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
108 /* Construct send-back message header */
109 reply
= genlmsg_put(msg
, info
->snd_portid
, info
->snd_seq
,
110 &mt753x_nl_family
, 0, cmd
);
120 static int mt753x_nl_send_reply(struct sk_buff
*skb
, struct genl_info
*info
)
122 struct genlmsghdr
*genlhdr
= nlmsg_data(nlmsg_hdr(skb
));
123 void *reply
= genlmsg_data(genlhdr
);
125 /* Finalize a generic netlink message (update message header) */
126 genlmsg_end(skb
, reply
);
128 /* reply to a request */
129 return genlmsg_reply(skb
, info
);
132 static s32
mt753x_nl_get_s32(struct genl_info
*info
, enum mt753x_attr attr
,
137 na
= info
->attrs
[attr
];
139 return nla_get_s32(na
);
144 static int mt753x_nl_get_u32(struct genl_info
*info
, enum mt753x_attr attr
,
149 na
= info
->attrs
[attr
];
151 *val
= nla_get_u32(na
);
158 static struct gsw_mt753x
*mt753x_nl_parse_find_gsw(struct genl_info
*info
)
160 struct gsw_mt753x
*gsw
;
164 na
= info
->attrs
[MT753X_ATTR_TYPE_DEV_ID
];
166 gsw_id
= nla_get_s32(na
);
168 gsw
= mt753x_get_gsw(gsw_id
);
170 gsw
= mt753x_get_first_gsw();
172 gsw
= mt753x_get_first_gsw();
178 static int mt753x_nl_get_swdevs(struct genl_info
*info
, struct gsw_mt753x
*gsw
)
180 struct sk_buff
*rep_skb
= NULL
;
184 ret
= mt753x_nl_list_devs(dev_info
, sizeof(dev_info
));
186 pr_info("No switch registered\n");
190 ret
= mt753x_nl_prepare_reply(info
, MT753X_CMD_REPLY
, &rep_skb
);
194 ret
= nla_put_string(rep_skb
, MT753X_ATTR_TYPE_MESG
, dev_info
);
198 return mt753x_nl_send_reply(rep_skb
, info
);
207 static int mt753x_nl_reply_read(struct genl_info
*info
, struct gsw_mt753x
*gsw
)
209 struct sk_buff
*rep_skb
= NULL
;
213 phy
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_PHY
, -1);
214 devad
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_DEVAD
, -1);
215 reg
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_REG
, -1);
220 ret
= mt753x_nl_prepare_reply(info
, MT753X_CMD_READ
, &rep_skb
);
226 value
= gsw
->mii_read(gsw
, phy
, reg
);
228 value
= gsw
->mmd_read(gsw
, phy
, devad
, reg
);
230 value
= mt753x_reg_read(gsw
, reg
);
233 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_REG
, reg
);
237 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_VAL
, value
);
241 return mt753x_nl_send_reply(rep_skb
, info
);
250 static int mt753x_nl_reply_write(struct genl_info
*info
, struct gsw_mt753x
*gsw
)
252 struct sk_buff
*rep_skb
= NULL
;
257 phy
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_PHY
, -1);
258 devad
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_DEVAD
, -1);
259 reg
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_REG
, -1);
261 if (mt753x_nl_get_u32(info
, MT753X_ATTR_TYPE_VAL
, &value
))
267 ret
= mt753x_nl_prepare_reply(info
, MT753X_CMD_WRITE
, &rep_skb
);
273 gsw
->mii_write(gsw
, phy
, reg
, value
);
275 gsw
->mmd_write(gsw
, phy
, devad
, reg
, value
);
277 mt753x_reg_write(gsw
, reg
, value
);
280 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_REG
, reg
);
284 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_VAL
, value
);
288 return mt753x_nl_send_reply(rep_skb
, info
);
297 static const enum mt753x_attr mt753x_nl_cmd_read_attrs
[] = {
301 static const enum mt753x_attr mt753x_nl_cmd_write_attrs
[] = {
302 MT753X_ATTR_TYPE_REG
,
306 static const struct mt753x_nl_cmd_item mt753x_nl_cmds
[] = {
308 .cmd
= MT753X_CMD_REQUEST
,
309 .require_dev
= false,
310 .process
= mt753x_nl_get_swdevs
312 .cmd
= MT753X_CMD_READ
,
314 .process
= mt753x_nl_reply_read
,
315 MT753X_NL_CMD_REQ_ATTRS(mt753x_nl_cmd_read_attrs
)
317 .cmd
= MT753X_CMD_WRITE
,
319 .process
= mt753x_nl_reply_write
,
320 MT753X_NL_CMD_REQ_ATTRS(mt753x_nl_cmd_write_attrs
)
324 static int mt753x_nl_response(struct sk_buff
*skb
, struct genl_info
*info
)
326 struct genlmsghdr
*hdr
= nlmsg_data(info
->nlhdr
);
327 const struct mt753x_nl_cmd_item
*cmditem
= NULL
;
328 struct gsw_mt753x
*gsw
= NULL
;
329 u32 sat_req_attrs
= 0;
332 for (i
= 0; i
< ARRAY_SIZE(mt753x_nl_cmds
); i
++) {
333 if (hdr
->cmd
== mt753x_nl_cmds
[i
].cmd
) {
334 cmditem
= &mt753x_nl_cmds
[i
];
340 pr_info("mt753x-nl: unknown cmd %u\n", hdr
->cmd
);
344 for (i
= 0; i
< cmditem
->nr_required_attrs
; i
++) {
345 if (info
->attrs
[cmditem
->required_attrs
[i
]])
349 if (sat_req_attrs
!= cmditem
->nr_required_attrs
) {
350 pr_info("mt753x-nl: missing required attr(s) for cmd %u\n",
355 if (cmditem
->require_dev
) {
356 gsw
= mt753x_nl_parse_find_gsw(info
);
358 pr_info("mt753x-nl: failed to find switch dev\n");
363 ret
= cmditem
->process(info
, gsw
);
370 int __init
mt753x_nl_init(void)
374 ret
= genl_register_family(&mt753x_nl_family
);
376 pr_info("mt753x-nl: genl_register_family_with_ops failed\n");
383 void __exit
mt753x_nl_exit(void)
385 genl_unregister_family(&mt753x_nl_family
);