From: Christian Gromm Date: Tue, 21 Nov 2017 14:04:38 +0000 (+0100) Subject: staging: most: dim2: rename module X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=9249c6a6d3560f09cddd04e2fda856f237b64987;p=openwrt%2Fstaging%2Fblogic.git staging: most: dim2: rename module This patch renames the folder and source files of the dim2 module. It is needed to clear the directory layout of the driver. Signed-off-by: Christian Gromm Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/most/Kconfig b/drivers/staging/most/Kconfig index b3393a5f5706..88a415b8d07a 100644 --- a/drivers/staging/most/Kconfig +++ b/drivers/staging/most/Kconfig @@ -25,7 +25,7 @@ source "drivers/staging/most/aim-sound/Kconfig" source "drivers/staging/most/aim-v4l2/Kconfig" -source "drivers/staging/most/hdm-dim2/Kconfig" +source "drivers/staging/most/dim2/Kconfig" source "drivers/staging/most/i2c/Kconfig" diff --git a/drivers/staging/most/dim2/Kconfig b/drivers/staging/most/dim2/Kconfig new file mode 100644 index 000000000000..e39c4e525cac --- /dev/null +++ b/drivers/staging/most/dim2/Kconfig @@ -0,0 +1,16 @@ +# +# MediaLB configuration +# + +config MOST_DIM2 + tristate "DIM2" + depends on HAS_IOMEM + + ---help--- + Say Y here if you want to connect via MediaLB to network transceiver. + This device driver is platform dependent and needs an additional + platform driver to be installed. For more information contact + maintainer of this driver. + + To compile this driver as a module, choose M here: the + module will be called most_dim2. diff --git a/drivers/staging/most/dim2/Makefile b/drivers/staging/most/dim2/Makefile new file mode 100644 index 000000000000..66676f5907ee --- /dev/null +++ b/drivers/staging/most/dim2/Makefile @@ -0,0 +1,4 @@ +obj-$(CONFIG_MOST_DIM2) += most_dim2.o + +most_dim2-objs := dim2.o hal.o sysfs.o +ccflags-y += -Idrivers/staging/ diff --git a/drivers/staging/most/dim2/dim2.c b/drivers/staging/most/dim2/dim2.c new file mode 100644 index 000000000000..921db9880d80 --- /dev/null +++ b/drivers/staging/most/dim2/dim2.c @@ -0,0 +1,912 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "most/core.h" +#include "hal.h" +#include "dim2.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) + +/* command line parameter to select clock speed */ +static char *clock_speed; +module_param(clock_speed, charp, 0000); +MODULE_PARM_DESC(clock_speed, "MediaLB Clock Speed"); + +/* + * 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); + +static void dim2_tasklet_fn(unsigned long data); +static DECLARE_TASKLET(dim2_tasklet, dim2_tasklet_fn, 0); + +/** + * struct hdm_channel - private structure to keep channel specific data + * @is_initialized: identifier to know whether the channel is initialized + * @ch: HAL specific channel data + * @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; + 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 + * @clk_speed: user selectable (through command line parameter) clock speed + * @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 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; + int clk_speed; + 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 *, + unsigned char, unsigned char *); +}; + +#define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface) + +/* Macro to identify a network status message */ +#define PACKET_IS_NET_INFO(p) \ + (((p)[1] == 0x18) && ((p)[2] == 0x05) && ((p)[3] == 0x0C) && \ + ((p)[13] == 0x3C) && ((p)[14] == 0x00) && ((p)[15] == 0x0A)) + +bool dim2_sysfs_get_state_cb(void) +{ + bool state; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + state = dim_get_lock_state(); + spin_unlock_irqrestore(&dim_lock, flags); + + return state; +} + +/** + * dimcb_io_read - callback from HAL to read an I/O register + * @ptr32: register address + */ +u32 dimcb_io_read(u32 __iomem *ptr32) +{ + return readl(ptr32); +} + +/** + * dimcb_io_write - callback from HAL to write value to an I/O register + * @ptr32: register address + * @value: value to write + */ +void dimcb_io_write(u32 __iomem *ptr32, u32 value) +{ + writel(value, ptr32); +} + +/** + * 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) +{ + pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id, + error_message); +} + +/** + * startup_dim - initialize the dim2 interface + * @pdev: platform device + * + * Get the value of command line parameter "clock_speed" if given or use the + * default value, enable the clock and PLL, and initialize the dim2 interface. + */ +static int startup_dim(struct platform_device *pdev) +{ + struct dim2_hdm *dev = platform_get_drvdata(pdev); + struct dim2_platform_data *pdata = pdev->dev.platform_data; + u8 hal_ret; + + dev->clk_speed = -1; + + if (clock_speed) { + if (!strcmp(clock_speed, "256fs")) + dev->clk_speed = CLK_256FS; + else if (!strcmp(clock_speed, "512fs")) + dev->clk_speed = CLK_512FS; + else if (!strcmp(clock_speed, "1024fs")) + dev->clk_speed = CLK_1024FS; + else if (!strcmp(clock_speed, "2048fs")) + dev->clk_speed = CLK_2048FS; + else if (!strcmp(clock_speed, "3072fs")) + dev->clk_speed = CLK_3072FS; + else if (!strcmp(clock_speed, "4096fs")) + dev->clk_speed = CLK_4096FS; + else if (!strcmp(clock_speed, "6144fs")) + dev->clk_speed = CLK_6144FS; + else if (!strcmp(clock_speed, "8192fs")) + dev->clk_speed = CLK_8192FS; + } + + if (dev->clk_speed == -1) { + pr_info("Bad or missing clock speed parameter, using default value: 3072fs\n"); + dev->clk_speed = CLK_3072FS; + } else { + pr_info("Selected clock speed: %s\n", clock_speed); + } + if (pdata && pdata->init) { + int ret = pdata->init(pdata, dev->io_base, dev->clk_speed); + + if (ret) + return ret; + } + + pr_info("sync: num of frames per sub-buffer: %u\n", fcnt); + hal_ret = dim_startup(dev->io_base, dev->clk_speed, fcnt); + if (hal_ret != DIM_NO_ERROR) { + pr_err("dim_startup failed: %d\n", hal_ret); + if (pdata && pdata->destroy) + pdata->destroy(pdata); + return -ENODEV; + } + + return 0; +} + +/** + * 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) +{ + u16 buf_size; + struct list_head *head = &hdm_ch->pending_list; + struct mbo *mbo; + unsigned long flags; + struct dim_ch_state_t st; + + BUG_ON(!hdm_ch); + BUG_ON(!hdm_ch->is_initialized); + + spin_lock_irqsave(&dim_lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EAGAIN; + } + + if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EAGAIN; + } + + mbo = list_first_entry(head, struct mbo, list); + buf_size = mbo->buffer_length; + + if (dim_dbr_space(&hdm_ch->ch) < buf_size) { + spin_unlock_irqrestore(&dim_lock, flags); + return -EAGAIN; + } + + BUG_ON(mbo->bus_address == 0); + if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) { + list_del(head->next); + spin_unlock_irqrestore(&dim_lock, flags); + mbo->processed_length = 0; + mbo->status = MBO_E_INVAL; + mbo->complete(mbo); + return -EFAULT; + } + + list_move_tail(head->next, &hdm_ch->started_list); + spin_unlock_irqrestore(&dim_lock, flags); + + return 0; +} + +/** + * 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) +{ + struct dim2_hdm *dev = data; + + while (!kthread_should_stop()) { + wait_event_interruptible(dev->netinfo_waitq, + dev->deliver_netinfo || + kthread_should_stop()); + + if (dev->deliver_netinfo) { + dev->deliver_netinfo--; + if (dev->on_netinfo) { + dev->on_netinfo(&dev->most_iface, + dev->link_state, + dev->mac_addrs); + } + } + } + + return 0; +} + +/** + * 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) +{ + u8 *data = mbo->virt_address; + + pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]); + dev->link_state = data[18]; + pr_info("NIState: %d\n", dev->link_state); + memcpy(dev->mac_addrs, data + 19, 6); + dev->deliver_netinfo++; + wake_up_interruptible(&dev->netinfo_waitq); +} + +/** + * 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) +{ + struct hdm_channel *hdm_ch = dev->hch + ch_idx; + struct dim_ch_state_t st; + struct list_head *head; + struct mbo *mbo; + int done_buffers; + unsigned long flags; + u8 *data; + + BUG_ON(!hdm_ch); + BUG_ON(!hdm_ch->is_initialized); + + spin_lock_irqsave(&dim_lock, flags); + + done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers; + if (!done_buffers) { + spin_unlock_irqrestore(&dim_lock, flags); + return; + } + + if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) { + spin_unlock_irqrestore(&dim_lock, flags); + return; + } + spin_unlock_irqrestore(&dim_lock, flags); + + head = &hdm_ch->started_list; + + while (done_buffers) { + spin_lock_irqsave(&dim_lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(&dim_lock, flags); + pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n"); + break; + } + + mbo = list_first_entry(head, struct mbo, list); + list_del(head->next); + spin_unlock_irqrestore(&dim_lock, flags); + + data = mbo->virt_address; + + if (hdm_ch->data_type == MOST_CH_ASYNC && + hdm_ch->direction == MOST_CH_RX && + PACKET_IS_NET_INFO(data)) { + retrieve_netinfo(dev, mbo); + + spin_lock_irqsave(&dim_lock, flags); + list_add_tail(&mbo->list, &hdm_ch->pending_list); + spin_unlock_irqrestore(&dim_lock, flags); + } else { + if (hdm_ch->data_type == MOST_CH_CONTROL || + hdm_ch->data_type == MOST_CH_ASYNC) { + u32 const data_size = + (u32)data[0] * 256 + data[1] + 2; + + mbo->processed_length = + min_t(u32, data_size, + mbo->buffer_length); + } else { + mbo->processed_length = mbo->buffer_length; + } + mbo->status = MBO_SUCCESS; + mbo->complete(mbo); + } + + done_buffers--; + } +} + +static struct dim_channel **get_active_channels(struct dim2_hdm *dev, + struct dim_channel **buffer) +{ + int idx = 0; + int ch_idx; + + for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { + if (dev->hch[ch_idx].is_initialized) + buffer[idx++] = &dev->hch[ch_idx].ch; + } + buffer[idx++] = NULL; + + return buffer; +} + +static irqreturn_t dim2_mlb_isr(int irq, void *_dev) +{ + struct dim2_hdm *dev = _dev; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + dim_service_mlb_int_irq(); + spin_unlock_irqrestore(&dim_lock, flags); + + if (dev->atx_idx >= 0 && dev->hch[dev->atx_idx].is_initialized) + while (!try_start_dim_transfer(dev->hch + dev->atx_idx)) + continue; + + return IRQ_HANDLED; +} + +/** + * dim2_tasklet_fn - tasklet function + * @data: private data + * + * Service each initialized channel, if needed + */ +static void dim2_tasklet_fn(unsigned long data) +{ + struct dim2_hdm *dev = (struct dim2_hdm *)data; + unsigned long flags; + int ch_idx; + + for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { + if (!dev->hch[ch_idx].is_initialized) + continue; + + spin_lock_irqsave(&dim_lock, flags); + dim_service_channel(&dev->hch[ch_idx].ch); + spin_unlock_irqrestore(&dim_lock, flags); + + service_done_flag(dev, ch_idx); + while (!try_start_dim_transfer(dev->hch + ch_idx)) + continue; + } +} + +/** + * dim2_ahb_isr - interrupt service routine + * @irq: irq number + * @_dev: private data + * + * Acknowledge the interrupt and schedule a tasklet to service channels. + * Return IRQ_HANDLED. + */ +static irqreturn_t dim2_ahb_isr(int irq, void *_dev) +{ + struct dim2_hdm *dev = _dev; + struct dim_channel *buffer[DMA_CHANNELS + 1]; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + dim_service_ahb_int_irq(get_active_channels(dev, buffer)); + spin_unlock_irqrestore(&dim_lock, flags); + + dim2_tasklet.data = (unsigned long)dev; + tasklet_schedule(&dim2_tasklet); + return IRQ_HANDLED; +} + +/** + * 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) +{ + unsigned long flags; + struct mbo *mbo; + + for (;;) { + spin_lock_irqsave(&dim_lock, flags); + if (list_empty(head)) { + spin_unlock_irqrestore(&dim_lock, flags); + break; + } + + mbo = list_first_entry(head, struct mbo, list); + list_del(head->next); + spin_unlock_irqrestore(&dim_lock, flags); + + mbo->processed_length = 0; + mbo->status = MBO_E_CLOSE; + mbo->complete(mbo); + } +} + +/** + * configure_channel - initialize a channel + * @iface: interface the channel belongs to + * @channel: channel to be configured + * @channel_config: 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) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + bool const is_tx = ccfg->direction == MOST_CH_TX; + u16 const sub_size = ccfg->subbuffer_size; + u16 const buf_size = ccfg->buffer_size; + u16 new_size; + unsigned long flags; + u8 hal_ret; + int const ch_addr = ch_idx * 2 + 2; + struct hdm_channel *const hdm_ch = dev->hch + ch_idx; + + BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + + if (hdm_ch->is_initialized) + return -EPERM; + + switch (ccfg->data_type) { + case MOST_CH_CONTROL: + new_size = dim_norm_ctrl_async_buffer_size(buf_size); + if (new_size == 0) { + pr_err("%s: too small buffer size\n", hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr, + is_tx ? new_size * 2 : new_size); + break; + case MOST_CH_ASYNC: + new_size = dim_norm_ctrl_async_buffer_size(buf_size); + if (new_size == 0) { + pr_err("%s: too small buffer size\n", hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, + is_tx ? new_size * 2 : new_size); + break; + case MOST_CH_ISOC: + new_size = dim_norm_isoc_buffer_size(buf_size, sub_size); + if (new_size == 0) { + pr_err("%s: invalid sub-buffer size or too small buffer size\n", + hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size); + break; + case MOST_CH_SYNC: + new_size = dim_norm_sync_buffer_size(buf_size, sub_size); + if (new_size == 0) { + pr_err("%s: invalid sub-buffer size or too small buffer size\n", + hdm_ch->name); + return -EINVAL; + } + ccfg->buffer_size = new_size; + if (new_size != buf_size) + pr_warn("%s: fixed buffer size (%d -> %d)\n", + hdm_ch->name, buf_size, new_size); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size); + break; + default: + pr_err("%s: configure failed, bad channel type: %d\n", + hdm_ch->name, ccfg->data_type); + return -EINVAL; + } + + if (hal_ret != DIM_NO_ERROR) { + spin_unlock_irqrestore(&dim_lock, flags); + pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n", + hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); + return -ENODEV; + } + + hdm_ch->data_type = ccfg->data_type; + hdm_ch->direction = ccfg->direction; + hdm_ch->is_initialized = true; + + if (hdm_ch->data_type == MOST_CH_ASYNC && + hdm_ch->direction == MOST_CH_TX && + dev->atx_idx < 0) + dev->atx_idx = ch_idx; + + spin_unlock_irqrestore(&dim_lock, flags); + + return 0; +} + +/** + * enqueue - enqueue a buffer for data transfer + * @iface: intended interface + * @channel: 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) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + struct hdm_channel *hdm_ch = dev->hch + ch_idx; + unsigned long flags; + + BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + + if (!hdm_ch->is_initialized) + return -EPERM; + + if (mbo->bus_address == 0) + return -EFAULT; + + spin_lock_irqsave(&dim_lock, flags); + list_add_tail(&mbo->list, &hdm_ch->pending_list); + spin_unlock_irqrestore(&dim_lock, flags); + + (void)try_start_dim_transfer(hdm_ch); + + return 0; +} + +/** + * request_netinfo - triggers retrieving of network info + * @iface: pointer to the interface + * @channel_id: corresponding channel ID + * + * 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 *)) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + struct mbo *mbo; + u8 *data; + + dev->on_netinfo = on_netinfo; + if (!on_netinfo) + return; + + if (dev->atx_idx < 0) { + pr_err("Async Tx Not initialized\n"); + return; + } + + mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL); + if (!mbo) + return; + + mbo->buffer_length = 5; + + data = mbo->virt_address; + + data[0] = 0x00; /* PML High byte */ + data[1] = 0x03; /* PML Low byte */ + data[2] = 0x02; /* PMHL */ + data[3] = 0x08; /* FPH */ + data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */ + + most_submit_mbo(mbo); +} + +/** + * poison_channel - poison buffers of a channel + * @iface: pointer to the interface the channel to be poisoned belongs to + * @channel_id: 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) +{ + struct dim2_hdm *dev = iface_to_hdm(most_iface); + struct hdm_channel *hdm_ch = dev->hch + ch_idx; + unsigned long flags; + u8 hal_ret; + int ret = 0; + + BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); + + if (!hdm_ch->is_initialized) + return -EPERM; + + tasklet_disable(&dim2_tasklet); + spin_lock_irqsave(&dim_lock, flags); + hal_ret = dim_destroy_channel(&hdm_ch->ch); + hdm_ch->is_initialized = false; + if (ch_idx == dev->atx_idx) + dev->atx_idx = -1; + spin_unlock_irqrestore(&dim_lock, flags); + tasklet_enable(&dim2_tasklet); + if (hal_ret != DIM_NO_ERROR) { + pr_err("HAL Failed to close channel %s\n", hdm_ch->name); + ret = -EFAULT; + } + + complete_all_mbos(&hdm_ch->started_list); + complete_all_mbos(&hdm_ch->pending_list); + + return ret; +} + +/* + * 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) +{ + struct dim2_hdm *dev; + struct resource *res; + int ret, i; + struct kobject *kobj; + int irq; + + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + dev->atx_idx = -1; + + platform_set_drvdata(pdev, dev); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + dev->io_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(dev->io_base)) + return PTR_ERR(dev->io_base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get ahb0_int irq: %d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, dim2_ahb_isr, 0, + "dim2_ahb0_int", dev); + if (ret) { + dev_err(&pdev->dev, "failed to request ahb0_int irq %d\n", irq); + return ret; + } + + irq = platform_get_irq(pdev, 1); + if (irq < 0) { + dev_err(&pdev->dev, "failed to get mlb_int irq: %d\n", irq); + return irq; + } + + ret = devm_request_irq(&pdev->dev, irq, dim2_mlb_isr, 0, + "dim2_mlb_int", dev); + if (ret) { + dev_err(&pdev->dev, "failed to request mlb_int irq %d\n", irq); + return ret; + } + + init_waitqueue_head(&dev->netinfo_waitq); + dev->deliver_netinfo = 0; + dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev, + "dim2_netinfo"); + if (IS_ERR(dev->netinfo_task)) + return PTR_ERR(dev->netinfo_task); + + for (i = 0; i < DMA_CHANNELS; i++) { + struct most_channel_capability *cap = dev->capabilities + i; + struct hdm_channel *hdm_ch = dev->hch + i; + + INIT_LIST_HEAD(&hdm_ch->pending_list); + INIT_LIST_HEAD(&hdm_ch->started_list); + hdm_ch->is_initialized = false; + snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2); + + cap->name_suffix = hdm_ch->name; + cap->direction = MOST_CH_RX | MOST_CH_TX; + cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | + MOST_CH_ISOC | MOST_CH_SYNC; + cap->num_buffers_packet = MAX_BUFFERS_PACKET; + cap->buffer_size_packet = MAX_BUF_SIZE_PACKET; + cap->num_buffers_streaming = MAX_BUFFERS_STREAMING; + cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING; + } + + { + const char *fmt; + + if (sizeof(res->start) == sizeof(long long)) + fmt = "dim2-%016llx"; + else if (sizeof(res->start) == sizeof(long)) + fmt = "dim2-%016lx"; + else + fmt = "dim2-%016x"; + + snprintf(dev->name, sizeof(dev->name), fmt, res->start); + } + + dev->most_iface.interface = ITYPE_MEDIALB_DIM2; + dev->most_iface.description = dev->name; + dev->most_iface.num_channels = DMA_CHANNELS; + dev->most_iface.channel_vector = dev->capabilities; + dev->most_iface.configure = configure_channel; + dev->most_iface.enqueue = enqueue; + dev->most_iface.poison_channel = poison_channel; + dev->most_iface.request_netinfo = request_netinfo; + + kobj = most_register_interface(&dev->most_iface); + if (IS_ERR(kobj)) { + ret = PTR_ERR(kobj); + dev_err(&pdev->dev, "failed to register MOST interface\n"); + goto err_stop_thread; + } + + ret = dim2_sysfs_probe(&dev->bus, kobj); + if (ret) + goto err_unreg_iface; + + ret = startup_dim(pdev); + if (ret) { + dev_err(&pdev->dev, "failed to initialize DIM2\n"); + goto err_destroy_bus; + } + + return 0; + +err_destroy_bus: + dim2_sysfs_destroy(&dev->bus); +err_unreg_iface: + most_deregister_interface(&dev->most_iface); +err_stop_thread: + kthread_stop(dev->netinfo_task); + + return ret; +} + +/** + * dim2_remove - dim2 remove handler + * @pdev: platform device structure + * + * Unregister the interface from mostcore + */ +static int dim2_remove(struct platform_device *pdev) +{ + struct dim2_hdm *dev = platform_get_drvdata(pdev); + struct dim2_platform_data *pdata = pdev->dev.platform_data; + unsigned long flags; + + spin_lock_irqsave(&dim_lock, flags); + dim_shutdown(); + spin_unlock_irqrestore(&dim_lock, flags); + + if (pdata && pdata->destroy) + pdata->destroy(pdata); + + dim2_sysfs_destroy(&dev->bus); + most_deregister_interface(&dev->most_iface); + kthread_stop(dev->netinfo_task); + + /* + * break link to local platform_device_id struct + * to prevent crash by unload platform device module + */ + pdev->id_entry = NULL; + + return 0; +} + +static const struct platform_device_id dim2_id[] = { + { "medialb_dim2" }, + { }, /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(platform, dim2_id); + +static struct platform_driver dim2_driver = { + .probe = dim2_probe, + .remove = dim2_remove, + .id_table = dim2_id, + .driver = { + .name = "hdm_dim2", + }, +}; + +module_platform_driver(dim2_driver); + +MODULE_AUTHOR("Jain Roy Ambi "); +MODULE_AUTHOR("Andrey Shvetsov "); +MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/most/dim2/dim2.h b/drivers/staging/most/dim2/dim2.h new file mode 100644 index 000000000000..6a9fc51a2eb4 --- /dev/null +++ b/drivers/staging/most/dim2/dim2.h @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * dim2.h - MediaLB DIM2 HDM Header + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef DIM2_HDM_H +#define DIM2_HDM_H + +struct device; + +/* platform dependent data for dim2 interface */ +struct dim2_platform_data { + int (*init)(struct dim2_platform_data *pd, void __iomem *io_base, + int clk_speed); + void (*destroy)(struct dim2_platform_data *pd); + void *priv; +}; + +#endif /* DIM2_HDM_H */ diff --git a/drivers/staging/most/dim2/errors.h b/drivers/staging/most/dim2/errors.h new file mode 100644 index 000000000000..3487510fbd2f --- /dev/null +++ b/drivers/staging/most/dim2/errors.h @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * errors.h - Definitions of errors for DIM2 HAL API + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef _MOST_DIM_ERRORS_H +#define _MOST_DIM_ERRORS_H + +/** + * MOST DIM errors. + */ +enum dim_errors_t { + /** Not an error */ + DIM_NO_ERROR = 0, + + /** Bad base address for DIM2 IP */ + DIM_INIT_ERR_DIM_ADDR = 0x10, + + /**< Bad MediaLB clock */ + DIM_INIT_ERR_MLB_CLOCK, + + /** Bad channel address */ + DIM_INIT_ERR_CHANNEL_ADDRESS, + + /** Out of DBR memory */ + DIM_INIT_ERR_OUT_OF_MEMORY, + + /** DIM API is called while DIM is not initialized successfully */ + DIM_ERR_DRIVER_NOT_INITIALIZED = 0x20, + + /** + * Configuration does not respect hardware limitations + * for isochronous or synchronous channels + */ + DIM_ERR_BAD_CONFIG, + + /** + * Buffer size does not respect hardware limitations + * for isochronous or synchronous channels + */ + DIM_ERR_BAD_BUFFER_SIZE, + + DIM_ERR_UNDERFLOW, + + DIM_ERR_OVERFLOW, +}; + +#endif /* _MOST_DIM_ERRORS_H */ diff --git a/drivers/staging/most/dim2/hal.c b/drivers/staging/most/dim2/hal.c new file mode 100644 index 000000000000..17c04e1c5e62 --- /dev/null +++ b/drivers/staging/most/dim2/hal.c @@ -0,0 +1,979 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * hal.c - DIM2 HAL implementation + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG + */ + +/* Author: Andrey Shvetsov */ + +#include "hal.h" +#include "errors.h" +#include "reg.h" +#include +#include + +/* + * Size factor for isochronous DBR buffer. + * Minimal value is 3. + */ +#define ISOC_DBR_FACTOR 3u + +/* + * Number of 32-bit units for DBR map. + * + * 1: block size is 512, max allocation is 16K + * 2: block size is 256, max allocation is 8K + * 4: block size is 128, max allocation is 4K + * 8: block size is 64, max allocation is 2K + * + * Min allocated space is block size. + * Max possible allocated space is 32 blocks. + */ +#define DBR_MAP_SIZE 2 + +/* -------------------------------------------------------------------------- */ +/* not configurable area */ + +#define CDT 0x00 +#define ADT 0x40 +#define MLB_CAT 0x80 +#define AHB_CAT 0x88 + +#define DBR_SIZE (16 * 1024) /* specified by IP */ +#define DBR_BLOCK_SIZE (DBR_SIZE / 32 / DBR_MAP_SIZE) + +#define ROUND_UP_TO(x, d) (DIV_ROUND_UP(x, (d)) * (d)) + +/* -------------------------------------------------------------------------- */ +/* generic helper functions and macros */ + +static inline u32 bit_mask(u8 position) +{ + return (u32)1 << position; +} + +static inline bool dim_on_error(u8 error_id, const char *error_message) +{ + dimcb_on_error(error_id, error_message); + return false; +} + +/* -------------------------------------------------------------------------- */ +/* types and local variables */ + +struct async_tx_dbr { + u8 ch_addr; + u16 rpc; + u16 wpc; + u16 rest_size; + u16 sz_queue[CDT0_RPC_MASK + 1]; +}; + +struct lld_global_vars_t { + bool dim_is_initialized; + bool mcm_is_initialized; + struct dim2_regs __iomem *dim2; /* DIM2 core base address */ + struct async_tx_dbr atx_dbr; + u32 fcnt; + u32 dbr_map[DBR_MAP_SIZE]; +}; + +static struct lld_global_vars_t g = { false }; + +/* -------------------------------------------------------------------------- */ + +static int dbr_get_mask_size(u16 size) +{ + int i; + + for (i = 0; i < 6; i++) + if (size <= (DBR_BLOCK_SIZE << i)) + return 1 << i; + return 0; +} + +/** + * Allocates DBR memory. + * @param size Allocating memory size. + * @return Offset in DBR memory by success or DBR_SIZE if out of memory. + */ +static int alloc_dbr(u16 size) +{ + int mask_size; + int i, block_idx = 0; + + if (size <= 0) + return DBR_SIZE; /* out of memory */ + + mask_size = dbr_get_mask_size(size); + if (mask_size == 0) + return DBR_SIZE; /* out of memory */ + + for (i = 0; i < DBR_MAP_SIZE; i++) { + u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); + u32 mask = ~((~(u32)0) << blocks); + + do { + if ((g.dbr_map[i] & mask) == 0) { + g.dbr_map[i] |= mask; + return block_idx * DBR_BLOCK_SIZE; + } + block_idx += mask_size; + /* do shift left with 2 steps in case mask_size == 32 */ + mask <<= mask_size - 1; + } while ((mask <<= 1) != 0); + } + + return DBR_SIZE; /* out of memory */ +} + +static void free_dbr(int offs, int size) +{ + int block_idx = offs / DBR_BLOCK_SIZE; + u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); + u32 mask = ~((~(u32)0) << blocks); + + mask <<= block_idx % 32; + g.dbr_map[block_idx / 32] &= ~mask; +} + +/* -------------------------------------------------------------------------- */ + +static void dim2_transfer_madr(u32 val) +{ + dimcb_io_write(&g.dim2->MADR, val); + + /* wait for transfer completion */ + while ((dimcb_io_read(&g.dim2->MCTL) & 1) != 1) + continue; + + dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ +} + +static void dim2_clear_dbr(u16 addr, u16 size) +{ + enum { MADR_TB_BIT = 30, MADR_WNR_BIT = 31 }; + + u16 const end_addr = addr + size; + u32 const cmd = bit_mask(MADR_WNR_BIT) | bit_mask(MADR_TB_BIT); + + dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ + dimcb_io_write(&g.dim2->MDAT0, 0); + + for (; addr < end_addr; addr++) + dim2_transfer_madr(cmd | addr); +} + +static u32 dim2_read_ctr(u32 ctr_addr, u16 mdat_idx) +{ + dim2_transfer_madr(ctr_addr); + + return dimcb_io_read((&g.dim2->MDAT0) + mdat_idx); +} + +static void dim2_write_ctr_mask(u32 ctr_addr, const u32 *mask, const u32 *value) +{ + enum { MADR_WNR_BIT = 31 }; + + dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ + + if (mask[0] != 0) + dimcb_io_write(&g.dim2->MDAT0, value[0]); + if (mask[1] != 0) + dimcb_io_write(&g.dim2->MDAT1, value[1]); + if (mask[2] != 0) + dimcb_io_write(&g.dim2->MDAT2, value[2]); + if (mask[3] != 0) + dimcb_io_write(&g.dim2->MDAT3, value[3]); + + dimcb_io_write(&g.dim2->MDWE0, mask[0]); + dimcb_io_write(&g.dim2->MDWE1, mask[1]); + dimcb_io_write(&g.dim2->MDWE2, mask[2]); + dimcb_io_write(&g.dim2->MDWE3, mask[3]); + + dim2_transfer_madr(bit_mask(MADR_WNR_BIT) | ctr_addr); +} + +static inline void dim2_write_ctr(u32 ctr_addr, const u32 *value) +{ + u32 const mask[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + dim2_write_ctr_mask(ctr_addr, mask, value); +} + +static inline void dim2_clear_ctr(u32 ctr_addr) +{ + u32 const value[4] = { 0, 0, 0, 0 }; + + dim2_write_ctr(ctr_addr, value); +} + +static void dim2_configure_cat(u8 cat_base, u8 ch_addr, u8 ch_type, + bool read_not_write) +{ + bool isoc_fce = ch_type == CAT_CT_VAL_ISOC; + bool sync_mfe = ch_type == CAT_CT_VAL_SYNC; + u16 const cat = + (read_not_write << CAT_RNW_BIT) | + (ch_type << CAT_CT_SHIFT) | + (ch_addr << CAT_CL_SHIFT) | + (isoc_fce << CAT_FCE_BIT) | + (sync_mfe << CAT_MFE_BIT) | + (false << CAT_MT_BIT) | + (true << CAT_CE_BIT); + u8 const ctr_addr = cat_base + ch_addr / 8; + u8 const idx = (ch_addr % 8) / 2; + u8 const shift = (ch_addr % 2) * 16; + u32 mask[4] = { 0, 0, 0, 0 }; + u32 value[4] = { 0, 0, 0, 0 }; + + mask[idx] = (u32)0xFFFF << shift; + value[idx] = cat << shift; + dim2_write_ctr_mask(ctr_addr, mask, value); +} + +static void dim2_clear_cat(u8 cat_base, u8 ch_addr) +{ + u8 const ctr_addr = cat_base + ch_addr / 8; + u8 const idx = (ch_addr % 8) / 2; + u8 const shift = (ch_addr % 2) * 16; + u32 mask[4] = { 0, 0, 0, 0 }; + u32 value[4] = { 0, 0, 0, 0 }; + + mask[idx] = (u32)0xFFFF << shift; + dim2_write_ctr_mask(ctr_addr, mask, value); +} + +static void dim2_configure_cdt(u8 ch_addr, u16 dbr_address, u16 hw_buffer_size, + u16 packet_length) +{ + u32 cdt[4] = { 0, 0, 0, 0 }; + + if (packet_length) + cdt[1] = ((packet_length - 1) << CDT1_BS_ISOC_SHIFT); + + cdt[3] = + ((hw_buffer_size - 1) << CDT3_BD_SHIFT) | + (dbr_address << CDT3_BA_SHIFT); + dim2_write_ctr(CDT + ch_addr, cdt); +} + +static u16 dim2_rpc(u8 ch_addr) +{ + u32 cdt0 = dim2_read_ctr(CDT + ch_addr, 0); + + return (cdt0 >> CDT0_RPC_SHIFT) & CDT0_RPC_MASK; +} + +static void dim2_clear_cdt(u8 ch_addr) +{ + u32 cdt[4] = { 0, 0, 0, 0 }; + + dim2_write_ctr(CDT + ch_addr, cdt); +} + +static void dim2_configure_adt(u8 ch_addr) +{ + u32 adt[4] = { 0, 0, 0, 0 }; + + adt[0] = + (true << ADT0_CE_BIT) | + (true << ADT0_LE_BIT) | + (0 << ADT0_PG_BIT); + + dim2_write_ctr(ADT + ch_addr, adt); +} + +static void dim2_clear_adt(u8 ch_addr) +{ + u32 adt[4] = { 0, 0, 0, 0 }; + + dim2_write_ctr(ADT + ch_addr, adt); +} + +static void dim2_start_ctrl_async(u8 ch_addr, u8 idx, u32 buf_addr, + u16 buffer_size) +{ + u8 const shift = idx * 16; + + u32 mask[4] = { 0, 0, 0, 0 }; + u32 adt[4] = { 0, 0, 0, 0 }; + + mask[1] = + bit_mask(ADT1_PS_BIT + shift) | + bit_mask(ADT1_RDY_BIT + shift) | + (ADT1_CTRL_ASYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); + adt[1] = + (true << (ADT1_PS_BIT + shift)) | + (true << (ADT1_RDY_BIT + shift)) | + ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); + + mask[idx + 2] = 0xFFFFFFFF; + adt[idx + 2] = buf_addr; + + dim2_write_ctr_mask(ADT + ch_addr, mask, adt); +} + +static void dim2_start_isoc_sync(u8 ch_addr, u8 idx, u32 buf_addr, + u16 buffer_size) +{ + u8 const shift = idx * 16; + + u32 mask[4] = { 0, 0, 0, 0 }; + u32 adt[4] = { 0, 0, 0, 0 }; + + mask[1] = + bit_mask(ADT1_RDY_BIT + shift) | + (ADT1_ISOC_SYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); + adt[1] = + (true << (ADT1_RDY_BIT + shift)) | + ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); + + mask[idx + 2] = 0xFFFFFFFF; + adt[idx + 2] = buf_addr; + + dim2_write_ctr_mask(ADT + ch_addr, mask, adt); +} + +static void dim2_clear_ctram(void) +{ + u32 ctr_addr; + + for (ctr_addr = 0; ctr_addr < 0x90; ctr_addr++) + dim2_clear_ctr(ctr_addr); +} + +static void dim2_configure_channel( + u8 ch_addr, u8 type, u8 is_tx, u16 dbr_address, u16 hw_buffer_size, + u16 packet_length) +{ + dim2_configure_cdt(ch_addr, dbr_address, hw_buffer_size, packet_length); + dim2_configure_cat(MLB_CAT, ch_addr, type, is_tx ? 1 : 0); + + dim2_configure_adt(ch_addr); + dim2_configure_cat(AHB_CAT, ch_addr, type, is_tx ? 0 : 1); + + /* unmask interrupt for used channel, enable mlb_sys_int[0] interrupt */ + dimcb_io_write(&g.dim2->ACMR0, + dimcb_io_read(&g.dim2->ACMR0) | bit_mask(ch_addr)); +} + +static void dim2_clear_channel(u8 ch_addr) +{ + /* mask interrupt for used channel, disable mlb_sys_int[0] interrupt */ + dimcb_io_write(&g.dim2->ACMR0, + dimcb_io_read(&g.dim2->ACMR0) & ~bit_mask(ch_addr)); + + dim2_clear_cat(AHB_CAT, ch_addr); + dim2_clear_adt(ch_addr); + + dim2_clear_cat(MLB_CAT, ch_addr); + dim2_clear_cdt(ch_addr); + + /* clear channel status bit */ + dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); +} + +/* -------------------------------------------------------------------------- */ +/* trace async tx dbr fill state */ + +static inline u16 norm_pc(u16 pc) +{ + return pc & CDT0_RPC_MASK; +} + +static void dbrcnt_init(u8 ch_addr, u16 dbr_size) +{ + g.atx_dbr.rest_size = dbr_size; + g.atx_dbr.rpc = dim2_rpc(ch_addr); + g.atx_dbr.wpc = g.atx_dbr.rpc; +} + +static void dbrcnt_enq(int buf_sz) +{ + g.atx_dbr.rest_size -= buf_sz; + g.atx_dbr.sz_queue[norm_pc(g.atx_dbr.wpc)] = buf_sz; + g.atx_dbr.wpc++; +} + +u16 dim_dbr_space(struct dim_channel *ch) +{ + u16 cur_rpc; + struct async_tx_dbr *dbr = &g.atx_dbr; + + if (ch->addr != dbr->ch_addr) + return 0xFFFF; + + cur_rpc = dim2_rpc(ch->addr); + + while (norm_pc(dbr->rpc) != cur_rpc) { + dbr->rest_size += dbr->sz_queue[norm_pc(dbr->rpc)]; + dbr->rpc++; + } + + if ((u16)(dbr->wpc - dbr->rpc) >= CDT0_RPC_MASK) + return 0; + + return dbr->rest_size; +} + +/* -------------------------------------------------------------------------- */ +/* channel state helpers */ + +static void state_init(struct int_ch_state *state) +{ + state->request_counter = 0; + state->service_counter = 0; + + state->idx1 = 0; + state->idx2 = 0; + state->level = 0; +} + +/* -------------------------------------------------------------------------- */ +/* macro helper functions */ + +static inline bool check_channel_address(u32 ch_address) +{ + return ch_address > 0 && (ch_address % 2) == 0 && + (ch_address / 2) <= (u32)CAT_CL_MASK; +} + +static inline bool check_packet_length(u32 packet_length) +{ + u16 const max_size = ((u16)CDT3_BD_ISOC_MASK + 1u) / ISOC_DBR_FACTOR; + + if (packet_length <= 0) + return false; /* too small */ + + if (packet_length > max_size) + return false; /* too big */ + + if (packet_length - 1u > (u32)CDT1_BS_ISOC_MASK) + return false; /* too big */ + + return true; +} + +static inline bool check_bytes_per_frame(u32 bytes_per_frame) +{ + u16 const bd_factor = g.fcnt + 2; + u16 const max_size = ((u16)CDT3_BD_MASK + 1u) >> bd_factor; + + if (bytes_per_frame <= 0) + return false; /* too small */ + + if (bytes_per_frame > max_size) + return false; /* too big */ + + return true; +} + +static inline u16 norm_ctrl_async_buffer_size(u16 buf_size) +{ + u16 const max_size = (u16)ADT1_CTRL_ASYNC_BD_MASK + 1u; + + if (buf_size > max_size) + return max_size; + + return buf_size; +} + +static inline u16 norm_isoc_buffer_size(u16 buf_size, u16 packet_length) +{ + u16 n; + u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; + + if (buf_size > max_size) + buf_size = max_size; + + n = buf_size / packet_length; + + if (n < 2u) + return 0; /* too small buffer for given packet_length */ + + return packet_length * n; +} + +static inline u16 norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) +{ + u16 n; + u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; + u32 const unit = bytes_per_frame << g.fcnt; + + if (buf_size > max_size) + buf_size = max_size; + + n = buf_size / unit; + + if (n < 1u) + return 0; /* too small buffer for given bytes_per_frame */ + + return unit * n; +} + +static void dim2_cleanup(void) +{ + /* disable MediaLB */ + dimcb_io_write(&g.dim2->MLBC0, false << MLBC0_MLBEN_BIT); + + dim2_clear_ctram(); + + /* disable mlb_int interrupt */ + dimcb_io_write(&g.dim2->MIEN, 0); + + /* clear status for all dma channels */ + dimcb_io_write(&g.dim2->ACSR0, 0xFFFFFFFF); + dimcb_io_write(&g.dim2->ACSR1, 0xFFFFFFFF); + + /* mask interrupts for all channels */ + dimcb_io_write(&g.dim2->ACMR0, 0); + dimcb_io_write(&g.dim2->ACMR1, 0); +} + +static void dim2_initialize(bool enable_6pin, u8 mlb_clock) +{ + dim2_cleanup(); + + /* configure and enable MediaLB */ + dimcb_io_write(&g.dim2->MLBC0, + enable_6pin << MLBC0_MLBPEN_BIT | + mlb_clock << MLBC0_MLBCLK_SHIFT | + g.fcnt << MLBC0_FCNT_SHIFT | + true << MLBC0_MLBEN_BIT); + + /* activate all HBI channels */ + dimcb_io_write(&g.dim2->HCMR0, 0xFFFFFFFF); + dimcb_io_write(&g.dim2->HCMR1, 0xFFFFFFFF); + + /* enable HBI */ + dimcb_io_write(&g.dim2->HCTL, bit_mask(HCTL_EN_BIT)); + + /* configure DMA */ + dimcb_io_write(&g.dim2->ACTL, + ACTL_DMA_MODE_VAL_DMA_MODE_1 << ACTL_DMA_MODE_BIT | + true << ACTL_SCE_BIT); +} + +static bool dim2_is_mlb_locked(void) +{ + u32 const mask0 = bit_mask(MLBC0_MLBLK_BIT); + u32 const mask1 = bit_mask(MLBC1_CLKMERR_BIT) | + bit_mask(MLBC1_LOCKERR_BIT); + u32 const c1 = dimcb_io_read(&g.dim2->MLBC1); + u32 const nda_mask = (u32)MLBC1_NDA_MASK << MLBC1_NDA_SHIFT; + + dimcb_io_write(&g.dim2->MLBC1, c1 & nda_mask); + return (dimcb_io_read(&g.dim2->MLBC1) & mask1) == 0 && + (dimcb_io_read(&g.dim2->MLBC0) & mask0) != 0; +} + +/* -------------------------------------------------------------------------- */ +/* channel help routines */ + +static inline bool service_channel(u8 ch_addr, u8 idx) +{ + u8 const shift = idx * 16; + u32 const adt1 = dim2_read_ctr(ADT + ch_addr, 1); + u32 mask[4] = { 0, 0, 0, 0 }; + u32 adt_w[4] = { 0, 0, 0, 0 }; + + if (((adt1 >> (ADT1_DNE_BIT + shift)) & 1) == 0) + return false; + + mask[1] = + bit_mask(ADT1_DNE_BIT + shift) | + bit_mask(ADT1_ERR_BIT + shift) | + bit_mask(ADT1_RDY_BIT + shift); + dim2_write_ctr_mask(ADT + ch_addr, mask, adt_w); + + /* clear channel status bit */ + dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); + + return true; +} + +/* -------------------------------------------------------------------------- */ +/* channel init routines */ + +static void isoc_init(struct dim_channel *ch, u8 ch_addr, u16 packet_length) +{ + state_init(&ch->state); + + ch->addr = ch_addr; + + ch->packet_length = packet_length; + ch->bytes_per_frame = 0; + ch->done_sw_buffers_number = 0; +} + +static void sync_init(struct dim_channel *ch, u8 ch_addr, u16 bytes_per_frame) +{ + state_init(&ch->state); + + ch->addr = ch_addr; + + ch->packet_length = 0; + ch->bytes_per_frame = bytes_per_frame; + ch->done_sw_buffers_number = 0; +} + +static void channel_init(struct dim_channel *ch, u8 ch_addr) +{ + state_init(&ch->state); + + ch->addr = ch_addr; + + ch->packet_length = 0; + ch->bytes_per_frame = 0; + ch->done_sw_buffers_number = 0; +} + +/* returns true if channel interrupt state is cleared */ +static bool channel_service_interrupt(struct dim_channel *ch) +{ + struct int_ch_state *const state = &ch->state; + + if (!service_channel(ch->addr, state->idx2)) + return false; + + state->idx2 ^= 1; + state->request_counter++; + return true; +} + +static bool channel_start(struct dim_channel *ch, u32 buf_addr, u16 buf_size) +{ + struct int_ch_state *const state = &ch->state; + + if (buf_size <= 0) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, "Bad buffer size"); + + if (ch->packet_length == 0 && ch->bytes_per_frame == 0 && + buf_size != norm_ctrl_async_buffer_size(buf_size)) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, + "Bad control/async buffer size"); + + if (ch->packet_length && + buf_size != norm_isoc_buffer_size(buf_size, ch->packet_length)) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, + "Bad isochronous buffer size"); + + if (ch->bytes_per_frame && + buf_size != norm_sync_buffer_size(buf_size, ch->bytes_per_frame)) + return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, + "Bad synchronous buffer size"); + + if (state->level >= 2u) + return dim_on_error(DIM_ERR_OVERFLOW, "Channel overflow"); + + ++state->level; + + if (ch->addr == g.atx_dbr.ch_addr) + dbrcnt_enq(buf_size); + + if (ch->packet_length || ch->bytes_per_frame) + dim2_start_isoc_sync(ch->addr, state->idx1, buf_addr, buf_size); + else + dim2_start_ctrl_async(ch->addr, state->idx1, buf_addr, + buf_size); + state->idx1 ^= 1; + + return true; +} + +static u8 channel_service(struct dim_channel *ch) +{ + struct int_ch_state *const state = &ch->state; + + if (state->service_counter != state->request_counter) { + state->service_counter++; + if (state->level == 0) + return DIM_ERR_UNDERFLOW; + + --state->level; + ch->done_sw_buffers_number++; + } + + return DIM_NO_ERROR; +} + +static bool channel_detach_buffers(struct dim_channel *ch, u16 buffers_number) +{ + if (buffers_number > ch->done_sw_buffers_number) + return dim_on_error(DIM_ERR_UNDERFLOW, "Channel underflow"); + + ch->done_sw_buffers_number -= buffers_number; + return true; +} + +/* -------------------------------------------------------------------------- */ +/* API */ + +u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, + u32 fcnt) +{ + g.dim_is_initialized = false; + + if (!dim_base_address) + return DIM_INIT_ERR_DIM_ADDR; + + /* MediaLB clock: 0 - 256 fs, 1 - 512 fs, 2 - 1024 fs, 3 - 2048 fs */ + /* MediaLB clock: 4 - 3072 fs, 5 - 4096 fs, 6 - 6144 fs, 7 - 8192 fs */ + if (mlb_clock >= 8) + return DIM_INIT_ERR_MLB_CLOCK; + + if (fcnt > MLBC0_FCNT_MAX_VAL) + return DIM_INIT_ERR_MLB_CLOCK; + + g.dim2 = dim_base_address; + g.fcnt = fcnt; + g.dbr_map[0] = 0; + g.dbr_map[1] = 0; + + dim2_initialize(mlb_clock >= 3, mlb_clock); + + g.dim_is_initialized = true; + + return DIM_NO_ERROR; +} + +void dim_shutdown(void) +{ + g.dim_is_initialized = false; + dim2_cleanup(); +} + +bool dim_get_lock_state(void) +{ + return dim2_is_mlb_locked(); +} + +static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx, + u16 ch_address, u16 hw_buffer_size) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (!check_channel_address(ch_address)) + return DIM_INIT_ERR_CHANNEL_ADDRESS; + + ch->dbr_size = ROUND_UP_TO(hw_buffer_size, DBR_BLOCK_SIZE); + ch->dbr_addr = alloc_dbr(ch->dbr_size); + if (ch->dbr_addr >= DBR_SIZE) + return DIM_INIT_ERR_OUT_OF_MEMORY; + + channel_init(ch, ch_address / 2); + + dim2_configure_channel(ch->addr, type, is_tx, + ch->dbr_addr, ch->dbr_size, 0); + + return DIM_NO_ERROR; +} + +void dim_service_mlb_int_irq(void) +{ + dimcb_io_write(&g.dim2->MS0, 0); + dimcb_io_write(&g.dim2->MS1, 0); +} + +u16 dim_norm_ctrl_async_buffer_size(u16 buf_size) +{ + return norm_ctrl_async_buffer_size(buf_size); +} + +/** + * Retrieves maximal possible correct buffer size for isochronous data type + * conform to given packet length and not bigger than given buffer size. + * + * Returns non-zero correct buffer size or zero by error. + */ +u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length) +{ + if (!check_packet_length(packet_length)) + return 0; + + return norm_isoc_buffer_size(buf_size, packet_length); +} + +/** + * Retrieves maximal possible correct buffer size for synchronous data type + * conform to given bytes per frame and not bigger than given buffer size. + * + * Returns non-zero correct buffer size or zero by error. + */ +u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) +{ + if (!check_bytes_per_frame(bytes_per_frame)) + return 0; + + return norm_sync_buffer_size(buf_size, bytes_per_frame); +} + +u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size) +{ + return init_ctrl_async(ch, CAT_CT_VAL_CONTROL, is_tx, ch_address, + max_buffer_size); +} + +u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size) +{ + u8 ret = init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address, + max_buffer_size); + + if (is_tx && !g.atx_dbr.ch_addr) { + g.atx_dbr.ch_addr = ch->addr; + dbrcnt_init(ch->addr, ch->dbr_size); + dimcb_io_write(&g.dim2->MIEN, bit_mask(20)); + } + + return ret; +} + +u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 packet_length) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (!check_channel_address(ch_address)) + return DIM_INIT_ERR_CHANNEL_ADDRESS; + + if (!check_packet_length(packet_length)) + return DIM_ERR_BAD_CONFIG; + + ch->dbr_size = packet_length * ISOC_DBR_FACTOR; + ch->dbr_addr = alloc_dbr(ch->dbr_size); + if (ch->dbr_addr >= DBR_SIZE) + return DIM_INIT_ERR_OUT_OF_MEMORY; + + isoc_init(ch, ch_address / 2, packet_length); + + dim2_configure_channel(ch->addr, CAT_CT_VAL_ISOC, is_tx, ch->dbr_addr, + ch->dbr_size, packet_length); + + return DIM_NO_ERROR; +} + +u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 bytes_per_frame) +{ + u16 bd_factor = g.fcnt + 2; + + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (!check_channel_address(ch_address)) + return DIM_INIT_ERR_CHANNEL_ADDRESS; + + if (!check_bytes_per_frame(bytes_per_frame)) + return DIM_ERR_BAD_CONFIG; + + ch->dbr_size = bytes_per_frame << bd_factor; + ch->dbr_addr = alloc_dbr(ch->dbr_size); + if (ch->dbr_addr >= DBR_SIZE) + return DIM_INIT_ERR_OUT_OF_MEMORY; + + sync_init(ch, ch_address / 2, bytes_per_frame); + + dim2_clear_dbr(ch->dbr_addr, ch->dbr_size); + dim2_configure_channel(ch->addr, CAT_CT_VAL_SYNC, is_tx, + ch->dbr_addr, ch->dbr_size, 0); + + return DIM_NO_ERROR; +} + +u8 dim_destroy_channel(struct dim_channel *ch) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + if (ch->addr == g.atx_dbr.ch_addr) { + dimcb_io_write(&g.dim2->MIEN, 0); + g.atx_dbr.ch_addr = 0; + } + + dim2_clear_channel(ch->addr); + if (ch->dbr_addr < DBR_SIZE) + free_dbr(ch->dbr_addr, ch->dbr_size); + ch->dbr_addr = DBR_SIZE; + + return DIM_NO_ERROR; +} + +void dim_service_ahb_int_irq(struct dim_channel *const *channels) +{ + bool state_changed; + + if (!g.dim_is_initialized) { + dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, + "DIM is not initialized"); + return; + } + + if (!channels) { + dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, "Bad channels"); + return; + } + + /* + * Use while-loop and a flag to make sure the age is changed back at + * least once, otherwise the interrupt may never come if CPU generates + * interrupt on changing age. + * This cycle runs not more than number of channels, because + * channel_service_interrupt() routine doesn't start the channel again. + */ + do { + struct dim_channel *const *ch = channels; + + state_changed = false; + + while (*ch) { + state_changed |= channel_service_interrupt(*ch); + ++ch; + } + } while (state_changed); +} + +u8 dim_service_channel(struct dim_channel *ch) +{ + if (!g.dim_is_initialized || !ch) + return DIM_ERR_DRIVER_NOT_INITIALIZED; + + return channel_service(ch); +} + +struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, + struct dim_ch_state_t *state_ptr) +{ + if (!ch || !state_ptr) + return NULL; + + state_ptr->ready = ch->state.level < 2; + state_ptr->done_buffers = ch->done_sw_buffers_number; + + return state_ptr; +} + +bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, + u16 buffer_size) +{ + if (!ch) + return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, + "Bad channel"); + + return channel_start(ch, buffer_addr, buffer_size); +} + +bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number) +{ + if (!ch) + return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, + "Bad channel"); + + return channel_detach_buffers(ch, buffers_number); +} diff --git a/drivers/staging/most/dim2/hal.h b/drivers/staging/most/dim2/hal.h new file mode 100644 index 000000000000..e04a5350f134 --- /dev/null +++ b/drivers/staging/most/dim2/hal.h @@ -0,0 +1,106 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * hal.h - DIM2 HAL interface + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef _DIM2_HAL_H +#define _DIM2_HAL_H + +#include +#include "reg.h" + +/* + * The values below are specified in the hardware specification. + * So, they should not be changed until the hardware specification changes. + */ +enum mlb_clk_speed { + CLK_256FS = 0, + CLK_512FS = 1, + CLK_1024FS = 2, + CLK_2048FS = 3, + CLK_3072FS = 4, + CLK_4096FS = 5, + CLK_6144FS = 6, + CLK_8192FS = 7, +}; + +struct dim_ch_state_t { + bool ready; /* Shows readiness to enqueue next buffer */ + u16 done_buffers; /* Number of completed buffers */ +}; + +struct int_ch_state { + /* changed only in interrupt context */ + volatile int request_counter; + + /* changed only in task context */ + volatile int service_counter; + + u8 idx1; + u8 idx2; + u8 level; /* [0..2], buffering level */ +}; + +struct dim_channel { + struct int_ch_state state; + u8 addr; + u16 dbr_addr; + u16 dbr_size; + u16 packet_length; /*< Isochronous packet length in bytes. */ + u16 bytes_per_frame; /*< Synchronous bytes per frame. */ + u16 done_sw_buffers_number; /*< Done software buffers number. */ +}; + +u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, + u32 fcnt); + +void dim_shutdown(void); + +bool dim_get_lock_state(void); + +u16 dim_norm_ctrl_async_buffer_size(u16 buf_size); + +u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length); + +u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame); + +u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size); + +u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 max_buffer_size); + +u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 packet_length); + +u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, + u16 bytes_per_frame); + +u8 dim_destroy_channel(struct dim_channel *ch); + +void dim_service_mlb_int_irq(void); + +void dim_service_ahb_int_irq(struct dim_channel *const *channels); + +u8 dim_service_channel(struct dim_channel *ch); + +struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, + struct dim_ch_state_t *state_ptr); + +u16 dim_dbr_space(struct dim_channel *ch); + +bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, + u16 buffer_size); + +bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number); + +u32 dimcb_io_read(u32 __iomem *ptr32); + +void dimcb_io_write(u32 __iomem *ptr32, u32 value); + +void dimcb_on_error(u8 error_id, const char *error_message); + +#endif /* _DIM2_HAL_H */ diff --git a/drivers/staging/most/dim2/reg.h b/drivers/staging/most/dim2/reg.h new file mode 100644 index 000000000000..69cbf78239f1 --- /dev/null +++ b/drivers/staging/most/dim2/reg.h @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * reg.h - Definitions for registers of DIM2 + * (MediaLB, Device Interface Macro IP, OS62420) + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +#ifndef DIM2_OS62420_H +#define DIM2_OS62420_H + +#include + +struct dim2_regs { + /* 0x00 */ u32 MLBC0; + /* 0x01 */ u32 rsvd0[1]; + /* 0x02 */ u32 MLBPC0; + /* 0x03 */ u32 MS0; + /* 0x04 */ u32 rsvd1[1]; + /* 0x05 */ u32 MS1; + /* 0x06 */ u32 rsvd2[2]; + /* 0x08 */ u32 MSS; + /* 0x09 */ u32 MSD; + /* 0x0A */ u32 rsvd3[1]; + /* 0x0B */ u32 MIEN; + /* 0x0C */ u32 rsvd4[1]; + /* 0x0D */ u32 MLBPC2; + /* 0x0E */ u32 MLBPC1; + /* 0x0F */ u32 MLBC1; + /* 0x10 */ u32 rsvd5[0x10]; + /* 0x20 */ u32 HCTL; + /* 0x21 */ u32 rsvd6[1]; + /* 0x22 */ u32 HCMR0; + /* 0x23 */ u32 HCMR1; + /* 0x24 */ u32 HCER0; + /* 0x25 */ u32 HCER1; + /* 0x26 */ u32 HCBR0; + /* 0x27 */ u32 HCBR1; + /* 0x28 */ u32 rsvd7[8]; + /* 0x30 */ u32 MDAT0; + /* 0x31 */ u32 MDAT1; + /* 0x32 */ u32 MDAT2; + /* 0x33 */ u32 MDAT3; + /* 0x34 */ u32 MDWE0; + /* 0x35 */ u32 MDWE1; + /* 0x36 */ u32 MDWE2; + /* 0x37 */ u32 MDWE3; + /* 0x38 */ u32 MCTL; + /* 0x39 */ u32 MADR; + /* 0x3A */ u32 rsvd8[0xB6]; + /* 0xF0 */ u32 ACTL; + /* 0xF1 */ u32 rsvd9[3]; + /* 0xF4 */ u32 ACSR0; + /* 0xF5 */ u32 ACSR1; + /* 0xF6 */ u32 ACMR0; + /* 0xF7 */ u32 ACMR1; +}; + +#define DIM2_MASK(n) (~((~(u32)0) << (n))) + +enum { + MLBC0_MLBLK_BIT = 7, + + MLBC0_MLBPEN_BIT = 5, + + MLBC0_MLBCLK_SHIFT = 2, + MLBC0_MLBCLK_VAL_256FS = 0, + MLBC0_MLBCLK_VAL_512FS = 1, + MLBC0_MLBCLK_VAL_1024FS = 2, + MLBC0_MLBCLK_VAL_2048FS = 3, + + MLBC0_FCNT_SHIFT = 15, + MLBC0_FCNT_MASK = 7, + MLBC0_FCNT_MAX_VAL = 6, + + MLBC0_MLBEN_BIT = 0, + + MIEN_CTX_BREAK_BIT = 29, + MIEN_CTX_PE_BIT = 28, + MIEN_CTX_DONE_BIT = 27, + + MIEN_CRX_BREAK_BIT = 26, + MIEN_CRX_PE_BIT = 25, + MIEN_CRX_DONE_BIT = 24, + + MIEN_ATX_BREAK_BIT = 22, + MIEN_ATX_PE_BIT = 21, + MIEN_ATX_DONE_BIT = 20, + + MIEN_ARX_BREAK_BIT = 19, + MIEN_ARX_PE_BIT = 18, + MIEN_ARX_DONE_BIT = 17, + + MIEN_SYNC_PE_BIT = 16, + + MIEN_ISOC_BUFO_BIT = 1, + MIEN_ISOC_PE_BIT = 0, + + MLBC1_NDA_SHIFT = 8, + MLBC1_NDA_MASK = 0xFF, + + MLBC1_CLKMERR_BIT = 7, + MLBC1_LOCKERR_BIT = 6, + + ACTL_DMA_MODE_BIT = 2, + ACTL_DMA_MODE_VAL_DMA_MODE_0 = 0, + ACTL_DMA_MODE_VAL_DMA_MODE_1 = 1, + ACTL_SCE_BIT = 0, + + HCTL_EN_BIT = 15 +}; + +enum { + CDT0_RPC_SHIFT = 16 + 11, + CDT0_RPC_MASK = DIM2_MASK(5), + + CDT1_BS_ISOC_SHIFT = 0, + CDT1_BS_ISOC_MASK = DIM2_MASK(9), + + CDT3_BD_SHIFT = 0, + CDT3_BD_MASK = DIM2_MASK(12), + CDT3_BD_ISOC_MASK = DIM2_MASK(13), + CDT3_BA_SHIFT = 16, + + ADT0_CE_BIT = 15, + ADT0_LE_BIT = 14, + ADT0_PG_BIT = 13, + + ADT1_RDY_BIT = 15, + ADT1_DNE_BIT = 14, + ADT1_ERR_BIT = 13, + ADT1_PS_BIT = 12, + ADT1_MEP_BIT = 11, + ADT1_BD_SHIFT = 0, + ADT1_CTRL_ASYNC_BD_MASK = DIM2_MASK(11), + ADT1_ISOC_SYNC_BD_MASK = DIM2_MASK(13), + + CAT_FCE_BIT = 14, + CAT_MFE_BIT = 14, + + CAT_MT_BIT = 13, + + CAT_RNW_BIT = 12, + + CAT_CE_BIT = 11, + + CAT_CT_SHIFT = 8, + CAT_CT_VAL_SYNC = 0, + CAT_CT_VAL_CONTROL = 1, + CAT_CT_VAL_ASYNC = 2, + CAT_CT_VAL_ISOC = 3, + + CAT_CL_SHIFT = 0, + CAT_CL_MASK = DIM2_MASK(6) +}; + +#endif /* DIM2_OS62420_H */ diff --git a/drivers/staging/most/dim2/sysfs.c b/drivers/staging/most/dim2/sysfs.c new file mode 100644 index 000000000000..ec1f4cecf9e7 --- /dev/null +++ b/drivers/staging/most/dim2/sysfs.c @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sysfs.c - MediaLB sysfs information + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +/* Author: Andrey Shvetsov */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include "sysfs.h" + +struct bus_attr { + struct attribute attr; + ssize_t (*show)(struct medialb_bus *bus, char *buf); + ssize_t (*store)(struct medialb_bus *bus, const char *buf, + size_t count); +}; + +static ssize_t state_show(struct medialb_bus *bus, char *buf) +{ + bool state = dim2_sysfs_get_state_cb(); + + return sprintf(buf, "%s\n", state ? "locked" : ""); +} + +static struct bus_attr state_attr = __ATTR_RO(state); + +static struct attribute *bus_default_attrs[] = { + &state_attr.attr, + NULL, +}; + +static const struct attribute_group bus_attr_group = { + .attrs = bus_default_attrs, +}; + +static void bus_kobj_release(struct kobject *kobj) +{ +} + +static ssize_t bus_kobj_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct medialb_bus *bus = + container_of(kobj, struct medialb_bus, kobj_group); + struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); + + if (!xattr->show) + return -EIO; + + return xattr->show(bus, buf); +} + +static ssize_t bus_kobj_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t count) +{ + struct medialb_bus *bus = + container_of(kobj, struct medialb_bus, kobj_group); + struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); + + if (!xattr->store) + return -EIO; + + return xattr->store(bus, buf, count); +} + +static struct sysfs_ops const bus_kobj_sysfs_ops = { + .show = bus_kobj_attr_show, + .store = bus_kobj_attr_store, +}; + +static struct kobj_type bus_ktype = { + .release = bus_kobj_release, + .sysfs_ops = &bus_kobj_sysfs_ops, +}; + +int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj) +{ + int err; + + kobject_init(&bus->kobj_group, &bus_ktype); + err = kobject_add(&bus->kobj_group, parent_kobj, "bus"); + if (err) { + pr_err("kobject_add() failed: %d\n", err); + goto err_kobject_add; + } + + err = sysfs_create_group(&bus->kobj_group, &bus_attr_group); + if (err) { + pr_err("sysfs_create_group() failed: %d\n", err); + goto err_create_group; + } + + return 0; + +err_create_group: + kobject_put(&bus->kobj_group); + +err_kobject_add: + return err; +} + +void dim2_sysfs_destroy(struct medialb_bus *bus) +{ + kobject_put(&bus->kobj_group); +} diff --git a/drivers/staging/most/dim2/sysfs.h b/drivers/staging/most/dim2/sysfs.h new file mode 100644 index 000000000000..a33ebd8b45f5 --- /dev/null +++ b/drivers/staging/most/dim2/sysfs.h @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * sysfs.h - MediaLB sysfs information + * + * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG + */ + +/* Author: Andrey Shvetsov */ + +#ifndef DIM2_SYSFS_H +#define DIM2_SYSFS_H + +#include + +struct medialb_bus { + struct kobject kobj_group; +}; + +struct dim2_hdm; + +int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj); +void dim2_sysfs_destroy(struct medialb_bus *bus); + +/* + * callback, + * must deliver MediaLB state as true if locked or false if unlocked + */ +bool dim2_sysfs_get_state_cb(void); + +#endif /* DIM2_SYSFS_H */ diff --git a/drivers/staging/most/hdm-dim2/Kconfig b/drivers/staging/most/hdm-dim2/Kconfig deleted file mode 100644 index 663bfebff674..000000000000 --- a/drivers/staging/most/hdm-dim2/Kconfig +++ /dev/null @@ -1,16 +0,0 @@ -# -# MediaLB configuration -# - -config HDM_DIM2 - tristate "DIM2 HDM" - depends on HAS_IOMEM - - ---help--- - Say Y here if you want to connect via MediaLB to network transceiver. - This device driver is platform dependent and needs an additional - platform driver to be installed. For more information contact - maintainer of this driver. - - To compile this driver as a module, choose M here: the - module will be called hdm_dim2. diff --git a/drivers/staging/most/hdm-dim2/dim2_errors.h b/drivers/staging/most/hdm-dim2/dim2_errors.h deleted file mode 100644 index 8b90196076d5..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_errors.h +++ /dev/null @@ -1,51 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_errors.h - Definitions of errors for DIM2 HAL API - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef _MOST_DIM_ERRORS_H -#define _MOST_DIM_ERRORS_H - -/** - * MOST DIM errors. - */ -enum dim_errors_t { - /** Not an error */ - DIM_NO_ERROR = 0, - - /** Bad base address for DIM2 IP */ - DIM_INIT_ERR_DIM_ADDR = 0x10, - - /**< Bad MediaLB clock */ - DIM_INIT_ERR_MLB_CLOCK, - - /** Bad channel address */ - DIM_INIT_ERR_CHANNEL_ADDRESS, - - /** Out of DBR memory */ - DIM_INIT_ERR_OUT_OF_MEMORY, - - /** DIM API is called while DIM is not initialized successfully */ - DIM_ERR_DRIVER_NOT_INITIALIZED = 0x20, - - /** - * Configuration does not respect hardware limitations - * for isochronous or synchronous channels - */ - DIM_ERR_BAD_CONFIG, - - /** - * Buffer size does not respect hardware limitations - * for isochronous or synchronous channels - */ - DIM_ERR_BAD_BUFFER_SIZE, - - DIM_ERR_UNDERFLOW, - - DIM_ERR_OVERFLOW, -}; - -#endif /* _MOST_DIM_ERRORS_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.c b/drivers/staging/most/hdm-dim2/dim2_hal.c deleted file mode 100644 index f98ac935729c..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hal.c +++ /dev/null @@ -1,979 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hal.c - DIM2 HAL implementation - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG - */ - -/* Author: Andrey Shvetsov */ - -#include "dim2_hal.h" -#include "dim2_errors.h" -#include "dim2_reg.h" -#include -#include - -/* - * Size factor for isochronous DBR buffer. - * Minimal value is 3. - */ -#define ISOC_DBR_FACTOR 3u - -/* - * Number of 32-bit units for DBR map. - * - * 1: block size is 512, max allocation is 16K - * 2: block size is 256, max allocation is 8K - * 4: block size is 128, max allocation is 4K - * 8: block size is 64, max allocation is 2K - * - * Min allocated space is block size. - * Max possible allocated space is 32 blocks. - */ -#define DBR_MAP_SIZE 2 - -/* -------------------------------------------------------------------------- */ -/* not configurable area */ - -#define CDT 0x00 -#define ADT 0x40 -#define MLB_CAT 0x80 -#define AHB_CAT 0x88 - -#define DBR_SIZE (16 * 1024) /* specified by IP */ -#define DBR_BLOCK_SIZE (DBR_SIZE / 32 / DBR_MAP_SIZE) - -#define ROUND_UP_TO(x, d) (DIV_ROUND_UP(x, (d)) * (d)) - -/* -------------------------------------------------------------------------- */ -/* generic helper functions and macros */ - -static inline u32 bit_mask(u8 position) -{ - return (u32)1 << position; -} - -static inline bool dim_on_error(u8 error_id, const char *error_message) -{ - dimcb_on_error(error_id, error_message); - return false; -} - -/* -------------------------------------------------------------------------- */ -/* types and local variables */ - -struct async_tx_dbr { - u8 ch_addr; - u16 rpc; - u16 wpc; - u16 rest_size; - u16 sz_queue[CDT0_RPC_MASK + 1]; -}; - -struct lld_global_vars_t { - bool dim_is_initialized; - bool mcm_is_initialized; - struct dim2_regs __iomem *dim2; /* DIM2 core base address */ - struct async_tx_dbr atx_dbr; - u32 fcnt; - u32 dbr_map[DBR_MAP_SIZE]; -}; - -static struct lld_global_vars_t g = { false }; - -/* -------------------------------------------------------------------------- */ - -static int dbr_get_mask_size(u16 size) -{ - int i; - - for (i = 0; i < 6; i++) - if (size <= (DBR_BLOCK_SIZE << i)) - return 1 << i; - return 0; -} - -/** - * Allocates DBR memory. - * @param size Allocating memory size. - * @return Offset in DBR memory by success or DBR_SIZE if out of memory. - */ -static int alloc_dbr(u16 size) -{ - int mask_size; - int i, block_idx = 0; - - if (size <= 0) - return DBR_SIZE; /* out of memory */ - - mask_size = dbr_get_mask_size(size); - if (mask_size == 0) - return DBR_SIZE; /* out of memory */ - - for (i = 0; i < DBR_MAP_SIZE; i++) { - u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); - u32 mask = ~((~(u32)0) << blocks); - - do { - if ((g.dbr_map[i] & mask) == 0) { - g.dbr_map[i] |= mask; - return block_idx * DBR_BLOCK_SIZE; - } - block_idx += mask_size; - /* do shift left with 2 steps in case mask_size == 32 */ - mask <<= mask_size - 1; - } while ((mask <<= 1) != 0); - } - - return DBR_SIZE; /* out of memory */ -} - -static void free_dbr(int offs, int size) -{ - int block_idx = offs / DBR_BLOCK_SIZE; - u32 const blocks = DIV_ROUND_UP(size, DBR_BLOCK_SIZE); - u32 mask = ~((~(u32)0) << blocks); - - mask <<= block_idx % 32; - g.dbr_map[block_idx / 32] &= ~mask; -} - -/* -------------------------------------------------------------------------- */ - -static void dim2_transfer_madr(u32 val) -{ - dimcb_io_write(&g.dim2->MADR, val); - - /* wait for transfer completion */ - while ((dimcb_io_read(&g.dim2->MCTL) & 1) != 1) - continue; - - dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ -} - -static void dim2_clear_dbr(u16 addr, u16 size) -{ - enum { MADR_TB_BIT = 30, MADR_WNR_BIT = 31 }; - - u16 const end_addr = addr + size; - u32 const cmd = bit_mask(MADR_WNR_BIT) | bit_mask(MADR_TB_BIT); - - dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ - dimcb_io_write(&g.dim2->MDAT0, 0); - - for (; addr < end_addr; addr++) - dim2_transfer_madr(cmd | addr); -} - -static u32 dim2_read_ctr(u32 ctr_addr, u16 mdat_idx) -{ - dim2_transfer_madr(ctr_addr); - - return dimcb_io_read((&g.dim2->MDAT0) + mdat_idx); -} - -static void dim2_write_ctr_mask(u32 ctr_addr, const u32 *mask, const u32 *value) -{ - enum { MADR_WNR_BIT = 31 }; - - dimcb_io_write(&g.dim2->MCTL, 0); /* clear transfer complete */ - - if (mask[0] != 0) - dimcb_io_write(&g.dim2->MDAT0, value[0]); - if (mask[1] != 0) - dimcb_io_write(&g.dim2->MDAT1, value[1]); - if (mask[2] != 0) - dimcb_io_write(&g.dim2->MDAT2, value[2]); - if (mask[3] != 0) - dimcb_io_write(&g.dim2->MDAT3, value[3]); - - dimcb_io_write(&g.dim2->MDWE0, mask[0]); - dimcb_io_write(&g.dim2->MDWE1, mask[1]); - dimcb_io_write(&g.dim2->MDWE2, mask[2]); - dimcb_io_write(&g.dim2->MDWE3, mask[3]); - - dim2_transfer_madr(bit_mask(MADR_WNR_BIT) | ctr_addr); -} - -static inline void dim2_write_ctr(u32 ctr_addr, const u32 *value) -{ - u32 const mask[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - - dim2_write_ctr_mask(ctr_addr, mask, value); -} - -static inline void dim2_clear_ctr(u32 ctr_addr) -{ - u32 const value[4] = { 0, 0, 0, 0 }; - - dim2_write_ctr(ctr_addr, value); -} - -static void dim2_configure_cat(u8 cat_base, u8 ch_addr, u8 ch_type, - bool read_not_write) -{ - bool isoc_fce = ch_type == CAT_CT_VAL_ISOC; - bool sync_mfe = ch_type == CAT_CT_VAL_SYNC; - u16 const cat = - (read_not_write << CAT_RNW_BIT) | - (ch_type << CAT_CT_SHIFT) | - (ch_addr << CAT_CL_SHIFT) | - (isoc_fce << CAT_FCE_BIT) | - (sync_mfe << CAT_MFE_BIT) | - (false << CAT_MT_BIT) | - (true << CAT_CE_BIT); - u8 const ctr_addr = cat_base + ch_addr / 8; - u8 const idx = (ch_addr % 8) / 2; - u8 const shift = (ch_addr % 2) * 16; - u32 mask[4] = { 0, 0, 0, 0 }; - u32 value[4] = { 0, 0, 0, 0 }; - - mask[idx] = (u32)0xFFFF << shift; - value[idx] = cat << shift; - dim2_write_ctr_mask(ctr_addr, mask, value); -} - -static void dim2_clear_cat(u8 cat_base, u8 ch_addr) -{ - u8 const ctr_addr = cat_base + ch_addr / 8; - u8 const idx = (ch_addr % 8) / 2; - u8 const shift = (ch_addr % 2) * 16; - u32 mask[4] = { 0, 0, 0, 0 }; - u32 value[4] = { 0, 0, 0, 0 }; - - mask[idx] = (u32)0xFFFF << shift; - dim2_write_ctr_mask(ctr_addr, mask, value); -} - -static void dim2_configure_cdt(u8 ch_addr, u16 dbr_address, u16 hw_buffer_size, - u16 packet_length) -{ - u32 cdt[4] = { 0, 0, 0, 0 }; - - if (packet_length) - cdt[1] = ((packet_length - 1) << CDT1_BS_ISOC_SHIFT); - - cdt[3] = - ((hw_buffer_size - 1) << CDT3_BD_SHIFT) | - (dbr_address << CDT3_BA_SHIFT); - dim2_write_ctr(CDT + ch_addr, cdt); -} - -static u16 dim2_rpc(u8 ch_addr) -{ - u32 cdt0 = dim2_read_ctr(CDT + ch_addr, 0); - - return (cdt0 >> CDT0_RPC_SHIFT) & CDT0_RPC_MASK; -} - -static void dim2_clear_cdt(u8 ch_addr) -{ - u32 cdt[4] = { 0, 0, 0, 0 }; - - dim2_write_ctr(CDT + ch_addr, cdt); -} - -static void dim2_configure_adt(u8 ch_addr) -{ - u32 adt[4] = { 0, 0, 0, 0 }; - - adt[0] = - (true << ADT0_CE_BIT) | - (true << ADT0_LE_BIT) | - (0 << ADT0_PG_BIT); - - dim2_write_ctr(ADT + ch_addr, adt); -} - -static void dim2_clear_adt(u8 ch_addr) -{ - u32 adt[4] = { 0, 0, 0, 0 }; - - dim2_write_ctr(ADT + ch_addr, adt); -} - -static void dim2_start_ctrl_async(u8 ch_addr, u8 idx, u32 buf_addr, - u16 buffer_size) -{ - u8 const shift = idx * 16; - - u32 mask[4] = { 0, 0, 0, 0 }; - u32 adt[4] = { 0, 0, 0, 0 }; - - mask[1] = - bit_mask(ADT1_PS_BIT + shift) | - bit_mask(ADT1_RDY_BIT + shift) | - (ADT1_CTRL_ASYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); - adt[1] = - (true << (ADT1_PS_BIT + shift)) | - (true << (ADT1_RDY_BIT + shift)) | - ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); - - mask[idx + 2] = 0xFFFFFFFF; - adt[idx + 2] = buf_addr; - - dim2_write_ctr_mask(ADT + ch_addr, mask, adt); -} - -static void dim2_start_isoc_sync(u8 ch_addr, u8 idx, u32 buf_addr, - u16 buffer_size) -{ - u8 const shift = idx * 16; - - u32 mask[4] = { 0, 0, 0, 0 }; - u32 adt[4] = { 0, 0, 0, 0 }; - - mask[1] = - bit_mask(ADT1_RDY_BIT + shift) | - (ADT1_ISOC_SYNC_BD_MASK << (ADT1_BD_SHIFT + shift)); - adt[1] = - (true << (ADT1_RDY_BIT + shift)) | - ((buffer_size - 1) << (ADT1_BD_SHIFT + shift)); - - mask[idx + 2] = 0xFFFFFFFF; - adt[idx + 2] = buf_addr; - - dim2_write_ctr_mask(ADT + ch_addr, mask, adt); -} - -static void dim2_clear_ctram(void) -{ - u32 ctr_addr; - - for (ctr_addr = 0; ctr_addr < 0x90; ctr_addr++) - dim2_clear_ctr(ctr_addr); -} - -static void dim2_configure_channel( - u8 ch_addr, u8 type, u8 is_tx, u16 dbr_address, u16 hw_buffer_size, - u16 packet_length) -{ - dim2_configure_cdt(ch_addr, dbr_address, hw_buffer_size, packet_length); - dim2_configure_cat(MLB_CAT, ch_addr, type, is_tx ? 1 : 0); - - dim2_configure_adt(ch_addr); - dim2_configure_cat(AHB_CAT, ch_addr, type, is_tx ? 0 : 1); - - /* unmask interrupt for used channel, enable mlb_sys_int[0] interrupt */ - dimcb_io_write(&g.dim2->ACMR0, - dimcb_io_read(&g.dim2->ACMR0) | bit_mask(ch_addr)); -} - -static void dim2_clear_channel(u8 ch_addr) -{ - /* mask interrupt for used channel, disable mlb_sys_int[0] interrupt */ - dimcb_io_write(&g.dim2->ACMR0, - dimcb_io_read(&g.dim2->ACMR0) & ~bit_mask(ch_addr)); - - dim2_clear_cat(AHB_CAT, ch_addr); - dim2_clear_adt(ch_addr); - - dim2_clear_cat(MLB_CAT, ch_addr); - dim2_clear_cdt(ch_addr); - - /* clear channel status bit */ - dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); -} - -/* -------------------------------------------------------------------------- */ -/* trace async tx dbr fill state */ - -static inline u16 norm_pc(u16 pc) -{ - return pc & CDT0_RPC_MASK; -} - -static void dbrcnt_init(u8 ch_addr, u16 dbr_size) -{ - g.atx_dbr.rest_size = dbr_size; - g.atx_dbr.rpc = dim2_rpc(ch_addr); - g.atx_dbr.wpc = g.atx_dbr.rpc; -} - -static void dbrcnt_enq(int buf_sz) -{ - g.atx_dbr.rest_size -= buf_sz; - g.atx_dbr.sz_queue[norm_pc(g.atx_dbr.wpc)] = buf_sz; - g.atx_dbr.wpc++; -} - -u16 dim_dbr_space(struct dim_channel *ch) -{ - u16 cur_rpc; - struct async_tx_dbr *dbr = &g.atx_dbr; - - if (ch->addr != dbr->ch_addr) - return 0xFFFF; - - cur_rpc = dim2_rpc(ch->addr); - - while (norm_pc(dbr->rpc) != cur_rpc) { - dbr->rest_size += dbr->sz_queue[norm_pc(dbr->rpc)]; - dbr->rpc++; - } - - if ((u16)(dbr->wpc - dbr->rpc) >= CDT0_RPC_MASK) - return 0; - - return dbr->rest_size; -} - -/* -------------------------------------------------------------------------- */ -/* channel state helpers */ - -static void state_init(struct int_ch_state *state) -{ - state->request_counter = 0; - state->service_counter = 0; - - state->idx1 = 0; - state->idx2 = 0; - state->level = 0; -} - -/* -------------------------------------------------------------------------- */ -/* macro helper functions */ - -static inline bool check_channel_address(u32 ch_address) -{ - return ch_address > 0 && (ch_address % 2) == 0 && - (ch_address / 2) <= (u32)CAT_CL_MASK; -} - -static inline bool check_packet_length(u32 packet_length) -{ - u16 const max_size = ((u16)CDT3_BD_ISOC_MASK + 1u) / ISOC_DBR_FACTOR; - - if (packet_length <= 0) - return false; /* too small */ - - if (packet_length > max_size) - return false; /* too big */ - - if (packet_length - 1u > (u32)CDT1_BS_ISOC_MASK) - return false; /* too big */ - - return true; -} - -static inline bool check_bytes_per_frame(u32 bytes_per_frame) -{ - u16 const bd_factor = g.fcnt + 2; - u16 const max_size = ((u16)CDT3_BD_MASK + 1u) >> bd_factor; - - if (bytes_per_frame <= 0) - return false; /* too small */ - - if (bytes_per_frame > max_size) - return false; /* too big */ - - return true; -} - -static inline u16 norm_ctrl_async_buffer_size(u16 buf_size) -{ - u16 const max_size = (u16)ADT1_CTRL_ASYNC_BD_MASK + 1u; - - if (buf_size > max_size) - return max_size; - - return buf_size; -} - -static inline u16 norm_isoc_buffer_size(u16 buf_size, u16 packet_length) -{ - u16 n; - u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; - - if (buf_size > max_size) - buf_size = max_size; - - n = buf_size / packet_length; - - if (n < 2u) - return 0; /* too small buffer for given packet_length */ - - return packet_length * n; -} - -static inline u16 norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) -{ - u16 n; - u16 const max_size = (u16)ADT1_ISOC_SYNC_BD_MASK + 1u; - u32 const unit = bytes_per_frame << g.fcnt; - - if (buf_size > max_size) - buf_size = max_size; - - n = buf_size / unit; - - if (n < 1u) - return 0; /* too small buffer for given bytes_per_frame */ - - return unit * n; -} - -static void dim2_cleanup(void) -{ - /* disable MediaLB */ - dimcb_io_write(&g.dim2->MLBC0, false << MLBC0_MLBEN_BIT); - - dim2_clear_ctram(); - - /* disable mlb_int interrupt */ - dimcb_io_write(&g.dim2->MIEN, 0); - - /* clear status for all dma channels */ - dimcb_io_write(&g.dim2->ACSR0, 0xFFFFFFFF); - dimcb_io_write(&g.dim2->ACSR1, 0xFFFFFFFF); - - /* mask interrupts for all channels */ - dimcb_io_write(&g.dim2->ACMR0, 0); - dimcb_io_write(&g.dim2->ACMR1, 0); -} - -static void dim2_initialize(bool enable_6pin, u8 mlb_clock) -{ - dim2_cleanup(); - - /* configure and enable MediaLB */ - dimcb_io_write(&g.dim2->MLBC0, - enable_6pin << MLBC0_MLBPEN_BIT | - mlb_clock << MLBC0_MLBCLK_SHIFT | - g.fcnt << MLBC0_FCNT_SHIFT | - true << MLBC0_MLBEN_BIT); - - /* activate all HBI channels */ - dimcb_io_write(&g.dim2->HCMR0, 0xFFFFFFFF); - dimcb_io_write(&g.dim2->HCMR1, 0xFFFFFFFF); - - /* enable HBI */ - dimcb_io_write(&g.dim2->HCTL, bit_mask(HCTL_EN_BIT)); - - /* configure DMA */ - dimcb_io_write(&g.dim2->ACTL, - ACTL_DMA_MODE_VAL_DMA_MODE_1 << ACTL_DMA_MODE_BIT | - true << ACTL_SCE_BIT); -} - -static bool dim2_is_mlb_locked(void) -{ - u32 const mask0 = bit_mask(MLBC0_MLBLK_BIT); - u32 const mask1 = bit_mask(MLBC1_CLKMERR_BIT) | - bit_mask(MLBC1_LOCKERR_BIT); - u32 const c1 = dimcb_io_read(&g.dim2->MLBC1); - u32 const nda_mask = (u32)MLBC1_NDA_MASK << MLBC1_NDA_SHIFT; - - dimcb_io_write(&g.dim2->MLBC1, c1 & nda_mask); - return (dimcb_io_read(&g.dim2->MLBC1) & mask1) == 0 && - (dimcb_io_read(&g.dim2->MLBC0) & mask0) != 0; -} - -/* -------------------------------------------------------------------------- */ -/* channel help routines */ - -static inline bool service_channel(u8 ch_addr, u8 idx) -{ - u8 const shift = idx * 16; - u32 const adt1 = dim2_read_ctr(ADT + ch_addr, 1); - u32 mask[4] = { 0, 0, 0, 0 }; - u32 adt_w[4] = { 0, 0, 0, 0 }; - - if (((adt1 >> (ADT1_DNE_BIT + shift)) & 1) == 0) - return false; - - mask[1] = - bit_mask(ADT1_DNE_BIT + shift) | - bit_mask(ADT1_ERR_BIT + shift) | - bit_mask(ADT1_RDY_BIT + shift); - dim2_write_ctr_mask(ADT + ch_addr, mask, adt_w); - - /* clear channel status bit */ - dimcb_io_write(&g.dim2->ACSR0, bit_mask(ch_addr)); - - return true; -} - -/* -------------------------------------------------------------------------- */ -/* channel init routines */ - -static void isoc_init(struct dim_channel *ch, u8 ch_addr, u16 packet_length) -{ - state_init(&ch->state); - - ch->addr = ch_addr; - - ch->packet_length = packet_length; - ch->bytes_per_frame = 0; - ch->done_sw_buffers_number = 0; -} - -static void sync_init(struct dim_channel *ch, u8 ch_addr, u16 bytes_per_frame) -{ - state_init(&ch->state); - - ch->addr = ch_addr; - - ch->packet_length = 0; - ch->bytes_per_frame = bytes_per_frame; - ch->done_sw_buffers_number = 0; -} - -static void channel_init(struct dim_channel *ch, u8 ch_addr) -{ - state_init(&ch->state); - - ch->addr = ch_addr; - - ch->packet_length = 0; - ch->bytes_per_frame = 0; - ch->done_sw_buffers_number = 0; -} - -/* returns true if channel interrupt state is cleared */ -static bool channel_service_interrupt(struct dim_channel *ch) -{ - struct int_ch_state *const state = &ch->state; - - if (!service_channel(ch->addr, state->idx2)) - return false; - - state->idx2 ^= 1; - state->request_counter++; - return true; -} - -static bool channel_start(struct dim_channel *ch, u32 buf_addr, u16 buf_size) -{ - struct int_ch_state *const state = &ch->state; - - if (buf_size <= 0) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, "Bad buffer size"); - - if (ch->packet_length == 0 && ch->bytes_per_frame == 0 && - buf_size != norm_ctrl_async_buffer_size(buf_size)) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, - "Bad control/async buffer size"); - - if (ch->packet_length && - buf_size != norm_isoc_buffer_size(buf_size, ch->packet_length)) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, - "Bad isochronous buffer size"); - - if (ch->bytes_per_frame && - buf_size != norm_sync_buffer_size(buf_size, ch->bytes_per_frame)) - return dim_on_error(DIM_ERR_BAD_BUFFER_SIZE, - "Bad synchronous buffer size"); - - if (state->level >= 2u) - return dim_on_error(DIM_ERR_OVERFLOW, "Channel overflow"); - - ++state->level; - - if (ch->addr == g.atx_dbr.ch_addr) - dbrcnt_enq(buf_size); - - if (ch->packet_length || ch->bytes_per_frame) - dim2_start_isoc_sync(ch->addr, state->idx1, buf_addr, buf_size); - else - dim2_start_ctrl_async(ch->addr, state->idx1, buf_addr, - buf_size); - state->idx1 ^= 1; - - return true; -} - -static u8 channel_service(struct dim_channel *ch) -{ - struct int_ch_state *const state = &ch->state; - - if (state->service_counter != state->request_counter) { - state->service_counter++; - if (state->level == 0) - return DIM_ERR_UNDERFLOW; - - --state->level; - ch->done_sw_buffers_number++; - } - - return DIM_NO_ERROR; -} - -static bool channel_detach_buffers(struct dim_channel *ch, u16 buffers_number) -{ - if (buffers_number > ch->done_sw_buffers_number) - return dim_on_error(DIM_ERR_UNDERFLOW, "Channel underflow"); - - ch->done_sw_buffers_number -= buffers_number; - return true; -} - -/* -------------------------------------------------------------------------- */ -/* API */ - -u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, - u32 fcnt) -{ - g.dim_is_initialized = false; - - if (!dim_base_address) - return DIM_INIT_ERR_DIM_ADDR; - - /* MediaLB clock: 0 - 256 fs, 1 - 512 fs, 2 - 1024 fs, 3 - 2048 fs */ - /* MediaLB clock: 4 - 3072 fs, 5 - 4096 fs, 6 - 6144 fs, 7 - 8192 fs */ - if (mlb_clock >= 8) - return DIM_INIT_ERR_MLB_CLOCK; - - if (fcnt > MLBC0_FCNT_MAX_VAL) - return DIM_INIT_ERR_MLB_CLOCK; - - g.dim2 = dim_base_address; - g.fcnt = fcnt; - g.dbr_map[0] = 0; - g.dbr_map[1] = 0; - - dim2_initialize(mlb_clock >= 3, mlb_clock); - - g.dim_is_initialized = true; - - return DIM_NO_ERROR; -} - -void dim_shutdown(void) -{ - g.dim_is_initialized = false; - dim2_cleanup(); -} - -bool dim_get_lock_state(void) -{ - return dim2_is_mlb_locked(); -} - -static u8 init_ctrl_async(struct dim_channel *ch, u8 type, u8 is_tx, - u16 ch_address, u16 hw_buffer_size) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (!check_channel_address(ch_address)) - return DIM_INIT_ERR_CHANNEL_ADDRESS; - - ch->dbr_size = ROUND_UP_TO(hw_buffer_size, DBR_BLOCK_SIZE); - ch->dbr_addr = alloc_dbr(ch->dbr_size); - if (ch->dbr_addr >= DBR_SIZE) - return DIM_INIT_ERR_OUT_OF_MEMORY; - - channel_init(ch, ch_address / 2); - - dim2_configure_channel(ch->addr, type, is_tx, - ch->dbr_addr, ch->dbr_size, 0); - - return DIM_NO_ERROR; -} - -void dim_service_mlb_int_irq(void) -{ - dimcb_io_write(&g.dim2->MS0, 0); - dimcb_io_write(&g.dim2->MS1, 0); -} - -u16 dim_norm_ctrl_async_buffer_size(u16 buf_size) -{ - return norm_ctrl_async_buffer_size(buf_size); -} - -/** - * Retrieves maximal possible correct buffer size for isochronous data type - * conform to given packet length and not bigger than given buffer size. - * - * Returns non-zero correct buffer size or zero by error. - */ -u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length) -{ - if (!check_packet_length(packet_length)) - return 0; - - return norm_isoc_buffer_size(buf_size, packet_length); -} - -/** - * Retrieves maximal possible correct buffer size for synchronous data type - * conform to given bytes per frame and not bigger than given buffer size. - * - * Returns non-zero correct buffer size or zero by error. - */ -u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame) -{ - if (!check_bytes_per_frame(bytes_per_frame)) - return 0; - - return norm_sync_buffer_size(buf_size, bytes_per_frame); -} - -u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size) -{ - return init_ctrl_async(ch, CAT_CT_VAL_CONTROL, is_tx, ch_address, - max_buffer_size); -} - -u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size) -{ - u8 ret = init_ctrl_async(ch, CAT_CT_VAL_ASYNC, is_tx, ch_address, - max_buffer_size); - - if (is_tx && !g.atx_dbr.ch_addr) { - g.atx_dbr.ch_addr = ch->addr; - dbrcnt_init(ch->addr, ch->dbr_size); - dimcb_io_write(&g.dim2->MIEN, bit_mask(20)); - } - - return ret; -} - -u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 packet_length) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (!check_channel_address(ch_address)) - return DIM_INIT_ERR_CHANNEL_ADDRESS; - - if (!check_packet_length(packet_length)) - return DIM_ERR_BAD_CONFIG; - - ch->dbr_size = packet_length * ISOC_DBR_FACTOR; - ch->dbr_addr = alloc_dbr(ch->dbr_size); - if (ch->dbr_addr >= DBR_SIZE) - return DIM_INIT_ERR_OUT_OF_MEMORY; - - isoc_init(ch, ch_address / 2, packet_length); - - dim2_configure_channel(ch->addr, CAT_CT_VAL_ISOC, is_tx, ch->dbr_addr, - ch->dbr_size, packet_length); - - return DIM_NO_ERROR; -} - -u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 bytes_per_frame) -{ - u16 bd_factor = g.fcnt + 2; - - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (!check_channel_address(ch_address)) - return DIM_INIT_ERR_CHANNEL_ADDRESS; - - if (!check_bytes_per_frame(bytes_per_frame)) - return DIM_ERR_BAD_CONFIG; - - ch->dbr_size = bytes_per_frame << bd_factor; - ch->dbr_addr = alloc_dbr(ch->dbr_size); - if (ch->dbr_addr >= DBR_SIZE) - return DIM_INIT_ERR_OUT_OF_MEMORY; - - sync_init(ch, ch_address / 2, bytes_per_frame); - - dim2_clear_dbr(ch->dbr_addr, ch->dbr_size); - dim2_configure_channel(ch->addr, CAT_CT_VAL_SYNC, is_tx, - ch->dbr_addr, ch->dbr_size, 0); - - return DIM_NO_ERROR; -} - -u8 dim_destroy_channel(struct dim_channel *ch) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - if (ch->addr == g.atx_dbr.ch_addr) { - dimcb_io_write(&g.dim2->MIEN, 0); - g.atx_dbr.ch_addr = 0; - } - - dim2_clear_channel(ch->addr); - if (ch->dbr_addr < DBR_SIZE) - free_dbr(ch->dbr_addr, ch->dbr_size); - ch->dbr_addr = DBR_SIZE; - - return DIM_NO_ERROR; -} - -void dim_service_ahb_int_irq(struct dim_channel *const *channels) -{ - bool state_changed; - - if (!g.dim_is_initialized) { - dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, - "DIM is not initialized"); - return; - } - - if (!channels) { - dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, "Bad channels"); - return; - } - - /* - * Use while-loop and a flag to make sure the age is changed back at - * least once, otherwise the interrupt may never come if CPU generates - * interrupt on changing age. - * This cycle runs not more than number of channels, because - * channel_service_interrupt() routine doesn't start the channel again. - */ - do { - struct dim_channel *const *ch = channels; - - state_changed = false; - - while (*ch) { - state_changed |= channel_service_interrupt(*ch); - ++ch; - } - } while (state_changed); -} - -u8 dim_service_channel(struct dim_channel *ch) -{ - if (!g.dim_is_initialized || !ch) - return DIM_ERR_DRIVER_NOT_INITIALIZED; - - return channel_service(ch); -} - -struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, - struct dim_ch_state_t *state_ptr) -{ - if (!ch || !state_ptr) - return NULL; - - state_ptr->ready = ch->state.level < 2; - state_ptr->done_buffers = ch->done_sw_buffers_number; - - return state_ptr; -} - -bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, - u16 buffer_size) -{ - if (!ch) - return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, - "Bad channel"); - - return channel_start(ch, buffer_addr, buffer_size); -} - -bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number) -{ - if (!ch) - return dim_on_error(DIM_ERR_DRIVER_NOT_INITIALIZED, - "Bad channel"); - - return channel_detach_buffers(ch, buffers_number); -} diff --git a/drivers/staging/most/hdm-dim2/dim2_hal.h b/drivers/staging/most/hdm-dim2/dim2_hal.h deleted file mode 100644 index fce9ae96121b..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hal.h +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hal.h - DIM2 HAL interface - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef _DIM2_HAL_H -#define _DIM2_HAL_H - -#include -#include "dim2_reg.h" - -/* - * The values below are specified in the hardware specification. - * So, they should not be changed until the hardware specification changes. - */ -enum mlb_clk_speed { - CLK_256FS = 0, - CLK_512FS = 1, - CLK_1024FS = 2, - CLK_2048FS = 3, - CLK_3072FS = 4, - CLK_4096FS = 5, - CLK_6144FS = 6, - CLK_8192FS = 7, -}; - -struct dim_ch_state_t { - bool ready; /* Shows readiness to enqueue next buffer */ - u16 done_buffers; /* Number of completed buffers */ -}; - -struct int_ch_state { - /* changed only in interrupt context */ - volatile int request_counter; - - /* changed only in task context */ - volatile int service_counter; - - u8 idx1; - u8 idx2; - u8 level; /* [0..2], buffering level */ -}; - -struct dim_channel { - struct int_ch_state state; - u8 addr; - u16 dbr_addr; - u16 dbr_size; - u16 packet_length; /*< Isochronous packet length in bytes. */ - u16 bytes_per_frame; /*< Synchronous bytes per frame. */ - u16 done_sw_buffers_number; /*< Done software buffers number. */ -}; - -u8 dim_startup(struct dim2_regs __iomem *dim_base_address, u32 mlb_clock, - u32 fcnt); - -void dim_shutdown(void); - -bool dim_get_lock_state(void); - -u16 dim_norm_ctrl_async_buffer_size(u16 buf_size); - -u16 dim_norm_isoc_buffer_size(u16 buf_size, u16 packet_length); - -u16 dim_norm_sync_buffer_size(u16 buf_size, u16 bytes_per_frame); - -u8 dim_init_control(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size); - -u8 dim_init_async(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 max_buffer_size); - -u8 dim_init_isoc(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 packet_length); - -u8 dim_init_sync(struct dim_channel *ch, u8 is_tx, u16 ch_address, - u16 bytes_per_frame); - -u8 dim_destroy_channel(struct dim_channel *ch); - -void dim_service_mlb_int_irq(void); - -void dim_service_ahb_int_irq(struct dim_channel *const *channels); - -u8 dim_service_channel(struct dim_channel *ch); - -struct dim_ch_state_t *dim_get_channel_state(struct dim_channel *ch, - struct dim_ch_state_t *state_ptr); - -u16 dim_dbr_space(struct dim_channel *ch); - -bool dim_enqueue_buffer(struct dim_channel *ch, u32 buffer_addr, - u16 buffer_size); - -bool dim_detach_buffers(struct dim_channel *ch, u16 buffers_number); - -u32 dimcb_io_read(u32 __iomem *ptr32); - -void dimcb_io_write(u32 __iomem *ptr32, u32 value); - -void dimcb_on_error(u8 error_id, const char *error_message); - -#endif /* _DIM2_HAL_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.c b/drivers/staging/most/hdm-dim2/dim2_hdm.c deleted file mode 100644 index fedd2d06742a..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.c +++ /dev/null @@ -1,912 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hdm.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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "most/core.h" -#include "dim2_hal.h" -#include "dim2_hdm.h" -#include "dim2_errors.h" -#include "dim2_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) - -/* command line parameter to select clock speed */ -static char *clock_speed; -module_param(clock_speed, charp, 0000); -MODULE_PARM_DESC(clock_speed, "MediaLB Clock Speed"); - -/* - * 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); - -static void dim2_tasklet_fn(unsigned long data); -static DECLARE_TASKLET(dim2_tasklet, dim2_tasklet_fn, 0); - -/** - * struct hdm_channel - private structure to keep channel specific data - * @is_initialized: identifier to know whether the channel is initialized - * @ch: HAL specific channel data - * @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; - 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 - * @clk_speed: user selectable (through command line parameter) clock speed - * @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 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; - int clk_speed; - 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 *, - unsigned char, unsigned char *); -}; - -#define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface) - -/* Macro to identify a network status message */ -#define PACKET_IS_NET_INFO(p) \ - (((p)[1] == 0x18) && ((p)[2] == 0x05) && ((p)[3] == 0x0C) && \ - ((p)[13] == 0x3C) && ((p)[14] == 0x00) && ((p)[15] == 0x0A)) - -bool dim2_sysfs_get_state_cb(void) -{ - bool state; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - state = dim_get_lock_state(); - spin_unlock_irqrestore(&dim_lock, flags); - - return state; -} - -/** - * dimcb_io_read - callback from HAL to read an I/O register - * @ptr32: register address - */ -u32 dimcb_io_read(u32 __iomem *ptr32) -{ - return readl(ptr32); -} - -/** - * dimcb_io_write - callback from HAL to write value to an I/O register - * @ptr32: register address - * @value: value to write - */ -void dimcb_io_write(u32 __iomem *ptr32, u32 value) -{ - writel(value, ptr32); -} - -/** - * 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) -{ - pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id, - error_message); -} - -/** - * startup_dim - initialize the dim2 interface - * @pdev: platform device - * - * Get the value of command line parameter "clock_speed" if given or use the - * default value, enable the clock and PLL, and initialize the dim2 interface. - */ -static int startup_dim(struct platform_device *pdev) -{ - struct dim2_hdm *dev = platform_get_drvdata(pdev); - struct dim2_platform_data *pdata = pdev->dev.platform_data; - u8 hal_ret; - - dev->clk_speed = -1; - - if (clock_speed) { - if (!strcmp(clock_speed, "256fs")) - dev->clk_speed = CLK_256FS; - else if (!strcmp(clock_speed, "512fs")) - dev->clk_speed = CLK_512FS; - else if (!strcmp(clock_speed, "1024fs")) - dev->clk_speed = CLK_1024FS; - else if (!strcmp(clock_speed, "2048fs")) - dev->clk_speed = CLK_2048FS; - else if (!strcmp(clock_speed, "3072fs")) - dev->clk_speed = CLK_3072FS; - else if (!strcmp(clock_speed, "4096fs")) - dev->clk_speed = CLK_4096FS; - else if (!strcmp(clock_speed, "6144fs")) - dev->clk_speed = CLK_6144FS; - else if (!strcmp(clock_speed, "8192fs")) - dev->clk_speed = CLK_8192FS; - } - - if (dev->clk_speed == -1) { - pr_info("Bad or missing clock speed parameter, using default value: 3072fs\n"); - dev->clk_speed = CLK_3072FS; - } else { - pr_info("Selected clock speed: %s\n", clock_speed); - } - if (pdata && pdata->init) { - int ret = pdata->init(pdata, dev->io_base, dev->clk_speed); - - if (ret) - return ret; - } - - pr_info("sync: num of frames per sub-buffer: %u\n", fcnt); - hal_ret = dim_startup(dev->io_base, dev->clk_speed, fcnt); - if (hal_ret != DIM_NO_ERROR) { - pr_err("dim_startup failed: %d\n", hal_ret); - if (pdata && pdata->destroy) - pdata->destroy(pdata); - return -ENODEV; - } - - return 0; -} - -/** - * 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) -{ - u16 buf_size; - struct list_head *head = &hdm_ch->pending_list; - struct mbo *mbo; - unsigned long flags; - struct dim_ch_state_t st; - - BUG_ON(!hdm_ch); - BUG_ON(!hdm_ch->is_initialized); - - spin_lock_irqsave(&dim_lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(&dim_lock, flags); - return -EAGAIN; - } - - if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) { - spin_unlock_irqrestore(&dim_lock, flags); - return -EAGAIN; - } - - mbo = list_first_entry(head, struct mbo, list); - buf_size = mbo->buffer_length; - - if (dim_dbr_space(&hdm_ch->ch) < buf_size) { - spin_unlock_irqrestore(&dim_lock, flags); - return -EAGAIN; - } - - BUG_ON(mbo->bus_address == 0); - if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) { - list_del(head->next); - spin_unlock_irqrestore(&dim_lock, flags); - mbo->processed_length = 0; - mbo->status = MBO_E_INVAL; - mbo->complete(mbo); - return -EFAULT; - } - - list_move_tail(head->next, &hdm_ch->started_list); - spin_unlock_irqrestore(&dim_lock, flags); - - return 0; -} - -/** - * 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) -{ - struct dim2_hdm *dev = data; - - while (!kthread_should_stop()) { - wait_event_interruptible(dev->netinfo_waitq, - dev->deliver_netinfo || - kthread_should_stop()); - - if (dev->deliver_netinfo) { - dev->deliver_netinfo--; - if (dev->on_netinfo) { - dev->on_netinfo(&dev->most_iface, - dev->link_state, - dev->mac_addrs); - } - } - } - - return 0; -} - -/** - * 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) -{ - u8 *data = mbo->virt_address; - - pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]); - dev->link_state = data[18]; - pr_info("NIState: %d\n", dev->link_state); - memcpy(dev->mac_addrs, data + 19, 6); - dev->deliver_netinfo++; - wake_up_interruptible(&dev->netinfo_waitq); -} - -/** - * 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) -{ - struct hdm_channel *hdm_ch = dev->hch + ch_idx; - struct dim_ch_state_t st; - struct list_head *head; - struct mbo *mbo; - int done_buffers; - unsigned long flags; - u8 *data; - - BUG_ON(!hdm_ch); - BUG_ON(!hdm_ch->is_initialized); - - spin_lock_irqsave(&dim_lock, flags); - - done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers; - if (!done_buffers) { - spin_unlock_irqrestore(&dim_lock, flags); - return; - } - - if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) { - spin_unlock_irqrestore(&dim_lock, flags); - return; - } - spin_unlock_irqrestore(&dim_lock, flags); - - head = &hdm_ch->started_list; - - while (done_buffers) { - spin_lock_irqsave(&dim_lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(&dim_lock, flags); - pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n"); - break; - } - - mbo = list_first_entry(head, struct mbo, list); - list_del(head->next); - spin_unlock_irqrestore(&dim_lock, flags); - - data = mbo->virt_address; - - if (hdm_ch->data_type == MOST_CH_ASYNC && - hdm_ch->direction == MOST_CH_RX && - PACKET_IS_NET_INFO(data)) { - retrieve_netinfo(dev, mbo); - - spin_lock_irqsave(&dim_lock, flags); - list_add_tail(&mbo->list, &hdm_ch->pending_list); - spin_unlock_irqrestore(&dim_lock, flags); - } else { - if (hdm_ch->data_type == MOST_CH_CONTROL || - hdm_ch->data_type == MOST_CH_ASYNC) { - u32 const data_size = - (u32)data[0] * 256 + data[1] + 2; - - mbo->processed_length = - min_t(u32, data_size, - mbo->buffer_length); - } else { - mbo->processed_length = mbo->buffer_length; - } - mbo->status = MBO_SUCCESS; - mbo->complete(mbo); - } - - done_buffers--; - } -} - -static struct dim_channel **get_active_channels(struct dim2_hdm *dev, - struct dim_channel **buffer) -{ - int idx = 0; - int ch_idx; - - for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { - if (dev->hch[ch_idx].is_initialized) - buffer[idx++] = &dev->hch[ch_idx].ch; - } - buffer[idx++] = NULL; - - return buffer; -} - -static irqreturn_t dim2_mlb_isr(int irq, void *_dev) -{ - struct dim2_hdm *dev = _dev; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - dim_service_mlb_int_irq(); - spin_unlock_irqrestore(&dim_lock, flags); - - if (dev->atx_idx >= 0 && dev->hch[dev->atx_idx].is_initialized) - while (!try_start_dim_transfer(dev->hch + dev->atx_idx)) - continue; - - return IRQ_HANDLED; -} - -/** - * dim2_tasklet_fn - tasklet function - * @data: private data - * - * Service each initialized channel, if needed - */ -static void dim2_tasklet_fn(unsigned long data) -{ - struct dim2_hdm *dev = (struct dim2_hdm *)data; - unsigned long flags; - int ch_idx; - - for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) { - if (!dev->hch[ch_idx].is_initialized) - continue; - - spin_lock_irqsave(&dim_lock, flags); - dim_service_channel(&dev->hch[ch_idx].ch); - spin_unlock_irqrestore(&dim_lock, flags); - - service_done_flag(dev, ch_idx); - while (!try_start_dim_transfer(dev->hch + ch_idx)) - continue; - } -} - -/** - * dim2_ahb_isr - interrupt service routine - * @irq: irq number - * @_dev: private data - * - * Acknowledge the interrupt and schedule a tasklet to service channels. - * Return IRQ_HANDLED. - */ -static irqreturn_t dim2_ahb_isr(int irq, void *_dev) -{ - struct dim2_hdm *dev = _dev; - struct dim_channel *buffer[DMA_CHANNELS + 1]; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - dim_service_ahb_int_irq(get_active_channels(dev, buffer)); - spin_unlock_irqrestore(&dim_lock, flags); - - dim2_tasklet.data = (unsigned long)dev; - tasklet_schedule(&dim2_tasklet); - return IRQ_HANDLED; -} - -/** - * 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) -{ - unsigned long flags; - struct mbo *mbo; - - for (;;) { - spin_lock_irqsave(&dim_lock, flags); - if (list_empty(head)) { - spin_unlock_irqrestore(&dim_lock, flags); - break; - } - - mbo = list_first_entry(head, struct mbo, list); - list_del(head->next); - spin_unlock_irqrestore(&dim_lock, flags); - - mbo->processed_length = 0; - mbo->status = MBO_E_CLOSE; - mbo->complete(mbo); - } -} - -/** - * configure_channel - initialize a channel - * @iface: interface the channel belongs to - * @channel: channel to be configured - * @channel_config: 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) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - bool const is_tx = ccfg->direction == MOST_CH_TX; - u16 const sub_size = ccfg->subbuffer_size; - u16 const buf_size = ccfg->buffer_size; - u16 new_size; - unsigned long flags; - u8 hal_ret; - int const ch_addr = ch_idx * 2 + 2; - struct hdm_channel *const hdm_ch = dev->hch + ch_idx; - - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); - - if (hdm_ch->is_initialized) - return -EPERM; - - switch (ccfg->data_type) { - case MOST_CH_CONTROL: - new_size = dim_norm_ctrl_async_buffer_size(buf_size); - if (new_size == 0) { - pr_err("%s: too small buffer size\n", hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr, - is_tx ? new_size * 2 : new_size); - break; - case MOST_CH_ASYNC: - new_size = dim_norm_ctrl_async_buffer_size(buf_size); - if (new_size == 0) { - pr_err("%s: too small buffer size\n", hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, - is_tx ? new_size * 2 : new_size); - break; - case MOST_CH_ISOC: - new_size = dim_norm_isoc_buffer_size(buf_size, sub_size); - if (new_size == 0) { - pr_err("%s: invalid sub-buffer size or too small buffer size\n", - hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size); - break; - case MOST_CH_SYNC: - new_size = dim_norm_sync_buffer_size(buf_size, sub_size); - if (new_size == 0) { - pr_err("%s: invalid sub-buffer size or too small buffer size\n", - hdm_ch->name); - return -EINVAL; - } - ccfg->buffer_size = new_size; - if (new_size != buf_size) - pr_warn("%s: fixed buffer size (%d -> %d)\n", - hdm_ch->name, buf_size, new_size); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size); - break; - default: - pr_err("%s: configure failed, bad channel type: %d\n", - hdm_ch->name, ccfg->data_type); - return -EINVAL; - } - - if (hal_ret != DIM_NO_ERROR) { - spin_unlock_irqrestore(&dim_lock, flags); - pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n", - hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx); - return -ENODEV; - } - - hdm_ch->data_type = ccfg->data_type; - hdm_ch->direction = ccfg->direction; - hdm_ch->is_initialized = true; - - if (hdm_ch->data_type == MOST_CH_ASYNC && - hdm_ch->direction == MOST_CH_TX && - dev->atx_idx < 0) - dev->atx_idx = ch_idx; - - spin_unlock_irqrestore(&dim_lock, flags); - - return 0; -} - -/** - * enqueue - enqueue a buffer for data transfer - * @iface: intended interface - * @channel: 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) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - struct hdm_channel *hdm_ch = dev->hch + ch_idx; - unsigned long flags; - - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); - - if (!hdm_ch->is_initialized) - return -EPERM; - - if (mbo->bus_address == 0) - return -EFAULT; - - spin_lock_irqsave(&dim_lock, flags); - list_add_tail(&mbo->list, &hdm_ch->pending_list); - spin_unlock_irqrestore(&dim_lock, flags); - - (void)try_start_dim_transfer(hdm_ch); - - return 0; -} - -/** - * request_netinfo - triggers retrieving of network info - * @iface: pointer to the interface - * @channel_id: corresponding channel ID - * - * 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 *)) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - struct mbo *mbo; - u8 *data; - - dev->on_netinfo = on_netinfo; - if (!on_netinfo) - return; - - if (dev->atx_idx < 0) { - pr_err("Async Tx Not initialized\n"); - return; - } - - mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL); - if (!mbo) - return; - - mbo->buffer_length = 5; - - data = mbo->virt_address; - - data[0] = 0x00; /* PML High byte */ - data[1] = 0x03; /* PML Low byte */ - data[2] = 0x02; /* PMHL */ - data[3] = 0x08; /* FPH */ - data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */ - - most_submit_mbo(mbo); -} - -/** - * poison_channel - poison buffers of a channel - * @iface: pointer to the interface the channel to be poisoned belongs to - * @channel_id: 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) -{ - struct dim2_hdm *dev = iface_to_hdm(most_iface); - struct hdm_channel *hdm_ch = dev->hch + ch_idx; - unsigned long flags; - u8 hal_ret; - int ret = 0; - - BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS); - - if (!hdm_ch->is_initialized) - return -EPERM; - - tasklet_disable(&dim2_tasklet); - spin_lock_irqsave(&dim_lock, flags); - hal_ret = dim_destroy_channel(&hdm_ch->ch); - hdm_ch->is_initialized = false; - if (ch_idx == dev->atx_idx) - dev->atx_idx = -1; - spin_unlock_irqrestore(&dim_lock, flags); - tasklet_enable(&dim2_tasklet); - if (hal_ret != DIM_NO_ERROR) { - pr_err("HAL Failed to close channel %s\n", hdm_ch->name); - ret = -EFAULT; - } - - complete_all_mbos(&hdm_ch->started_list); - complete_all_mbos(&hdm_ch->pending_list); - - return ret; -} - -/* - * 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) -{ - struct dim2_hdm *dev; - struct resource *res; - int ret, i; - struct kobject *kobj; - int irq; - - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - dev->atx_idx = -1; - - platform_set_drvdata(pdev, dev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - dev->io_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(dev->io_base)) - return PTR_ERR(dev->io_base); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get ahb0_int irq: %d\n", irq); - return irq; - } - - ret = devm_request_irq(&pdev->dev, irq, dim2_ahb_isr, 0, - "dim2_ahb0_int", dev); - if (ret) { - dev_err(&pdev->dev, "failed to request ahb0_int irq %d\n", irq); - return ret; - } - - irq = platform_get_irq(pdev, 1); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get mlb_int irq: %d\n", irq); - return irq; - } - - ret = devm_request_irq(&pdev->dev, irq, dim2_mlb_isr, 0, - "dim2_mlb_int", dev); - if (ret) { - dev_err(&pdev->dev, "failed to request mlb_int irq %d\n", irq); - return ret; - } - - init_waitqueue_head(&dev->netinfo_waitq); - dev->deliver_netinfo = 0; - dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev, - "dim2_netinfo"); - if (IS_ERR(dev->netinfo_task)) - return PTR_ERR(dev->netinfo_task); - - for (i = 0; i < DMA_CHANNELS; i++) { - struct most_channel_capability *cap = dev->capabilities + i; - struct hdm_channel *hdm_ch = dev->hch + i; - - INIT_LIST_HEAD(&hdm_ch->pending_list); - INIT_LIST_HEAD(&hdm_ch->started_list); - hdm_ch->is_initialized = false; - snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2); - - cap->name_suffix = hdm_ch->name; - cap->direction = MOST_CH_RX | MOST_CH_TX; - cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC | - MOST_CH_ISOC | MOST_CH_SYNC; - cap->num_buffers_packet = MAX_BUFFERS_PACKET; - cap->buffer_size_packet = MAX_BUF_SIZE_PACKET; - cap->num_buffers_streaming = MAX_BUFFERS_STREAMING; - cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING; - } - - { - const char *fmt; - - if (sizeof(res->start) == sizeof(long long)) - fmt = "dim2-%016llx"; - else if (sizeof(res->start) == sizeof(long)) - fmt = "dim2-%016lx"; - else - fmt = "dim2-%016x"; - - snprintf(dev->name, sizeof(dev->name), fmt, res->start); - } - - dev->most_iface.interface = ITYPE_MEDIALB_DIM2; - dev->most_iface.description = dev->name; - dev->most_iface.num_channels = DMA_CHANNELS; - dev->most_iface.channel_vector = dev->capabilities; - dev->most_iface.configure = configure_channel; - dev->most_iface.enqueue = enqueue; - dev->most_iface.poison_channel = poison_channel; - dev->most_iface.request_netinfo = request_netinfo; - - kobj = most_register_interface(&dev->most_iface); - if (IS_ERR(kobj)) { - ret = PTR_ERR(kobj); - dev_err(&pdev->dev, "failed to register MOST interface\n"); - goto err_stop_thread; - } - - ret = dim2_sysfs_probe(&dev->bus, kobj); - if (ret) - goto err_unreg_iface; - - ret = startup_dim(pdev); - if (ret) { - dev_err(&pdev->dev, "failed to initialize DIM2\n"); - goto err_destroy_bus; - } - - return 0; - -err_destroy_bus: - dim2_sysfs_destroy(&dev->bus); -err_unreg_iface: - most_deregister_interface(&dev->most_iface); -err_stop_thread: - kthread_stop(dev->netinfo_task); - - return ret; -} - -/** - * dim2_remove - dim2 remove handler - * @pdev: platform device structure - * - * Unregister the interface from mostcore - */ -static int dim2_remove(struct platform_device *pdev) -{ - struct dim2_hdm *dev = platform_get_drvdata(pdev); - struct dim2_platform_data *pdata = pdev->dev.platform_data; - unsigned long flags; - - spin_lock_irqsave(&dim_lock, flags); - dim_shutdown(); - spin_unlock_irqrestore(&dim_lock, flags); - - if (pdata && pdata->destroy) - pdata->destroy(pdata); - - dim2_sysfs_destroy(&dev->bus); - most_deregister_interface(&dev->most_iface); - kthread_stop(dev->netinfo_task); - - /* - * break link to local platform_device_id struct - * to prevent crash by unload platform device module - */ - pdev->id_entry = NULL; - - return 0; -} - -static const struct platform_device_id dim2_id[] = { - { "medialb_dim2" }, - { }, /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(platform, dim2_id); - -static struct platform_driver dim2_driver = { - .probe = dim2_probe, - .remove = dim2_remove, - .id_table = dim2_id, - .driver = { - .name = "hdm_dim2", - }, -}; - -module_platform_driver(dim2_driver); - -MODULE_AUTHOR("Jain Roy Ambi "); -MODULE_AUTHOR("Andrey Shvetsov "); -MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/most/hdm-dim2/dim2_hdm.h b/drivers/staging/most/hdm-dim2/dim2_hdm.h deleted file mode 100644 index 5f380b648bd7..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_hdm.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_hdm.h - MediaLB DIM2 HDM Header - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef DIM2_HDM_H -#define DIM2_HDM_H - -struct device; - -/* platform dependent data for dim2 interface */ -struct dim2_platform_data { - int (*init)(struct dim2_platform_data *pd, void __iomem *io_base, - int clk_speed); - void (*destroy)(struct dim2_platform_data *pd); - void *priv; -}; - -#endif /* DIM2_HDM_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_reg.h b/drivers/staging/most/hdm-dim2/dim2_reg.h deleted file mode 100644 index 2b2fca4f6451..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_reg.h +++ /dev/null @@ -1,157 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_reg.h - Definitions for registers of DIM2 - * (MediaLB, Device Interface Macro IP, OS62420) - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -#ifndef DIM2_OS62420_H -#define DIM2_OS62420_H - -#include - -struct dim2_regs { - /* 0x00 */ u32 MLBC0; - /* 0x01 */ u32 rsvd0[1]; - /* 0x02 */ u32 MLBPC0; - /* 0x03 */ u32 MS0; - /* 0x04 */ u32 rsvd1[1]; - /* 0x05 */ u32 MS1; - /* 0x06 */ u32 rsvd2[2]; - /* 0x08 */ u32 MSS; - /* 0x09 */ u32 MSD; - /* 0x0A */ u32 rsvd3[1]; - /* 0x0B */ u32 MIEN; - /* 0x0C */ u32 rsvd4[1]; - /* 0x0D */ u32 MLBPC2; - /* 0x0E */ u32 MLBPC1; - /* 0x0F */ u32 MLBC1; - /* 0x10 */ u32 rsvd5[0x10]; - /* 0x20 */ u32 HCTL; - /* 0x21 */ u32 rsvd6[1]; - /* 0x22 */ u32 HCMR0; - /* 0x23 */ u32 HCMR1; - /* 0x24 */ u32 HCER0; - /* 0x25 */ u32 HCER1; - /* 0x26 */ u32 HCBR0; - /* 0x27 */ u32 HCBR1; - /* 0x28 */ u32 rsvd7[8]; - /* 0x30 */ u32 MDAT0; - /* 0x31 */ u32 MDAT1; - /* 0x32 */ u32 MDAT2; - /* 0x33 */ u32 MDAT3; - /* 0x34 */ u32 MDWE0; - /* 0x35 */ u32 MDWE1; - /* 0x36 */ u32 MDWE2; - /* 0x37 */ u32 MDWE3; - /* 0x38 */ u32 MCTL; - /* 0x39 */ u32 MADR; - /* 0x3A */ u32 rsvd8[0xB6]; - /* 0xF0 */ u32 ACTL; - /* 0xF1 */ u32 rsvd9[3]; - /* 0xF4 */ u32 ACSR0; - /* 0xF5 */ u32 ACSR1; - /* 0xF6 */ u32 ACMR0; - /* 0xF7 */ u32 ACMR1; -}; - -#define DIM2_MASK(n) (~((~(u32)0) << (n))) - -enum { - MLBC0_MLBLK_BIT = 7, - - MLBC0_MLBPEN_BIT = 5, - - MLBC0_MLBCLK_SHIFT = 2, - MLBC0_MLBCLK_VAL_256FS = 0, - MLBC0_MLBCLK_VAL_512FS = 1, - MLBC0_MLBCLK_VAL_1024FS = 2, - MLBC0_MLBCLK_VAL_2048FS = 3, - - MLBC0_FCNT_SHIFT = 15, - MLBC0_FCNT_MASK = 7, - MLBC0_FCNT_MAX_VAL = 6, - - MLBC0_MLBEN_BIT = 0, - - MIEN_CTX_BREAK_BIT = 29, - MIEN_CTX_PE_BIT = 28, - MIEN_CTX_DONE_BIT = 27, - - MIEN_CRX_BREAK_BIT = 26, - MIEN_CRX_PE_BIT = 25, - MIEN_CRX_DONE_BIT = 24, - - MIEN_ATX_BREAK_BIT = 22, - MIEN_ATX_PE_BIT = 21, - MIEN_ATX_DONE_BIT = 20, - - MIEN_ARX_BREAK_BIT = 19, - MIEN_ARX_PE_BIT = 18, - MIEN_ARX_DONE_BIT = 17, - - MIEN_SYNC_PE_BIT = 16, - - MIEN_ISOC_BUFO_BIT = 1, - MIEN_ISOC_PE_BIT = 0, - - MLBC1_NDA_SHIFT = 8, - MLBC1_NDA_MASK = 0xFF, - - MLBC1_CLKMERR_BIT = 7, - MLBC1_LOCKERR_BIT = 6, - - ACTL_DMA_MODE_BIT = 2, - ACTL_DMA_MODE_VAL_DMA_MODE_0 = 0, - ACTL_DMA_MODE_VAL_DMA_MODE_1 = 1, - ACTL_SCE_BIT = 0, - - HCTL_EN_BIT = 15 -}; - -enum { - CDT0_RPC_SHIFT = 16 + 11, - CDT0_RPC_MASK = DIM2_MASK(5), - - CDT1_BS_ISOC_SHIFT = 0, - CDT1_BS_ISOC_MASK = DIM2_MASK(9), - - CDT3_BD_SHIFT = 0, - CDT3_BD_MASK = DIM2_MASK(12), - CDT3_BD_ISOC_MASK = DIM2_MASK(13), - CDT3_BA_SHIFT = 16, - - ADT0_CE_BIT = 15, - ADT0_LE_BIT = 14, - ADT0_PG_BIT = 13, - - ADT1_RDY_BIT = 15, - ADT1_DNE_BIT = 14, - ADT1_ERR_BIT = 13, - ADT1_PS_BIT = 12, - ADT1_MEP_BIT = 11, - ADT1_BD_SHIFT = 0, - ADT1_CTRL_ASYNC_BD_MASK = DIM2_MASK(11), - ADT1_ISOC_SYNC_BD_MASK = DIM2_MASK(13), - - CAT_FCE_BIT = 14, - CAT_MFE_BIT = 14, - - CAT_MT_BIT = 13, - - CAT_RNW_BIT = 12, - - CAT_CE_BIT = 11, - - CAT_CT_SHIFT = 8, - CAT_CT_VAL_SYNC = 0, - CAT_CT_VAL_CONTROL = 1, - CAT_CT_VAL_ASYNC = 2, - CAT_CT_VAL_ISOC = 3, - - CAT_CL_SHIFT = 0, - CAT_CL_MASK = DIM2_MASK(6) -}; - -#endif /* DIM2_OS62420_H */ diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.c b/drivers/staging/most/hdm-dim2/dim2_sysfs.c deleted file mode 100644 index 3a2ad355cab1..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.c +++ /dev/null @@ -1,109 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_sysfs.c - MediaLB sysfs information - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -/* Author: Andrey Shvetsov */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include "dim2_sysfs.h" - -struct bus_attr { - struct attribute attr; - ssize_t (*show)(struct medialb_bus *bus, char *buf); - ssize_t (*store)(struct medialb_bus *bus, const char *buf, - size_t count); -}; - -static ssize_t state_show(struct medialb_bus *bus, char *buf) -{ - bool state = dim2_sysfs_get_state_cb(); - - return sprintf(buf, "%s\n", state ? "locked" : ""); -} - -static struct bus_attr state_attr = __ATTR_RO(state); - -static struct attribute *bus_default_attrs[] = { - &state_attr.attr, - NULL, -}; - -static const struct attribute_group bus_attr_group = { - .attrs = bus_default_attrs, -}; - -static void bus_kobj_release(struct kobject *kobj) -{ -} - -static ssize_t bus_kobj_attr_show(struct kobject *kobj, struct attribute *attr, - char *buf) -{ - struct medialb_bus *bus = - container_of(kobj, struct medialb_bus, kobj_group); - struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); - - if (!xattr->show) - return -EIO; - - return xattr->show(bus, buf); -} - -static ssize_t bus_kobj_attr_store(struct kobject *kobj, struct attribute *attr, - const char *buf, size_t count) -{ - struct medialb_bus *bus = - container_of(kobj, struct medialb_bus, kobj_group); - struct bus_attr *xattr = container_of(attr, struct bus_attr, attr); - - if (!xattr->store) - return -EIO; - - return xattr->store(bus, buf, count); -} - -static struct sysfs_ops const bus_kobj_sysfs_ops = { - .show = bus_kobj_attr_show, - .store = bus_kobj_attr_store, -}; - -static struct kobj_type bus_ktype = { - .release = bus_kobj_release, - .sysfs_ops = &bus_kobj_sysfs_ops, -}; - -int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj) -{ - int err; - - kobject_init(&bus->kobj_group, &bus_ktype); - err = kobject_add(&bus->kobj_group, parent_kobj, "bus"); - if (err) { - pr_err("kobject_add() failed: %d\n", err); - goto err_kobject_add; - } - - err = sysfs_create_group(&bus->kobj_group, &bus_attr_group); - if (err) { - pr_err("sysfs_create_group() failed: %d\n", err); - goto err_create_group; - } - - return 0; - -err_create_group: - kobject_put(&bus->kobj_group); - -err_kobject_add: - return err; -} - -void dim2_sysfs_destroy(struct medialb_bus *bus) -{ - kobject_put(&bus->kobj_group); -} diff --git a/drivers/staging/most/hdm-dim2/dim2_sysfs.h b/drivers/staging/most/hdm-dim2/dim2_sysfs.h deleted file mode 100644 index e46dc4ba3946..000000000000 --- a/drivers/staging/most/hdm-dim2/dim2_sysfs.h +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * dim2_sysfs.h - MediaLB sysfs information - * - * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG - */ - -/* Author: Andrey Shvetsov */ - -#ifndef DIM2_SYSFS_H -#define DIM2_SYSFS_H - -#include - -struct medialb_bus { - struct kobject kobj_group; -}; - -struct dim2_hdm; - -int dim2_sysfs_probe(struct medialb_bus *bus, struct kobject *parent_kobj); -void dim2_sysfs_destroy(struct medialb_bus *bus); - -/* - * callback, - * must deliver MediaLB state as true if locked or false if unlocked - */ -bool dim2_sysfs_get_state_cb(void); - -#endif /* DIM2_SYSFS_H */