#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/socket.h>
#include <sys/sockio.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_var.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_reg.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_hw.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_desc.h>
#include <dev/qcom_ess_edma/qcom_ess_edma_debug.h>
static void
qcom_ess_edma_desc_map_addr(void *arg, bus_dma_segment_t *segs, int nsegs,
int error)
{
if (error != 0)
return;
KASSERT(nsegs == 1, ("too many DMA segments, %d should be 1", nsegs));
*(bus_addr_t *)arg = segs[0].ds_addr;
}
int
qcom_ess_edma_desc_ring_setup(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring,
char *label,
int count,
int sw_desc_size,
int hw_desc_size,
int num_segments,
int buffer_align)
{
int error;
int hw_ring_size;
ring->label = strdup(label, M_TEMP);
if (ring->label == NULL) {
device_printf(sc->sc_dev,
"ERROR: failed to strdup label\n");
error = ENOMEM;
goto error;
}
mtx_init(&ring->mtx, ring->label, NULL, MTX_DEF);
hw_ring_size = count * hw_desc_size;
hw_ring_size = ((hw_ring_size + PAGE_SIZE) / PAGE_SIZE) * PAGE_SIZE;
ring->ring_align = EDMA_DESC_RING_ALIGN;
error = bus_dma_tag_create(
sc->sc_dma_tag,
EDMA_DESC_RING_ALIGN, 0,
BUS_SPACE_MAXADDR,
BUS_SPACE_MAXADDR,
NULL, NULL,
hw_ring_size,
1,
hw_ring_size,
0,
NULL, NULL,
&ring->hw_ring_dma_tag);
if (error != 0) {
device_printf(sc->sc_dev,
"ERROR: failed to create descriptor DMA tag (%d)\n",
error);
goto error;
}
ring->buffer_align = buffer_align;
error = bus_dma_tag_create(
sc->sc_dma_tag,
buffer_align, 0,
BUS_SPACE_MAXADDR,
BUS_SPACE_MAXADDR,
NULL, NULL,
EDMA_DESC_MAX_BUFFER_SIZE * num_segments,
num_segments,
EDMA_DESC_MAX_BUFFER_SIZE,
0,
NULL, NULL,
&ring->buffer_dma_tag);
if (error != 0) {
device_printf(sc->sc_dev,
"ERROR: failed to create buffer DMA tag (%d)\n",
error);
goto error;
}
ring->sw_desc = mallocarray(count, sw_desc_size, M_TEMP,
M_NOWAIT | M_ZERO);
if (ring->sw_desc == NULL) {
device_printf(sc->sc_dev,
"ERROR: failed to allocate sw_desc\n");
goto error;
}
error = bus_dmamem_alloc(ring->hw_ring_dma_tag,
(void **)&ring->hw_desc,
BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
&ring->hw_desc_map);
if (error != 0) {
device_printf(sc->sc_dev,
"failed to allocate DMA'able memory for hw_desc ring\n");
goto error;
}
ring->hw_desc_paddr = 0;
error = bus_dmamap_load(ring->hw_ring_dma_tag, ring->hw_desc_map,
ring->hw_desc, hw_ring_size, qcom_ess_edma_desc_map_addr,
&ring->hw_desc_paddr, BUS_DMA_NOWAIT);
bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
BUS_DMASYNC_PREWRITE);
QCOM_ESS_EDMA_DPRINTF(sc, QCOM_ESS_EDMA_DBG_DESCRIPTOR_SETUP,
"%s: PADDR=0x%08lx\n", __func__, ring->hw_desc_paddr);
ring->hw_entry_size = hw_desc_size;
ring->sw_entry_size = sw_desc_size;
ring->ring_count = count;
return (0);
error:
mtx_destroy(&ring->mtx);
if (ring->label != NULL)
free(ring->label, M_TEMP);
if (ring->hw_desc != NULL) {
bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(ring->hw_ring_dma_tag, ring->hw_desc_map);
bus_dmamem_free(ring->hw_ring_dma_tag, ring->hw_desc,
ring->hw_desc_map);
ring->hw_desc = NULL;
}
if (ring->sw_desc != NULL) {
free(ring->sw_desc, M_TEMP);
ring->sw_desc = NULL;
}
if (ring->hw_ring_dma_tag != NULL) {
bus_dma_tag_destroy(ring->hw_ring_dma_tag);
ring->hw_ring_dma_tag = NULL;
}
if (ring->buffer_dma_tag != NULL) {
bus_dma_tag_destroy(ring->buffer_dma_tag);
ring->buffer_dma_tag = NULL;
}
return (error);
}
int
qcom_ess_edma_desc_ring_free(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring)
{
mtx_destroy(&ring->mtx);
if (ring->label != NULL)
free(ring->label, M_TEMP);
if (ring->hw_desc != NULL) {
bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(ring->hw_ring_dma_tag, ring->hw_desc_map);
bus_dmamem_free(ring->hw_ring_dma_tag, ring->hw_desc,
ring->hw_desc_map);
ring->hw_desc = NULL;
}
if (ring->sw_desc != NULL) {
free(ring->sw_desc, M_TEMP);
ring->sw_desc = NULL;
}
if (ring->hw_ring_dma_tag != NULL) {
bus_dma_tag_destroy(ring->hw_ring_dma_tag);
ring->hw_ring_dma_tag = NULL;
}
if (ring->buffer_dma_tag != NULL) {
bus_dma_tag_destroy(ring->buffer_dma_tag);
ring->buffer_dma_tag = NULL;
}
return (0);
}
void *
qcom_ess_edma_desc_ring_get_sw_desc(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring, uint16_t index)
{
char *p;
if (index >= ring->ring_count)
return (NULL);
p = (char *) ring->sw_desc;
return (void *) (p + (ring->sw_entry_size * index));
}
void *
qcom_ess_edma_desc_ring_get_hw_desc(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring, uint16_t index)
{
char *p;
if (index >= ring->ring_count)
return (NULL);
p = (char *) ring->hw_desc;
return (void *) (p + (ring->hw_entry_size * index));
}
int
qcom_ess_edma_desc_ring_flush_preupdate(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring)
{
bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
BUS_DMASYNC_PREWRITE);
return (0);
}
int
qcom_ess_edma_desc_ring_flush_postupdate(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring)
{
bus_dmamap_sync(ring->hw_ring_dma_tag, ring->hw_desc_map,
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
return (0);
}
int
qcom_ess_edma_desc_ring_get_num_available(struct qcom_ess_edma_softc *sc,
struct qcom_ess_edma_desc_ring *ring)
{
uint16_t sw_next_to_fill;
uint16_t sw_next_to_clean;
uint16_t count = 0;
sw_next_to_clean = ring->next_to_clean;
sw_next_to_fill = ring->next_to_fill;
if (sw_next_to_clean <= sw_next_to_fill)
count = ring->ring_count;
return (count + sw_next_to_clean - sw_next_to_fill - 1);
}