Suneel Garapati | 8666ae8 | 2020-08-26 14:37:42 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0 |
| 2 | /* |
| 3 | * Copyright (C) 2018 Marvell International Ltd. |
| 4 | */ |
| 5 | |
| 6 | #include <dm.h> |
| 7 | #include <errno.h> |
| 8 | #include <malloc.h> |
| 9 | #include <misc.h> |
| 10 | #include <net.h> |
| 11 | #include <pci_ids.h> |
| 12 | #include <linux/list.h> |
| 13 | #include <asm/arch/board.h> |
| 14 | #include <asm/arch/csrs/csrs-cgx.h> |
| 15 | #include <asm/io.h> |
| 16 | |
| 17 | #include "cgx.h" |
| 18 | |
| 19 | char lmac_type_to_str[][8] = { |
| 20 | "SGMII", |
| 21 | "XAUI", |
| 22 | "RXAUI", |
| 23 | "10G_R", |
| 24 | "40G_R", |
| 25 | "RGMII", |
| 26 | "QSGMII", |
| 27 | "25G_R", |
| 28 | "50G_R", |
| 29 | "100G_R", |
| 30 | "USXGMII", |
| 31 | }; |
| 32 | |
| 33 | char lmac_speed_to_str[][8] = { |
| 34 | "0", |
| 35 | "10M", |
| 36 | "100M", |
| 37 | "1G", |
| 38 | "2.5G", |
| 39 | "5G", |
| 40 | "10G", |
| 41 | "20G", |
| 42 | "25G", |
| 43 | "40G", |
| 44 | "50G", |
| 45 | "80G", |
| 46 | "100G", |
| 47 | }; |
| 48 | |
| 49 | /** |
| 50 | * Given an LMAC/PF instance number, return the lmac |
| 51 | * Per design, each PF has only one LMAC mapped. |
| 52 | * |
| 53 | * @param instance instance to find |
| 54 | * |
Heinrich Schuchardt | 47b4c02 | 2022-01-19 18:05:50 +0100 | [diff] [blame] | 55 | * Return: pointer to lmac data structure or NULL if not found |
Suneel Garapati | 8666ae8 | 2020-08-26 14:37:42 +0200 | [diff] [blame] | 56 | */ |
| 57 | struct lmac *nix_get_cgx_lmac(int lmac_instance) |
| 58 | { |
| 59 | struct cgx *cgx; |
| 60 | struct udevice *dev; |
| 61 | int i, idx, err; |
| 62 | |
| 63 | for (i = 0; i < CGX_PER_NODE; i++) { |
| 64 | err = dm_pci_find_device(PCI_VENDOR_ID_CAVIUM, |
| 65 | PCI_DEVICE_ID_OCTEONTX2_CGX, i, |
| 66 | &dev); |
| 67 | if (err) |
| 68 | continue; |
| 69 | |
| 70 | cgx = dev_get_priv(dev); |
| 71 | debug("%s udev %p cgx %p instance %d\n", __func__, dev, cgx, |
| 72 | lmac_instance); |
| 73 | for (idx = 0; idx < cgx->lmac_count; idx++) { |
| 74 | if (cgx->lmac[idx]->instance == lmac_instance) |
| 75 | return cgx->lmac[idx]; |
| 76 | } |
| 77 | } |
| 78 | return NULL; |
| 79 | } |
| 80 | |
| 81 | void cgx_lmac_mac_filter_clear(struct lmac *lmac) |
| 82 | { |
| 83 | union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0; |
| 84 | union cgxx_cmr_rx_dmacx_cam0 dmac_cam0; |
| 85 | void *reg_addr; |
| 86 | |
| 87 | dmac_cam0.u = 0x0; |
| 88 | reg_addr = lmac->cgx->reg_base + |
| 89 | CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8); |
| 90 | writeq(dmac_cam0.u, reg_addr); |
| 91 | debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u); |
| 92 | |
| 93 | dmac_ctl0.u = 0x0; |
| 94 | dmac_ctl0.s.bcst_accept = 1; |
| 95 | dmac_ctl0.s.mcst_mode = 1; |
| 96 | dmac_ctl0.s.cam_accept = 0; |
| 97 | reg_addr = lmac->cgx->reg_base + |
| 98 | CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id); |
| 99 | writeq(dmac_ctl0.u, reg_addr); |
| 100 | debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u); |
| 101 | } |
| 102 | |
| 103 | void cgx_lmac_mac_filter_setup(struct lmac *lmac) |
| 104 | { |
| 105 | union cgxx_cmrx_rx_dmac_ctl0 dmac_ctl0; |
| 106 | union cgxx_cmr_rx_dmacx_cam0 dmac_cam0; |
| 107 | u64 mac, tmp; |
| 108 | void *reg_addr; |
| 109 | |
| 110 | memcpy((void *)&tmp, lmac->mac_addr, 6); |
| 111 | debug("%s: tmp %llx\n", __func__, tmp); |
| 112 | debug("%s: swab tmp %llx\n", __func__, swab64(tmp)); |
| 113 | mac = swab64(tmp) >> 16; |
| 114 | debug("%s: mac %llx\n", __func__, mac); |
| 115 | dmac_cam0.u = 0x0; |
| 116 | dmac_cam0.s.id = lmac->lmac_id; |
| 117 | dmac_cam0.s.adr = mac; |
| 118 | dmac_cam0.s.en = 1; |
| 119 | reg_addr = lmac->cgx->reg_base + |
| 120 | CGXX_CMR_RX_DMACX_CAM0(lmac->lmac_id * 8); |
| 121 | writeq(dmac_cam0.u, reg_addr); |
| 122 | debug("%s: reg %p dmac_cam0 %llx\n", __func__, reg_addr, dmac_cam0.u); |
| 123 | dmac_ctl0.u = 0x0; |
| 124 | dmac_ctl0.s.bcst_accept = 1; |
| 125 | dmac_ctl0.s.mcst_mode = 0; |
| 126 | dmac_ctl0.s.cam_accept = 1; |
| 127 | reg_addr = lmac->cgx->reg_base + |
| 128 | CGXX_CMRX_RX_DMAC_CTL0(lmac->lmac_id); |
| 129 | writeq(dmac_ctl0.u, reg_addr); |
| 130 | debug("%s: reg %p dmac_ctl0 %llx\n", __func__, reg_addr, dmac_ctl0.u); |
| 131 | } |
| 132 | |
| 133 | int cgx_lmac_set_pkind(struct lmac *lmac, u8 lmac_id, int pkind) |
| 134 | { |
| 135 | cgx_write(lmac->cgx, lmac_id, CGXX_CMRX_RX_ID_MAP(0), |
| 136 | (pkind & 0x3f)); |
| 137 | return 0; |
| 138 | } |
| 139 | |
| 140 | int cgx_lmac_link_status(struct lmac *lmac, int lmac_id, u64 *status) |
| 141 | { |
| 142 | int ret = 0; |
| 143 | |
| 144 | ret = cgx_intf_get_link_sts(lmac->cgx->cgx_id, lmac_id, status); |
| 145 | if (ret) { |
| 146 | debug("%s request failed for cgx%d lmac%d\n", |
| 147 | __func__, lmac->cgx->cgx_id, lmac->lmac_id); |
| 148 | ret = -1; |
| 149 | } |
| 150 | return ret; |
| 151 | } |
| 152 | |
| 153 | int cgx_lmac_rx_tx_enable(struct lmac *lmac, int lmac_id, bool enable) |
| 154 | { |
| 155 | struct cgx *cgx = lmac->cgx; |
| 156 | union cgxx_cmrx_config cmrx_config; |
| 157 | |
| 158 | if (!cgx || lmac_id >= cgx->lmac_count) |
| 159 | return -ENODEV; |
| 160 | |
| 161 | cmrx_config.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0)); |
| 162 | cmrx_config.s.data_pkt_rx_en = |
| 163 | cmrx_config.s.data_pkt_tx_en = enable ? 1 : 0; |
| 164 | cgx_write(cgx, lmac_id, CGXX_CMRX_CONFIG(0), cmrx_config.u); |
| 165 | return 0; |
| 166 | } |
| 167 | |
| 168 | int cgx_lmac_link_enable(struct lmac *lmac, int lmac_id, bool enable, |
| 169 | u64 *status) |
| 170 | { |
| 171 | int ret = 0; |
| 172 | |
| 173 | ret = cgx_intf_link_up_dwn(lmac->cgx->cgx_id, lmac_id, enable, |
| 174 | status); |
| 175 | if (ret) { |
| 176 | debug("%s request failed for cgx%d lmac%d\n", |
| 177 | __func__, lmac->cgx->cgx_id, lmac->lmac_id); |
| 178 | ret = -1; |
| 179 | } |
| 180 | return ret; |
| 181 | } |
| 182 | |
| 183 | int cgx_lmac_internal_loopback(struct lmac *lmac, int lmac_id, bool enable) |
| 184 | { |
| 185 | struct cgx *cgx = lmac->cgx; |
| 186 | union cgxx_cmrx_config cmrx_cfg; |
| 187 | union cgxx_gmp_pcs_mrx_control mrx_control; |
| 188 | union cgxx_spux_control1 spux_control1; |
| 189 | enum lmac_type lmac_type; |
| 190 | |
| 191 | if (!cgx || lmac_id >= cgx->lmac_count) |
| 192 | return -ENODEV; |
| 193 | |
| 194 | cmrx_cfg.u = cgx_read(cgx, lmac_id, CGXX_CMRX_CONFIG(0)); |
| 195 | lmac_type = cmrx_cfg.s.lmac_type; |
| 196 | if (lmac_type == LMAC_MODE_SGMII || lmac_type == LMAC_MODE_QSGMII) { |
| 197 | mrx_control.u = cgx_read(cgx, lmac_id, |
| 198 | CGXX_GMP_PCS_MRX_CONTROL(0)); |
| 199 | mrx_control.s.loopbck1 = enable ? 1 : 0; |
| 200 | cgx_write(cgx, lmac_id, CGXX_GMP_PCS_MRX_CONTROL(0), |
| 201 | mrx_control.u); |
| 202 | } else { |
| 203 | spux_control1.u = cgx_read(cgx, lmac_id, |
| 204 | CGXX_SPUX_CONTROL1(0)); |
| 205 | spux_control1.s.loopbck = enable ? 1 : 0; |
| 206 | cgx_write(cgx, lmac_id, CGXX_SPUX_CONTROL1(0), |
| 207 | spux_control1.u); |
| 208 | } |
| 209 | return 0; |
| 210 | } |
| 211 | |
| 212 | static int cgx_lmac_init(struct cgx *cgx) |
| 213 | { |
| 214 | struct lmac *lmac; |
| 215 | union cgxx_cmrx_config cmrx_cfg; |
| 216 | static int instance = 1; |
| 217 | int i; |
| 218 | |
| 219 | cgx->lmac_count = cgx_read(cgx, 0, CGXX_CMR_RX_LMACS()); |
| 220 | debug("%s: Found %d lmacs for cgx %d@%p\n", __func__, cgx->lmac_count, |
| 221 | cgx->cgx_id, cgx->reg_base); |
| 222 | |
| 223 | for (i = 0; i < cgx->lmac_count; i++) { |
| 224 | lmac = calloc(1, sizeof(*lmac)); |
| 225 | if (!lmac) |
| 226 | return -ENOMEM; |
| 227 | lmac->instance = instance++; |
| 228 | snprintf(lmac->name, sizeof(lmac->name), "cgx_fwi_%d_%d", |
| 229 | cgx->cgx_id, i); |
| 230 | /* Get LMAC type */ |
| 231 | cmrx_cfg.u = cgx_read(cgx, i, CGXX_CMRX_CONFIG(0)); |
| 232 | lmac->lmac_type = cmrx_cfg.s.lmac_type; |
| 233 | |
| 234 | lmac->lmac_id = i; |
| 235 | lmac->cgx = cgx; |
| 236 | cgx->lmac[i] = lmac; |
| 237 | debug("%s: map id %d to lmac %p (%s), type:%d instance %d\n", |
| 238 | __func__, i, lmac, lmac->name, lmac->lmac_type, |
| 239 | lmac->instance); |
| 240 | lmac->init_pend = 1; |
| 241 | printf("CGX%d LMAC%d [%s]\n", lmac->cgx->cgx_id, |
| 242 | lmac->lmac_id, lmac_type_to_str[lmac->lmac_type]); |
| 243 | octeontx2_board_get_mac_addr((lmac->instance - 1), |
| 244 | lmac->mac_addr); |
| 245 | debug("%s: MAC %pM\n", __func__, lmac->mac_addr); |
| 246 | cgx_lmac_mac_filter_setup(lmac); |
| 247 | } |
| 248 | return 0; |
| 249 | } |
| 250 | |
| 251 | int cgx_probe(struct udevice *dev) |
| 252 | { |
| 253 | struct cgx *cgx = dev_get_priv(dev); |
| 254 | int err; |
| 255 | |
Andrew Scull | 6520c82 | 2022-04-21 16:11:13 +0000 | [diff] [blame] | 256 | cgx->reg_base = dm_pci_map_bar(dev, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, |
Suneel Garapati | 8666ae8 | 2020-08-26 14:37:42 +0200 | [diff] [blame] | 257 | PCI_REGION_MEM); |
| 258 | cgx->dev = dev; |
| 259 | cgx->cgx_id = ((u64)(cgx->reg_base) >> 24) & 0x7; |
| 260 | |
| 261 | debug("%s CGX BAR %p, id: %d\n", __func__, cgx->reg_base, |
| 262 | cgx->cgx_id); |
| 263 | debug("%s CGX %p, udev: %p\n", __func__, cgx, dev); |
| 264 | |
| 265 | err = cgx_lmac_init(cgx); |
| 266 | |
| 267 | return err; |
| 268 | } |
| 269 | |
| 270 | int cgx_remove(struct udevice *dev) |
| 271 | { |
| 272 | struct cgx *cgx = dev_get_priv(dev); |
| 273 | int i; |
| 274 | |
| 275 | debug("%s: cgx remove reg_base %p cgx_id %d", |
| 276 | __func__, cgx->reg_base, cgx->cgx_id); |
| 277 | for (i = 0; i < cgx->lmac_count; i++) |
| 278 | cgx_lmac_mac_filter_clear(cgx->lmac[i]); |
| 279 | |
| 280 | return 0; |
| 281 | } |
| 282 | |
| 283 | U_BOOT_DRIVER(cgx) = { |
| 284 | .name = "cgx", |
| 285 | .id = UCLASS_MISC, |
| 286 | .probe = cgx_probe, |
| 287 | .remove = cgx_remove, |
Simon Glass | 8a2b47f | 2020-12-03 16:55:17 -0700 | [diff] [blame] | 288 | .priv_auto = sizeof(struct cgx), |
Suneel Garapati | 8666ae8 | 2020-08-26 14:37:42 +0200 | [diff] [blame] | 289 | }; |
| 290 | |
| 291 | static struct pci_device_id cgx_supported[] = { |
| 292 | {PCI_VDEVICE(CAVIUM, PCI_DEVICE_ID_CAVIUM_CGX) }, |
| 293 | {} |
| 294 | }; |
| 295 | |
| 296 | U_BOOT_PCI_DEVICE(cgx, cgx_supported); |