xref: /linux/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c (revision fe8ecccc10b3adc071de05ca7af728ca1a4ac9aa)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Marvell OcteonTx2 RVU Admin Function driver
3  *
4  * Copyright (C) 2018 Marvell International Ltd.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 as
8  * published by the Free Software Foundation.
9  */
10 
11 #include <linux/types.h>
12 #include <linux/module.h>
13 #include <linux/pci.h>
14 
15 #include "rvu.h"
16 #include "cgx.h"
17 
18 struct cgx_evq_entry {
19 	struct list_head evq_node;
20 	struct cgx_link_event link_event;
21 };
22 
23 static inline u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id)
24 {
25 	return ((cgx_id & 0xF) << 4) | (lmac_id & 0xF);
26 }
27 
28 static void *rvu_cgx_pdata(u8 cgx_id, struct rvu *rvu)
29 {
30 	if (cgx_id >= rvu->cgx_cnt)
31 		return NULL;
32 
33 	return rvu->cgx_idmap[cgx_id];
34 }
35 
36 static int rvu_map_cgx_lmac_pf(struct rvu *rvu)
37 {
38 	int cgx_cnt = rvu->cgx_cnt;
39 	int cgx, lmac_cnt, lmac;
40 	int pf = PF_CGXMAP_BASE;
41 	int size;
42 
43 	if (!cgx_cnt)
44 		return 0;
45 
46 	if (cgx_cnt > 0xF || MAX_LMAC_PER_CGX > 0xF)
47 		return -EINVAL;
48 
49 	/* Alloc map table
50 	 * An additional entry is required since PF id starts from 1 and
51 	 * hence entry at offset 0 is invalid.
52 	 */
53 	size = (cgx_cnt * MAX_LMAC_PER_CGX + 1) * sizeof(u8);
54 	rvu->pf2cgxlmac_map = devm_kzalloc(rvu->dev, size, GFP_KERNEL);
55 	if (!rvu->pf2cgxlmac_map)
56 		return -ENOMEM;
57 
58 	/* Initialize offset 0 with an invalid cgx and lmac id */
59 	rvu->pf2cgxlmac_map[0] = 0xFF;
60 
61 	/* Reverse map table */
62 	rvu->cgxlmac2pf_map = devm_kzalloc(rvu->dev,
63 				  cgx_cnt * MAX_LMAC_PER_CGX * sizeof(u16),
64 				  GFP_KERNEL);
65 	if (!rvu->cgxlmac2pf_map)
66 		return -ENOMEM;
67 
68 	rvu->cgx_mapped_pfs = 0;
69 	for (cgx = 0; cgx < cgx_cnt; cgx++) {
70 		lmac_cnt = cgx_get_lmac_cnt(rvu_cgx_pdata(cgx, rvu));
71 		for (lmac = 0; lmac < lmac_cnt; lmac++, pf++) {
72 			rvu->pf2cgxlmac_map[pf] = cgxlmac_id_to_bmap(cgx, lmac);
73 			rvu->cgxlmac2pf_map[CGX_OFFSET(cgx) + lmac] = 1 << pf;
74 			rvu->cgx_mapped_pfs++;
75 		}
76 	}
77 	return 0;
78 }
79 
80 /* This is called from interrupt context and is expected to be atomic */
81 static int cgx_lmac_postevent(struct cgx_link_event *event, void *data)
82 {
83 	struct cgx_evq_entry *qentry;
84 	struct rvu *rvu = data;
85 
86 	/* post event to the event queue */
87 	qentry = kmalloc(sizeof(*qentry), GFP_ATOMIC);
88 	if (!qentry)
89 		return -ENOMEM;
90 	qentry->link_event = *event;
91 	spin_lock(&rvu->cgx_evq_lock);
92 	list_add_tail(&qentry->evq_node, &rvu->cgx_evq_head);
93 	spin_unlock(&rvu->cgx_evq_lock);
94 
95 	/* start worker to process the events */
96 	queue_work(rvu->cgx_evh_wq, &rvu->cgx_evh_work);
97 
98 	return 0;
99 }
100 
101 static void cgx_evhandler_task(struct work_struct *work)
102 {
103 	struct rvu *rvu = container_of(work, struct rvu, cgx_evh_work);
104 	struct cgx_evq_entry *qentry;
105 	struct cgx_link_event *event;
106 	unsigned long flags;
107 
108 	do {
109 		/* Dequeue an event */
110 		spin_lock_irqsave(&rvu->cgx_evq_lock, flags);
111 		qentry = list_first_entry_or_null(&rvu->cgx_evq_head,
112 						  struct cgx_evq_entry,
113 						  evq_node);
114 		if (qentry)
115 			list_del(&qentry->evq_node);
116 		spin_unlock_irqrestore(&rvu->cgx_evq_lock, flags);
117 		if (!qentry)
118 			break; /* nothing more to process */
119 
120 		event = &qentry->link_event;
121 
122 		/* Do nothing for now */
123 		kfree(qentry);
124 	} while (1);
125 }
126 
127 static void cgx_lmac_event_handler_init(struct rvu *rvu)
128 {
129 	struct cgx_event_cb cb;
130 	int cgx, lmac, err;
131 	void *cgxd;
132 
133 	spin_lock_init(&rvu->cgx_evq_lock);
134 	INIT_LIST_HEAD(&rvu->cgx_evq_head);
135 	INIT_WORK(&rvu->cgx_evh_work, cgx_evhandler_task);
136 	rvu->cgx_evh_wq = alloc_workqueue("rvu_evh_wq", 0, 0);
137 	if (!rvu->cgx_evh_wq) {
138 		dev_err(rvu->dev, "alloc workqueue failed");
139 		return;
140 	}
141 
142 	cb.notify_link_chg = cgx_lmac_postevent; /* link change call back */
143 	cb.data = rvu;
144 
145 	for (cgx = 0; cgx < rvu->cgx_cnt; cgx++) {
146 		cgxd = rvu_cgx_pdata(cgx, rvu);
147 		for (lmac = 0; lmac < cgx_get_lmac_cnt(cgxd); lmac++) {
148 			err = cgx_lmac_evh_register(&cb, cgxd, lmac);
149 			if (err)
150 				dev_err(rvu->dev,
151 					"%d:%d handler register failed\n",
152 					cgx, lmac);
153 		}
154 	}
155 }
156 
157 void rvu_cgx_wq_destroy(struct rvu *rvu)
158 {
159 	if (rvu->cgx_evh_wq) {
160 		flush_workqueue(rvu->cgx_evh_wq);
161 		destroy_workqueue(rvu->cgx_evh_wq);
162 		rvu->cgx_evh_wq = NULL;
163 	}
164 }
165 
166 int rvu_cgx_probe(struct rvu *rvu)
167 {
168 	int i, err;
169 
170 	/* find available cgx ports */
171 	rvu->cgx_cnt = cgx_get_cgx_cnt();
172 	if (!rvu->cgx_cnt) {
173 		dev_info(rvu->dev, "No CGX devices found!\n");
174 		return -ENODEV;
175 	}
176 
177 	rvu->cgx_idmap = devm_kzalloc(rvu->dev, rvu->cgx_cnt * sizeof(void *),
178 				      GFP_KERNEL);
179 	if (!rvu->cgx_idmap)
180 		return -ENOMEM;
181 
182 	/* Initialize the cgxdata table */
183 	for (i = 0; i < rvu->cgx_cnt; i++)
184 		rvu->cgx_idmap[i] = cgx_get_pdata(i);
185 
186 	/* Map CGX LMAC interfaces to RVU PFs */
187 	err = rvu_map_cgx_lmac_pf(rvu);
188 	if (err)
189 		return err;
190 
191 	/* Register for CGX events */
192 	cgx_lmac_event_handler_init(rvu);
193 	return 0;
194 }
195