brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · 20bfdf7 Raw
259 lines · c
1// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)2/* Copyright 2017-2019 NXP */3 4#include "enetc.h"5 6int enetc_setup_cbdr(struct device *dev, struct enetc_hw *hw, int bd_count,7		     struct enetc_cbdr *cbdr)8{9	int size = bd_count * sizeof(struct enetc_cbd);10 11	cbdr->bd_base = dma_alloc_coherent(dev, size, &cbdr->bd_dma_base,12					   GFP_KERNEL);13	if (!cbdr->bd_base)14		return -ENOMEM;15 16	/* h/w requires 128B alignment */17	if (!IS_ALIGNED(cbdr->bd_dma_base, 128)) {18		dma_free_coherent(dev, size, cbdr->bd_base,19				  cbdr->bd_dma_base);20		return -EINVAL;21	}22 23	cbdr->next_to_clean = 0;24	cbdr->next_to_use = 0;25	cbdr->dma_dev = dev;26	cbdr->bd_count = bd_count;27 28	cbdr->pir = hw->reg + ENETC_SICBDRPIR;29	cbdr->cir = hw->reg + ENETC_SICBDRCIR;30	cbdr->mr = hw->reg + ENETC_SICBDRMR;31 32	/* set CBDR cache attributes */33	enetc_wr(hw, ENETC_SICAR2,34		 ENETC_SICAR_RD_COHERENT | ENETC_SICAR_WR_COHERENT);35 36	enetc_wr(hw, ENETC_SICBDRBAR0, lower_32_bits(cbdr->bd_dma_base));37	enetc_wr(hw, ENETC_SICBDRBAR1, upper_32_bits(cbdr->bd_dma_base));38	enetc_wr(hw, ENETC_SICBDRLENR, ENETC_RTBLENR_LEN(cbdr->bd_count));39 40	enetc_wr_reg(cbdr->pir, cbdr->next_to_clean);41	enetc_wr_reg(cbdr->cir, cbdr->next_to_use);42	/* enable ring */43	enetc_wr_reg(cbdr->mr, BIT(31));44 45	return 0;46}47EXPORT_SYMBOL_GPL(enetc_setup_cbdr);48 49void enetc_teardown_cbdr(struct enetc_cbdr *cbdr)50{51	int size = cbdr->bd_count * sizeof(struct enetc_cbd);52 53	/* disable ring */54	enetc_wr_reg(cbdr->mr, 0);55 56	dma_free_coherent(cbdr->dma_dev, size, cbdr->bd_base,57			  cbdr->bd_dma_base);58	cbdr->bd_base = NULL;59	cbdr->dma_dev = NULL;60}61EXPORT_SYMBOL_GPL(enetc_teardown_cbdr);62 63static void enetc_clean_cbdr(struct enetc_cbdr *ring)64{65	struct enetc_cbd *dest_cbd;66	int i, status;67 68	i = ring->next_to_clean;69 70	while (enetc_rd_reg(ring->cir) != i) {71		dest_cbd = ENETC_CBD(*ring, i);72		status = dest_cbd->status_flags & ENETC_CBD_STATUS_MASK;73		if (status)74			dev_warn(ring->dma_dev, "CMD err %04x for cmd %04x\n",75				 status, dest_cbd->cmd);76 77		memset(dest_cbd, 0, sizeof(*dest_cbd));78 79		i = (i + 1) % ring->bd_count;80	}81 82	ring->next_to_clean = i;83}84 85static int enetc_cbd_unused(struct enetc_cbdr *r)86{87	return (r->next_to_clean - r->next_to_use - 1 + r->bd_count) %88		r->bd_count;89}90 91int enetc_send_cmd(struct enetc_si *si, struct enetc_cbd *cbd)92{93	struct enetc_cbdr *ring = &si->cbd_ring;94	int timeout = ENETC_CBDR_TIMEOUT;95	struct enetc_cbd *dest_cbd;96	int i;97 98	if (unlikely(!ring->bd_base))99		return -EIO;100 101	if (unlikely(!enetc_cbd_unused(ring)))102		enetc_clean_cbdr(ring);103 104	i = ring->next_to_use;105	dest_cbd = ENETC_CBD(*ring, i);106 107	/* copy command to the ring */108	*dest_cbd = *cbd;109	i = (i + 1) % ring->bd_count;110 111	ring->next_to_use = i;112	/* let H/W know BD ring has been updated */113	enetc_wr_reg(ring->pir, i);114 115	do {116		if (enetc_rd_reg(ring->cir) == i)117			break;118		udelay(10); /* cannot sleep, rtnl_lock() */119		timeout -= 10;120	} while (timeout);121 122	if (!timeout)123		return -EBUSY;124 125	/* CBD may writeback data, feedback up level */126	*cbd = *dest_cbd;127 128	enetc_clean_cbdr(ring);129 130	return 0;131}132EXPORT_SYMBOL_GPL(enetc_send_cmd);133 134int enetc_clear_mac_flt_entry(struct enetc_si *si, int index)135{136	struct enetc_cbd cbd;137 138	memset(&cbd, 0, sizeof(cbd));139 140	cbd.cls = 1;141	cbd.status_flags = ENETC_CBD_FLAGS_SF;142	cbd.index = cpu_to_le16(index);143 144	return enetc_send_cmd(si, &cbd);145}146EXPORT_SYMBOL_GPL(enetc_clear_mac_flt_entry);147 148int enetc_set_mac_flt_entry(struct enetc_si *si, int index,149			    char *mac_addr, int si_map)150{151	struct enetc_cbd cbd;152	u32 upper;153	u16 lower;154 155	memset(&cbd, 0, sizeof(cbd));156 157	/* fill up the "set" descriptor */158	cbd.cls = 1;159	cbd.status_flags = ENETC_CBD_FLAGS_SF;160	cbd.index = cpu_to_le16(index);161	cbd.opt[3] = cpu_to_le32(si_map);162	/* enable entry */163	cbd.opt[0] = cpu_to_le32(BIT(31));164 165	upper = *(const u32 *)mac_addr;166	lower = *(const u16 *)(mac_addr + 4);167	cbd.addr[0] = cpu_to_le32(upper);168	cbd.addr[1] = cpu_to_le32(lower);169 170	return enetc_send_cmd(si, &cbd);171}172EXPORT_SYMBOL_GPL(enetc_set_mac_flt_entry);173 174/* Set entry in RFS table */175int enetc_set_fs_entry(struct enetc_si *si, struct enetc_cmd_rfse *rfse,176		       int index)177{178	struct enetc_cbdr *ring = &si->cbd_ring;179	struct enetc_cbd cbd = {.cmd = 0};180	void *tmp, *tmp_align;181	dma_addr_t dma;182	int err;183 184	/* fill up the "set" descriptor */185	cbd.cmd = 0;186	cbd.cls = 4;187	cbd.index = cpu_to_le16(index);188	cbd.opt[3] = cpu_to_le32(0); /* SI */189 190	tmp = enetc_cbd_alloc_data_mem(si, &cbd, sizeof(*rfse),191				       &dma, &tmp_align);192	if (!tmp)193		return -ENOMEM;194 195	memcpy(tmp_align, rfse, sizeof(*rfse));196 197	err = enetc_send_cmd(si, &cbd);198	if (err)199		dev_err(ring->dma_dev, "FS entry add failed (%d)!", err);200 201	enetc_cbd_free_data_mem(si, sizeof(*rfse), tmp, &dma);202 203	return err;204}205EXPORT_SYMBOL_GPL(enetc_set_fs_entry);206 207static int enetc_cmd_rss_table(struct enetc_si *si, u32 *table, int count,208			       bool read)209{210	struct enetc_cbdr *ring = &si->cbd_ring;211	struct enetc_cbd cbd = {.cmd = 0};212	u8 *tmp, *tmp_align;213	dma_addr_t dma;214	int err, i;215 216	if (count < ENETC_CBD_DATA_MEM_ALIGN)217		/* HW only takes in a full 64 entry table */218		return -EINVAL;219 220	tmp = enetc_cbd_alloc_data_mem(si, &cbd, count,221				       &dma, (void *)&tmp_align);222	if (!tmp)223		return -ENOMEM;224 225	if (!read)226		for (i = 0; i < count; i++)227			tmp_align[i] = (u8)(table[i]);228 229	/* fill up the descriptor */230	cbd.cmd = read ? 2 : 1;231	cbd.cls = 3;232 233	err = enetc_send_cmd(si, &cbd);234	if (err)235		dev_err(ring->dma_dev, "RSS cmd failed (%d)!", err);236 237	if (read)238		for (i = 0; i < count; i++)239			table[i] = tmp_align[i];240 241	enetc_cbd_free_data_mem(si, count, tmp, &dma);242 243	return err;244}245 246/* Get RSS table */247int enetc_get_rss_table(struct enetc_si *si, u32 *table, int count)248{249	return enetc_cmd_rss_table(si, table, count, true);250}251EXPORT_SYMBOL_GPL(enetc_get_rss_table);252 253/* Set RSS table */254int enetc_set_rss_table(struct enetc_si *si, const u32 *table, int count)255{256	return enetc_cmd_rss_table(si, (u32 *)table, count, false);257}258EXPORT_SYMBOL_GPL(enetc_set_rss_table);259