// 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) … #include <linux/module.h> #include <linux/printk.h> #include <linux/kernel.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/sched.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 … #define MAX_BUFFERS_PACKET … #define MAX_BUFFERS_STREAMING … #define MAX_BUF_SIZE_PACKET … #define MAX_BUF_SIZE_STREAMING … /* * 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 = …; /* (1 << fcnt) frames per subbuffer */ module_param(fcnt, byte, 0000); MODULE_PARM_DESC(…) …; 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 { … }; /* * 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 dim2_platform_data { … }; static inline struct dim2_hdm *iface_to_hdm(struct most_interface *iface) { … } /* Macro to identify a network status message */ #define PACKET_IS_NET_INFO(p) … static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { … } static DEVICE_ATTR_RO(state); static struct attribute *dim2_attrs[] = …; ATTRIBUTE_GROUPS(…); /** * dimcb_on_error - callback from HAL to report miscommunication between * HDM and HAL * @error_id: Error ID * @error_message: Error message. Some text in a free format */ void dimcb_on_error(u8 error_id, const char *error_message) { … } /** * try_start_dim_transfer - try to transfer a buffer on a channel * @hdm_ch: channel specific data * * Transfer a buffer from pending_list if the channel is ready */ static int try_start_dim_transfer(struct hdm_channel *hdm_ch) { … } /** * deliver_netinfo_thread - thread to deliver network status to mostcore * @data: private data * * Wait for network status and deliver it to mostcore once it is received */ static int deliver_netinfo_thread(void *data) { … } /** * retrieve_netinfo - retrieve network status from received buffer * @dev: private data * @mbo: received MBO * * Parse the message in buffer and get node address, link state, MAC address. * Wake up a thread to deliver this status to mostcore */ static void retrieve_netinfo(struct dim2_hdm *dev, struct mbo *mbo) { … } /** * service_done_flag - handle completed buffers * @dev: private data * @ch_idx: channel index * * Return back the completed buffers to mostcore, using completion callback */ static void service_done_flag(struct dim2_hdm *dev, int ch_idx) { … } static struct dim_channel **get_active_channels(struct dim2_hdm *dev, struct dim_channel **buffer) { … } static irqreturn_t dim2_mlb_isr(int irq, void *_dev) { … } static irqreturn_t dim2_task_irq(int irq, void *_dev) { … } /** * dim2_ahb_isr - interrupt service routine * @irq: irq number * @_dev: private data * * Acknowledge the interrupt and service each initialized channel, * if needed, in task context. */ static irqreturn_t dim2_ahb_isr(int irq, void *_dev) { … } /** * complete_all_mbos - complete MBO's in a list * @head: list head * * Delete all the entries in list and return back MBO's to mostcore using * completion call back. */ static void complete_all_mbos(struct list_head *head) { … } /** * configure_channel - initialize a channel * @most_iface: interface the channel belongs to * @ch_idx: channel index to be configured * @ccfg: structure that holds the configuration information * * Receives configuration information from mostcore and initialize * the corresponding channel. Return 0 on success, negative on failure. */ static int configure_channel(struct most_interface *most_iface, int ch_idx, struct most_channel_config *ccfg) { … } /** * enqueue - enqueue a buffer for data transfer * @most_iface: intended interface * @ch_idx: ID of the channel the buffer is intended for * @mbo: pointer to the buffer object * * Push the buffer into pending_list and try to transfer one buffer from * pending_list. Return 0 on success, negative on failure. */ static int enqueue(struct most_interface *most_iface, int ch_idx, struct mbo *mbo) { … } /** * request_netinfo - triggers retrieving of network info * @most_iface: pointer to the interface * @ch_idx: corresponding channel ID * @on_netinfo: call-back used to deliver network status to mostcore * * Send a command to INIC which triggers retrieving of network info by means of * "Message exchange over MDP/MEP". Return 0 on success, negative on failure. */ static void request_netinfo(struct most_interface *most_iface, int ch_idx, void (*on_netinfo)(struct most_interface *, unsigned char, unsigned char *)) { … } /** * poison_channel - poison buffers of a channel * @most_iface: pointer to the interface the channel to be poisoned belongs to * @ch_idx: corresponding channel ID * * Destroy a channel and complete all the buffers in both started_list & * pending_list. Return 0 on success, negative on failure. */ static int poison_channel(struct most_interface *most_iface, int ch_idx) { … } static void *dma_alloc(struct mbo *mbo, u32 size) { … } static void dma_free(struct mbo *mbo, u32 size) { … } static const struct of_device_id dim2_of_match[]; static struct { … } clk_mt[] = …; /** * get_dim2_clk_speed - converts string to DIM2 clock speed value * * @clock_speed: string in the format "{NUMBER}fs" * @val: pointer to get one of the CLK_{NUMBER}FS values * * By success stores one of the CLK_{NUMBER}FS in the *val and returns 0, * otherwise returns -EINVAL. */ static int get_dim2_clk_speed(const char *clock_speed, u8 *val) { … } static void dim2_release(struct device *d) { … } /* * dim2_probe - dim2 probe handler * @pdev: platform device structure * * Register the dim2 interface with mostcore and initialize it. * Return 0 on success, negative on failure. */ static int dim2_probe(struct platform_device *pdev) { … } /** * dim2_remove - dim2 remove handler * @pdev: platform device structure * * Unregister the interface from mostcore */ static void dim2_remove(struct platform_device *pdev) { … } /* platform specific functions [[ */ static int fsl_mx6_enable(struct platform_device *pdev) { … } static void fsl_mx6_disable(struct platform_device *pdev) { … } static int rcar_gen2_enable(struct platform_device *pdev) { … } static void rcar_gen2_disable(struct platform_device *pdev) { … } static int rcar_gen3_enable(struct platform_device *pdev) { … } static void rcar_gen3_disable(struct platform_device *pdev) { … } /* ]] platform specific functions */ enum dim2_platforms { … }; static struct dim2_platform_data plat_data[] = …; static const struct of_device_id dim2_of_match[] = …; MODULE_DEVICE_TABLE(of, dim2_of_match); static struct platform_driver dim2_driver = …; module_platform_driver(…) …; MODULE_AUTHOR(…) …; MODULE_DESCRIPTION(…) …; MODULE_LICENSE(…) …;