// SPDX-License-Identifier: GPL-2.0
/*
* dim2.c - MediaLB DIM2 Hardware Dependent Module
*
* Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/printk.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/kthread.h>
#include <linux/most.h>
#include <linux/of.h>
#include "hal.h"
#include "errors.h"
#include "sysfs.h"
#define DMA_CHANNELS (32 - 1) /* channel 0 is a system channel */
#define MAX_BUFFERS_PACKET 32
#define MAX_BUFFERS_STREAMING 32
#define MAX_BUF_SIZE_PACKET 2048
#define MAX_BUF_SIZE_STREAMING (8 * 1024)
/*
* The parameter representing the number of frames per sub-buffer for
* synchronous channels. Valid values: [0 .. 6].
*
* The values 0, 1, 2, 3, 4, 5, 6 represent corresponding number of frames per
* sub-buffer 1, 2, 4, 8, 16, 32, 64.
*/
static u8 fcnt = 4; /* (1 << fcnt) frames per subbuffer */
module_param(fcnt, byte, 0000);
MODULE_PARM_DESC(fcnt, "Num of frames per sub-buffer for sync channels as a power of 2");
static DEFINE_SPINLOCK(dim_lock);
/**
* struct hdm_channel - private structure to keep channel specific data
* @name: channel name
* @is_initialized: identifier to know whether the channel is initialized
* @ch: HAL specific channel data
* @reset_dbr_size: reset DBR data buffer size
* @pending_list: list to keep MBO's before starting transfer
* @started_list: list to keep MBO's after starting transfer
* @direction: channel direction (TX or RX)
* @data_type: channel data type
*/
struct hdm_channel {
char name[sizeof "caNNN"];
bool is_initialized;
struct dim_channel ch;
u16 *reset_dbr_size;
struct list_head pending_list; /* before dim_enqueue_buffer() */
struct list_head started_list; /* after dim_enqueue_buffer() */
enum most_channel_direction direction;
enum most_channel_data_type data_type;
};
/*
* struct dim2_hdm - private structure to keep interface specific data
* @hch: an array of channel specific data
* @most_iface: most interface structure
* @capabilities: an array of channel capability data
* @io_base: I/O register base address
* @netinfo_task: thread to deliver network status
* @netinfo_waitq: waitq for the thread to sleep
* @deliver_netinfo: to identify whether network status received
* @mac_addrs: INIC mac address
* @link_state: network link state
* @atx_idx: index of async tx channel
*/
struct dim2_hdm {
struct device dev;
struct hdm_channel hch[DMA_CHANNELS];
struct most_channel_capability capabilities[DMA_CHANNELS];
struct most_interface most_iface;
char name[16 + sizeof "dim2-"];
void __iomem *io_base;
u8 clk_speed;
struct clk *clk;
struct clk *clk_pll;
struct task_struct *netinfo_task;
wait_queue_head_t netinfo_waitq;
int deliver_netinfo;
unsigned char mac_addrs[6];
unsigned char link_state;
int atx_idx;
struct medialb_bus bus;
void (*on_netinfo)(struct most_interface *most_iface,
unsigned char link_state, unsigned char *addrs);
void (*disable_platform)(struct platform_device *pdev);
};
struct dim2_platform_data {
int (*enable)(struct platform_device *pdev);
void (*disable)(struct platform_device *pdev);
u8 fcnt;
};
static inline struct dim2_hdm *iface_to_hdm(struct most_interface *iface)
{
return container_of(iface, struct dim2_hdm, most_iface);
}
/* Identify a network status message */
static bool packet_is_net_info(const u8 *p)
{
return p[1] == 0x18 && p[2] == 0x05 && p[3] == 0x0C &&
p[13] == 0x3C && p[14] == 0x00 && p[15] == 0x0A;
}
static ssize_t state_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
bool state;
unsigned long flags;
spin_lock_irqsave(&dim_lock, flags);
state = dim_get_lock_state();
spin_unlock_irqrestore(&dim_lock, flags);
return sysfs_emit(buf, "%s\n", state ? "locked" : "");
}