162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
262306a36Sopenharmony_ci/* Copyright 2021 NXP
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * The Integrated Endpoint Register Block (IERB) is configured by pre-boot
562306a36Sopenharmony_ci * software and is supposed to be to ENETC what a NVRAM is to a 'real' PCIe
662306a36Sopenharmony_ci * card. Upon FLR, values from the IERB are transferred to the ENETC PFs, and
762306a36Sopenharmony_ci * are read-only in the PF memory space.
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * This driver fixes up the power-on reset values for the ENETC shared FIFO,
1062306a36Sopenharmony_ci * such that the TX and RX allocations are sufficient for jumbo frames, and
1162306a36Sopenharmony_ci * that intelligent FIFO dropping is enabled before the internal data
1262306a36Sopenharmony_ci * structures are corrupted.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Even though not all ports might be used on a given board, we are not
1562306a36Sopenharmony_ci * concerned with partitioning the FIFO, because the default values configure
1662306a36Sopenharmony_ci * no strict reservations, so the entire FIFO can be used by the RX of a single
1762306a36Sopenharmony_ci * port, or the TX of a single port.
1862306a36Sopenharmony_ci */
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include <linux/io.h>
2162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/pci.h>
2462306a36Sopenharmony_ci#include <linux/platform_device.h>
2562306a36Sopenharmony_ci#include "enetc.h"
2662306a36Sopenharmony_ci#include "enetc_ierb.h"
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* IERB registers */
2962306a36Sopenharmony_ci#define ENETC_IERB_TXMBAR(port)			(((port) * 0x100) + 0x8080)
3062306a36Sopenharmony_ci#define ENETC_IERB_RXMBER(port)			(((port) * 0x100) + 0x8090)
3162306a36Sopenharmony_ci#define ENETC_IERB_RXMBLR(port)			(((port) * 0x100) + 0x8094)
3262306a36Sopenharmony_ci#define ENETC_IERB_RXBCR(port)			(((port) * 0x100) + 0x80a0)
3362306a36Sopenharmony_ci#define ENETC_IERB_TXBCR(port)			(((port) * 0x100) + 0x80a8)
3462306a36Sopenharmony_ci#define ENETC_IERB_FMBDTR			0xa000
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define ENETC_RESERVED_FOR_ICM			1024
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct enetc_ierb {
3962306a36Sopenharmony_ci	void __iomem *regs;
4062306a36Sopenharmony_ci};
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void enetc_ierb_write(struct enetc_ierb *ierb, u32 offset, u32 val)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	iowrite32(val, ierb->regs + offset);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ciint enetc_ierb_register_pf(struct platform_device *pdev,
4862306a36Sopenharmony_ci			   struct pci_dev *pf_pdev)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct enetc_ierb *ierb = platform_get_drvdata(pdev);
5162306a36Sopenharmony_ci	int port = enetc_pf_to_port(pf_pdev);
5262306a36Sopenharmony_ci	u16 tx_credit, rx_credit, tx_alloc;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (port < 0)
5562306a36Sopenharmony_ci		return -ENODEV;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	if (!ierb)
5862306a36Sopenharmony_ci		return -EPROBE_DEFER;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* By default, it is recommended to set the Host Transfer Agent
6162306a36Sopenharmony_ci	 * per port transmit byte credit to "1000 + max_frame_size/2".
6262306a36Sopenharmony_ci	 * The power-on reset value (1800 bytes) is rounded up to the nearest
6362306a36Sopenharmony_ci	 * 100 assuming a maximum frame size of 1536 bytes.
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	tx_credit = roundup(1000 + ENETC_MAC_MAXFRM_SIZE / 2, 100);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	/* Internal memory allocated for transmit buffering is guaranteed but
6862306a36Sopenharmony_ci	 * not reserved; i.e. if the total transmit allocation is not used,
6962306a36Sopenharmony_ci	 * then the unused portion is not left idle, it can be used for receive
7062306a36Sopenharmony_ci	 * buffering but it will be reclaimed, if required, from receive by
7162306a36Sopenharmony_ci	 * intelligently dropping already stored receive frames in the internal
7262306a36Sopenharmony_ci	 * memory to ensure that the transmit allocation is respected.
7362306a36Sopenharmony_ci	 *
7462306a36Sopenharmony_ci	 * PaTXMBAR must be set to a value larger than
7562306a36Sopenharmony_ci	 *     PaTXBCR + 2 * max_frame_size + 32
7662306a36Sopenharmony_ci	 * if frame preemption is not enabled, or to
7762306a36Sopenharmony_ci	 *     2 * PaTXBCR + 2 * p_max_frame_size (pMAC maximum frame size) +
7862306a36Sopenharmony_ci	 *     2 * np_max_frame_size (eMAC maximum frame size) + 64
7962306a36Sopenharmony_ci	 * if frame preemption is enabled.
8062306a36Sopenharmony_ci	 */
8162306a36Sopenharmony_ci	tx_alloc = roundup(2 * tx_credit + 4 * ENETC_MAC_MAXFRM_SIZE + 64, 16);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	/* Initial credits, in units of 8 bytes, to the Ingress Congestion
8462306a36Sopenharmony_ci	 * Manager for the maximum amount of bytes the port is allocated for
8562306a36Sopenharmony_ci	 * pending traffic.
8662306a36Sopenharmony_ci	 * It is recommended to set the initial credits to 2 times the maximum
8762306a36Sopenharmony_ci	 * frame size (2 frames of maximum size).
8862306a36Sopenharmony_ci	 */
8962306a36Sopenharmony_ci	rx_credit = DIV_ROUND_UP(ENETC_MAC_MAXFRM_SIZE * 2, 8);
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	enetc_ierb_write(ierb, ENETC_IERB_TXBCR(port), tx_credit);
9262306a36Sopenharmony_ci	enetc_ierb_write(ierb, ENETC_IERB_TXMBAR(port), tx_alloc);
9362306a36Sopenharmony_ci	enetc_ierb_write(ierb, ENETC_IERB_RXBCR(port), rx_credit);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ciEXPORT_SYMBOL(enetc_ierb_register_pf);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic int enetc_ierb_probe(struct platform_device *pdev)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct enetc_ierb *ierb;
10262306a36Sopenharmony_ci	void __iomem *regs;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	ierb = devm_kzalloc(&pdev->dev, sizeof(*ierb), GFP_KERNEL);
10562306a36Sopenharmony_ci	if (!ierb)
10662306a36Sopenharmony_ci		return -ENOMEM;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
10962306a36Sopenharmony_ci	if (IS_ERR(regs))
11062306a36Sopenharmony_ci		return PTR_ERR(regs);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	ierb->regs = regs;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	/* Free buffer depletion threshold in bytes.
11562306a36Sopenharmony_ci	 * This sets the minimum amount of free buffer memory that should be
11662306a36Sopenharmony_ci	 * maintained in the datapath sub system, and when the amount of free
11762306a36Sopenharmony_ci	 * buffer memory falls below this threshold, a depletion indication is
11862306a36Sopenharmony_ci	 * asserted, which may trigger "intelligent drop" frame releases from
11962306a36Sopenharmony_ci	 * the ingress queues in the ICM.
12062306a36Sopenharmony_ci	 * It is recommended to set the free buffer depletion threshold to 1024
12162306a36Sopenharmony_ci	 * bytes, since the ICM needs some FIFO memory for its own use.
12262306a36Sopenharmony_ci	 */
12362306a36Sopenharmony_ci	enetc_ierb_write(ierb, ENETC_IERB_FMBDTR, ENETC_RESERVED_FOR_ICM);
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	platform_set_drvdata(pdev, ierb);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic const struct of_device_id enetc_ierb_match[] = {
13162306a36Sopenharmony_ci	{ .compatible = "fsl,ls1028a-enetc-ierb", },
13262306a36Sopenharmony_ci	{},
13362306a36Sopenharmony_ci};
13462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, enetc_ierb_match);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_cistatic struct platform_driver enetc_ierb_driver = {
13762306a36Sopenharmony_ci	.driver = {
13862306a36Sopenharmony_ci		.name = "fsl-enetc-ierb",
13962306a36Sopenharmony_ci		.of_match_table = enetc_ierb_match,
14062306a36Sopenharmony_ci	},
14162306a36Sopenharmony_ci	.probe = enetc_ierb_probe,
14262306a36Sopenharmony_ci};
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cimodule_platform_driver(enetc_ierb_driver);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ciMODULE_DESCRIPTION("NXP ENETC IERB");
14762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL");
148