Merge remote-tracking branch 'staging/staging-next'
[deliverable/linux.git] / drivers / staging / most / hdm-dim2 / dim2_hdm.c
CommitLineData
ba3d7ddf
CG
1/*
2 * dim2_hdm.c - MediaLB DIM2 Hardware Dependent Module
3 *
4 * Copyright (C) 2015, Microchip Technology Germany II GmbH & Co. KG
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * This file is licensed under GPLv2.
12 */
13
14#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
15
16#include <linux/module.h>
17#include <linux/printk.h>
18#include <linux/kernel.h>
19#include <linux/init.h>
20#include <linux/platform_device.h>
21#include <linux/interrupt.h>
22#include <linux/slab.h>
23#include <linux/io.h>
24#include <linux/dma-mapping.h>
25#include <linux/sched.h>
26#include <linux/kthread.h>
27
28#include <mostcore.h>
29#include <networking.h>
30#include "dim2_hal.h"
31#include "dim2_hdm.h"
32#include "dim2_errors.h"
33#include "dim2_sysfs.h"
34
35#define DMA_CHANNELS (32 - 1) /* channel 0 is a system channel */
36
37#define MAX_BUFFERS_PACKET 32
38#define MAX_BUFFERS_STREAMING 32
39#define MAX_BUF_SIZE_PACKET 2048
16dc3743 40#define MAX_BUF_SIZE_STREAMING (8 * 1024)
ba3d7ddf
CG
41
42/* command line parameter to select clock speed */
43static char *clock_speed;
44module_param(clock_speed, charp, 0);
45MODULE_PARM_DESC(clock_speed, "MediaLB Clock Speed");
46
63c87669
CG
47/*
48 * The parameter representing the number of frames per sub-buffer for
49 * synchronous channels. Valid values: [0 .. 6].
50 *
51 * The values 0, 1, 2, 3, 4, 5, 6 represent corresponding number of frames per
52 * sub-buffer 1, 2, 4, 8, 16, 32, 64.
53 */
54static u8 fcnt = 4; /* (1 << fcnt) frames per subbuffer */
55module_param(fcnt, byte, 0);
56MODULE_PARM_DESC(fcnt, "Num of frames per sub-buffer for sync channels as a power of 2");
57
ba3d7ddf
CG
58/*
59 * #############################################################################
60 *
61 * The define below activates an utility function used by HAL-simu
62 * for calling DIM interrupt handler.
63 * It is used only for TEST PURPOSE and shall be commented before release.
64 *
65 * #############################################################################
66 */
67/* #define ENABLE_HDM_TEST */
68
69static DEFINE_SPINLOCK(dim_lock);
70
71static void dim2_tasklet_fn(unsigned long data);
72static DECLARE_TASKLET(dim2_tasklet, dim2_tasklet_fn, 0);
73
74/**
75 * struct hdm_channel - private structure to keep channel specific data
76 * @is_initialized: identifier to know whether the channel is initialized
77 * @ch: HAL specific channel data
78 * @pending_list: list to keep MBO's before starting transfer
79 * @started_list: list to keep MBO's after starting transfer
80 * @direction: channel direction (TX or RX)
81 * @data_type: channel data type
82 */
83struct hdm_channel {
84 char name[sizeof "caNNN"];
85 bool is_initialized;
86 struct dim_channel ch;
c904ffda
CL
87 struct list_head pending_list; /* before dim_enqueue_buffer() */
88 struct list_head started_list; /* after dim_enqueue_buffer() */
ba3d7ddf
CG
89 enum most_channel_direction direction;
90 enum most_channel_data_type data_type;
91};
92
93/**
94 * struct dim2_hdm - private structure to keep interface specific data
95 * @hch: an array of channel specific data
96 * @most_iface: most interface structure
97 * @capabilities: an array of channel capability data
98 * @io_base: I/O register base address
99 * @irq_ahb0: dim2 AHB0 irq number
100 * @clk_speed: user selectable (through command line parameter) clock speed
101 * @netinfo_task: thread to deliver network status
102 * @netinfo_waitq: waitq for the thread to sleep
103 * @deliver_netinfo: to identify whether network status received
104 * @mac_addrs: INIC mac address
105 * @link_state: network link state
106 * @atx_idx: index of async tx channel
107 */
108struct dim2_hdm {
109 struct hdm_channel hch[DMA_CHANNELS];
110 struct most_channel_capability capabilities[DMA_CHANNELS];
111 struct most_interface most_iface;
112 char name[16 + sizeof "dim2-"];
092c78f2 113 void __iomem *io_base;
ba3d7ddf
CG
114 unsigned int irq_ahb0;
115 int clk_speed;
116 struct task_struct *netinfo_task;
117 wait_queue_head_t netinfo_waitq;
118 int deliver_netinfo;
119 unsigned char mac_addrs[6];
120 unsigned char link_state;
121 int atx_idx;
122 struct medialb_bus bus;
123};
124
125#define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface)
126
127/* Macro to identify a network status message */
128#define PACKET_IS_NET_INFO(p) \
129 (((p)[1] == 0x18) && ((p)[2] == 0x05) && ((p)[3] == 0x0C) && \
130 ((p)[13] == 0x3C) && ((p)[14] == 0x00) && ((p)[15] == 0x0A))
131
132#if defined(ENABLE_HDM_TEST)
133static struct dim2_hdm *test_dev;
134#endif
135
136bool dim2_sysfs_get_state_cb(void)
137{
138 bool state;
139 unsigned long flags;
140
141 spin_lock_irqsave(&dim_lock, flags);
b724207b 142 state = dim_get_lock_state();
ba3d7ddf
CG
143 spin_unlock_irqrestore(&dim_lock, flags);
144
145 return state;
146}
147
148/**
58889788 149 * dimcb_io_read - callback from HAL to read an I/O register
ba3d7ddf
CG
150 * @ptr32: register address
151 */
092c78f2 152u32 dimcb_io_read(u32 __iomem *ptr32)
ba3d7ddf 153{
560dca25 154 return readl(ptr32);
ba3d7ddf
CG
155}
156
157/**
1efc4564 158 * dimcb_io_write - callback from HAL to write value to an I/O register
ba3d7ddf
CG
159 * @ptr32: register address
160 * @value: value to write
161 */
092c78f2 162void dimcb_io_write(u32 __iomem *ptr32, u32 value)
ba3d7ddf 163{
560dca25 164 writel(value, ptr32);
ba3d7ddf
CG
165}
166
167/**
de668731 168 * dimcb_on_error - callback from HAL to report miscommunication between
ba3d7ddf
CG
169 * HDM and HAL
170 * @error_id: Error ID
171 * @error_message: Error message. Some text in a free format
172 */
de668731 173void dimcb_on_error(u8 error_id, const char *error_message)
ba3d7ddf 174{
de668731 175 pr_err("dimcb_on_error: error_id - %d, error_message - %s\n", error_id,
ba3d7ddf
CG
176 error_message);
177}
178
ba3d7ddf
CG
179/**
180 * startup_dim - initialize the dim2 interface
181 * @pdev: platform device
182 *
183 * Get the value of command line parameter "clock_speed" if given or use the
184 * default value, enable the clock and PLL, and initialize the dim2 interface.
185 */
186static int startup_dim(struct platform_device *pdev)
187{
188 struct dim2_hdm *dev = platform_get_drvdata(pdev);
189 struct dim2_platform_data *pdata = pdev->dev.platform_data;
190 u8 hal_ret;
191
192 dev->clk_speed = -1;
193
194 if (clock_speed) {
195 if (!strcmp(clock_speed, "256fs"))
196 dev->clk_speed = CLK_256FS;
197 else if (!strcmp(clock_speed, "512fs"))
198 dev->clk_speed = CLK_512FS;
199 else if (!strcmp(clock_speed, "1024fs"))
200 dev->clk_speed = CLK_1024FS;
201 else if (!strcmp(clock_speed, "2048fs"))
202 dev->clk_speed = CLK_2048FS;
203 else if (!strcmp(clock_speed, "3072fs"))
204 dev->clk_speed = CLK_3072FS;
205 else if (!strcmp(clock_speed, "4096fs"))
206 dev->clk_speed = CLK_4096FS;
207 else if (!strcmp(clock_speed, "6144fs"))
208 dev->clk_speed = CLK_6144FS;
209 else if (!strcmp(clock_speed, "8192fs"))
210 dev->clk_speed = CLK_8192FS;
211 }
212
213 if (dev->clk_speed == -1) {
9158d33a 214 pr_info("Bad or missing clock speed parameter, using default value: 3072fs\n");
ba3d7ddf 215 dev->clk_speed = CLK_3072FS;
9deba73d 216 } else {
ba3d7ddf 217 pr_info("Selected clock speed: %s\n", clock_speed);
9deba73d 218 }
ba3d7ddf
CG
219 if (pdata && pdata->init) {
220 int ret = pdata->init(pdata, dev->io_base, dev->clk_speed);
221
222 if (ret)
223 return ret;
224 }
225
63c87669
CG
226 pr_info("sync: num of frames per sub-buffer: %u\n", fcnt);
227 hal_ret = dim_startup(dev->io_base, dev->clk_speed, fcnt);
ba3d7ddf 228 if (hal_ret != DIM_NO_ERROR) {
6417267f 229 pr_err("dim_startup failed: %d\n", hal_ret);
ba3d7ddf
CG
230 if (pdata && pdata->destroy)
231 pdata->destroy(pdata);
232 return -ENODEV;
233 }
234
235 return 0;
236}
237
238/**
239 * try_start_dim_transfer - try to transfer a buffer on a channel
240 * @hdm_ch: channel specific data
241 *
242 * Transfer a buffer from pending_list if the channel is ready
243 */
244static int try_start_dim_transfer(struct hdm_channel *hdm_ch)
245{
246 u16 buf_size;
247 struct list_head *head = &hdm_ch->pending_list;
248 struct mbo *mbo;
249 unsigned long flags;
250 struct dim_ch_state_t st;
251
96d3064b 252 BUG_ON(!hdm_ch);
ba3d7ddf
CG
253 BUG_ON(!hdm_ch->is_initialized);
254
255 spin_lock_irqsave(&dim_lock, flags);
256 if (list_empty(head)) {
257 spin_unlock_irqrestore(&dim_lock, flags);
258 return -EAGAIN;
259 }
260
60d5f66c 261 if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) {
ba3d7ddf
CG
262 spin_unlock_irqrestore(&dim_lock, flags);
263 return -EAGAIN;
264 }
265
d93f27b7 266 mbo = list_first_entry(head, struct mbo, list);
ba3d7ddf
CG
267 buf_size = mbo->buffer_length;
268
269 BUG_ON(mbo->bus_address == 0);
c904ffda 270 if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) {
ba3d7ddf
CG
271 list_del(head->next);
272 spin_unlock_irqrestore(&dim_lock, flags);
273 mbo->processed_length = 0;
274 mbo->status = MBO_E_INVAL;
275 mbo->complete(mbo);
276 return -EFAULT;
277 }
278
279 list_move_tail(head->next, &hdm_ch->started_list);
280 spin_unlock_irqrestore(&dim_lock, flags);
281
282 return 0;
283}
284
285/**
286 * deliver_netinfo_thread - thread to deliver network status to mostcore
287 * @data: private data
288 *
289 * Wait for network status and deliver it to mostcore once it is received
290 */
291static int deliver_netinfo_thread(void *data)
292{
98b5afd8 293 struct dim2_hdm *dev = data;
ba3d7ddf
CG
294
295 while (!kthread_should_stop()) {
296 wait_event_interruptible(dev->netinfo_waitq,
297 dev->deliver_netinfo ||
298 kthread_should_stop());
299
300 if (dev->deliver_netinfo) {
301 dev->deliver_netinfo--;
302 most_deliver_netinfo(&dev->most_iface, dev->link_state,
303 dev->mac_addrs);
304 }
305 }
306
307 return 0;
308}
309
310/**
311 * retrieve_netinfo - retrieve network status from received buffer
312 * @dev: private data
313 * @mbo: received MBO
314 *
315 * Parse the message in buffer and get node address, link state, MAC address.
316 * Wake up a thread to deliver this status to mostcore
317 */
318static void retrieve_netinfo(struct dim2_hdm *dev, struct mbo *mbo)
319{
320 u8 *data = mbo->virt_address;
321 u8 *mac = dev->mac_addrs;
322
323 pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]);
324 dev->link_state = data[18];
325 pr_info("NIState: %d\n", dev->link_state);
326 memcpy(mac, data + 19, 6);
327 pr_info("MAC address: %02X:%02X:%02X:%02X:%02X:%02X\n",
328 mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
329 dev->deliver_netinfo++;
330 wake_up_interruptible(&dev->netinfo_waitq);
331}
332
333/**
334 * service_done_flag - handle completed buffers
335 * @dev: private data
336 * @ch_idx: channel index
337 *
338 * Return back the completed buffers to mostcore, using completion callback
339 */
340static void service_done_flag(struct dim2_hdm *dev, int ch_idx)
341{
342 struct hdm_channel *hdm_ch = dev->hch + ch_idx;
343 struct dim_ch_state_t st;
344 struct list_head *head;
345 struct mbo *mbo;
346 int done_buffers;
347 unsigned long flags;
348 u8 *data;
349
96d3064b 350 BUG_ON(!hdm_ch);
ba3d7ddf
CG
351 BUG_ON(!hdm_ch->is_initialized);
352
353 spin_lock_irqsave(&dim_lock, flags);
354
60d5f66c 355 done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers;
ba3d7ddf
CG
356 if (!done_buffers) {
357 spin_unlock_irqrestore(&dim_lock, flags);
358 return;
359 }
360
38c38544 361 if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) {
ba3d7ddf
CG
362 spin_unlock_irqrestore(&dim_lock, flags);
363 return;
364 }
365 spin_unlock_irqrestore(&dim_lock, flags);
366
367 head = &hdm_ch->started_list;
368
369 while (done_buffers) {
370 spin_lock_irqsave(&dim_lock, flags);
371 if (list_empty(head)) {
372 spin_unlock_irqrestore(&dim_lock, flags);
9158d33a 373 pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n");
ba3d7ddf
CG
374 break;
375 }
376
d93f27b7 377 mbo = list_first_entry(head, struct mbo, list);
ba3d7ddf
CG
378 list_del(head->next);
379 spin_unlock_irqrestore(&dim_lock, flags);
380
381 data = mbo->virt_address;
382
383 if (hdm_ch->data_type == MOST_CH_ASYNC &&
384 hdm_ch->direction == MOST_CH_RX &&
385 PACKET_IS_NET_INFO(data)) {
ba3d7ddf
CG
386 retrieve_netinfo(dev, mbo);
387
388 spin_lock_irqsave(&dim_lock, flags);
389 list_add_tail(&mbo->list, &hdm_ch->pending_list);
390 spin_unlock_irqrestore(&dim_lock, flags);
391 } else {
392 if (hdm_ch->data_type == MOST_CH_CONTROL ||
393 hdm_ch->data_type == MOST_CH_ASYNC) {
ba3d7ddf
CG
394 u32 const data_size =
395 (u32)data[0] * 256 + data[1] + 2;
396
397 mbo->processed_length =
7f9cacb6
CG
398 min_t(u32, data_size,
399 mbo->buffer_length);
ba3d7ddf
CG
400 } else {
401 mbo->processed_length = mbo->buffer_length;
402 }
403 mbo->status = MBO_SUCCESS;
404 mbo->complete(mbo);
405 }
406
407 done_buffers--;
408 }
409}
410
411static struct dim_channel **get_active_channels(struct dim2_hdm *dev,
edaa1e33 412 struct dim_channel **buffer)
ba3d7ddf
CG
413{
414 int idx = 0;
415 int ch_idx;
416
417 for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) {
418 if (dev->hch[ch_idx].is_initialized)
419 buffer[idx++] = &dev->hch[ch_idx].ch;
420 }
96d3064b 421 buffer[idx++] = NULL;
ba3d7ddf
CG
422
423 return buffer;
424}
425
426/**
427 * dim2_tasklet_fn - tasklet function
428 * @data: private data
429 *
430 * Service each initialized channel, if needed
431 */
432static void dim2_tasklet_fn(unsigned long data)
433{
434 struct dim2_hdm *dev = (struct dim2_hdm *)data;
435 unsigned long flags;
436 int ch_idx;
437
438 for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) {
439 if (!dev->hch[ch_idx].is_initialized)
440 continue;
441
442 spin_lock_irqsave(&dim_lock, flags);
0d08d54f 443 dim_service_channel(&dev->hch[ch_idx].ch);
ba3d7ddf
CG
444 spin_unlock_irqrestore(&dim_lock, flags);
445
446 service_done_flag(dev, ch_idx);
447 while (!try_start_dim_transfer(dev->hch + ch_idx))
448 continue;
449 }
450}
451
452/**
453 * dim2_ahb_isr - interrupt service routine
454 * @irq: irq number
455 * @_dev: private data
456 *
457 * Acknowledge the interrupt and schedule a tasklet to service channels.
458 * Return IRQ_HANDLED.
459 */
460static irqreturn_t dim2_ahb_isr(int irq, void *_dev)
461{
98b5afd8 462 struct dim2_hdm *dev = _dev;
ba3d7ddf
CG
463 struct dim_channel *buffer[DMA_CHANNELS + 1];
464 unsigned long flags;
465
466 spin_lock_irqsave(&dim_lock, flags);
e5baa9e9 467 dim_service_irq(get_active_channels(dev, buffer));
ba3d7ddf
CG
468 spin_unlock_irqrestore(&dim_lock, flags);
469
470#if !defined(ENABLE_HDM_TEST)
471 dim2_tasklet.data = (unsigned long)dev;
472 tasklet_schedule(&dim2_tasklet);
473#else
474 dim2_tasklet_fn((unsigned long)dev);
475#endif
476 return IRQ_HANDLED;
477}
478
479#if defined(ENABLE_HDM_TEST)
480
481/*
482 * Utility function used by HAL-simu for calling DIM interrupt handler.
483 * It is used only for TEST PURPOSE.
484 */
485void raise_dim_interrupt(void)
486{
487 (void)dim2_ahb_isr(0, test_dev);
488}
489#endif
490
491/**
492 * complete_all_mbos - complete MBO's in a list
493 * @head: list head
494 *
495 * Delete all the entries in list and return back MBO's to mostcore using
496 * completion call back.
497 */
498static void complete_all_mbos(struct list_head *head)
499{
500 unsigned long flags;
501 struct mbo *mbo;
502
503 for (;;) {
504 spin_lock_irqsave(&dim_lock, flags);
505 if (list_empty(head)) {
506 spin_unlock_irqrestore(&dim_lock, flags);
507 break;
508 }
509
d93f27b7 510 mbo = list_first_entry(head, struct mbo, list);
ba3d7ddf
CG
511 list_del(head->next);
512 spin_unlock_irqrestore(&dim_lock, flags);
513
514 mbo->processed_length = 0;
515 mbo->status = MBO_E_CLOSE;
516 mbo->complete(mbo);
517 }
518}
519
520/**
521 * configure_channel - initialize a channel
522 * @iface: interface the channel belongs to
523 * @channel: channel to be configured
524 * @channel_config: structure that holds the configuration information
525 *
526 * Receives configuration information from mostcore and initialize
527 * the corresponding channel. Return 0 on success, negative on failure.
528 */
529static int configure_channel(struct most_interface *most_iface, int ch_idx,
530 struct most_channel_config *ccfg)
531{
532 struct dim2_hdm *dev = iface_to_hdm(most_iface);
533 bool const is_tx = ccfg->direction == MOST_CH_TX;
534 u16 const sub_size = ccfg->subbuffer_size;
535 u16 const buf_size = ccfg->buffer_size;
536 u16 new_size;
537 unsigned long flags;
538 u8 hal_ret;
539 int const ch_addr = ch_idx * 2 + 2;
540 struct hdm_channel *const hdm_ch = dev->hch + ch_idx;
541
542 BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS);
543
544 if (hdm_ch->is_initialized)
545 return -EPERM;
546
547 switch (ccfg->data_type) {
548 case MOST_CH_CONTROL:
c64c6073 549 new_size = dim_norm_ctrl_async_buffer_size(buf_size);
ba3d7ddf
CG
550 if (new_size == 0) {
551 pr_err("%s: too small buffer size\n", hdm_ch->name);
552 return -EINVAL;
553 }
554 ccfg->buffer_size = new_size;
555 if (new_size != buf_size)
556 pr_warn("%s: fixed buffer size (%d -> %d)\n",
557 hdm_ch->name, buf_size, new_size);
558 spin_lock_irqsave(&dim_lock, flags);
a3f3e921
CL
559 hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr,
560 buf_size);
ba3d7ddf
CG
561 break;
562 case MOST_CH_ASYNC:
c64c6073 563 new_size = dim_norm_ctrl_async_buffer_size(buf_size);
ba3d7ddf
CG
564 if (new_size == 0) {
565 pr_err("%s: too small buffer size\n", hdm_ch->name);
566 return -EINVAL;
567 }
568 ccfg->buffer_size = new_size;
569 if (new_size != buf_size)
570 pr_warn("%s: fixed buffer size (%d -> %d)\n",
571 hdm_ch->name, buf_size, new_size);
572 spin_lock_irqsave(&dim_lock, flags);
26303150 573 hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr, buf_size);
ba3d7ddf
CG
574 break;
575 case MOST_CH_ISOC_AVP:
e302ca47 576 new_size = dim_norm_isoc_buffer_size(buf_size, sub_size);
ba3d7ddf 577 if (new_size == 0) {
9158d33a
CG
578 pr_err("%s: invalid sub-buffer size or too small buffer size\n",
579 hdm_ch->name);
ba3d7ddf
CG
580 return -EINVAL;
581 }
582 ccfg->buffer_size = new_size;
583 if (new_size != buf_size)
584 pr_warn("%s: fixed buffer size (%d -> %d)\n",
585 hdm_ch->name, buf_size, new_size);
586 spin_lock_irqsave(&dim_lock, flags);
f1383176 587 hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size);
ba3d7ddf
CG
588 break;
589 case MOST_CH_SYNC:
aff19245 590 new_size = dim_norm_sync_buffer_size(buf_size, sub_size);
ba3d7ddf 591 if (new_size == 0) {
9158d33a
CG
592 pr_err("%s: invalid sub-buffer size or too small buffer size\n",
593 hdm_ch->name);
ba3d7ddf
CG
594 return -EINVAL;
595 }
596 ccfg->buffer_size = new_size;
597 if (new_size != buf_size)
598 pr_warn("%s: fixed buffer size (%d -> %d)\n",
599 hdm_ch->name, buf_size, new_size);
600 spin_lock_irqsave(&dim_lock, flags);
10e5efb7 601 hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size);
ba3d7ddf
CG
602 break;
603 default:
604 pr_err("%s: configure failed, bad channel type: %d\n",
605 hdm_ch->name, ccfg->data_type);
606 return -EINVAL;
607 }
608
609 if (hal_ret != DIM_NO_ERROR) {
610 spin_unlock_irqrestore(&dim_lock, flags);
611 pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n",
612 hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx);
613 return -ENODEV;
614 }
615
616 hdm_ch->data_type = ccfg->data_type;
617 hdm_ch->direction = ccfg->direction;
618 hdm_ch->is_initialized = true;
619
620 if (hdm_ch->data_type == MOST_CH_ASYNC &&
621 hdm_ch->direction == MOST_CH_TX &&
622 dev->atx_idx < 0)
623 dev->atx_idx = ch_idx;
624
625 spin_unlock_irqrestore(&dim_lock, flags);
626
627 return 0;
628}
629
630/**
631 * enqueue - enqueue a buffer for data transfer
632 * @iface: intended interface
633 * @channel: ID of the channel the buffer is intended for
634 * @mbo: pointer to the buffer object
635 *
636 * Push the buffer into pending_list and try to transfer one buffer from
637 * pending_list. Return 0 on success, negative on failure.
638 */
639static int enqueue(struct most_interface *most_iface, int ch_idx,
640 struct mbo *mbo)
641{
642 struct dim2_hdm *dev = iface_to_hdm(most_iface);
643 struct hdm_channel *hdm_ch = dev->hch + ch_idx;
644 unsigned long flags;
645
646 BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS);
647
648 if (!hdm_ch->is_initialized)
649 return -EPERM;
650
651 if (mbo->bus_address == 0)
652 return -EFAULT;
653
654 spin_lock_irqsave(&dim_lock, flags);
655 list_add_tail(&mbo->list, &hdm_ch->pending_list);
656 spin_unlock_irqrestore(&dim_lock, flags);
657
658 (void)try_start_dim_transfer(hdm_ch);
659
660 return 0;
661}
662
663/**
664 * request_netinfo - triggers retrieving of network info
665 * @iface: pointer to the interface
666 * @channel_id: corresponding channel ID
667 *
668 * Send a command to INIC which triggers retrieving of network info by means of
669 * "Message exchange over MDP/MEP". Return 0 on success, negative on failure.
670 */
671static void request_netinfo(struct most_interface *most_iface, int ch_idx)
672{
673 struct dim2_hdm *dev = iface_to_hdm(most_iface);
674 struct mbo *mbo;
675 u8 *data;
676
677 if (dev->atx_idx < 0) {
678 pr_err("Async Tx Not initialized\n");
679 return;
680 }
681
71457d48 682 mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL);
ba3d7ddf
CG
683 if (!mbo)
684 return;
685
686 mbo->buffer_length = 5;
687
688 data = mbo->virt_address;
689
690 data[0] = 0x00; /* PML High byte */
691 data[1] = 0x03; /* PML Low byte */
692 data[2] = 0x02; /* PMHL */
693 data[3] = 0x08; /* FPH */
694 data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */
695
696 most_submit_mbo(mbo);
697}
698
699/**
700 * poison_channel - poison buffers of a channel
701 * @iface: pointer to the interface the channel to be poisoned belongs to
702 * @channel_id: corresponding channel ID
703 *
704 * Destroy a channel and complete all the buffers in both started_list &
705 * pending_list. Return 0 on success, negative on failure.
706 */
707static int poison_channel(struct most_interface *most_iface, int ch_idx)
708{
709 struct dim2_hdm *dev = iface_to_hdm(most_iface);
710 struct hdm_channel *hdm_ch = dev->hch + ch_idx;
711 unsigned long flags;
712 u8 hal_ret;
713 int ret = 0;
714
715 BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS);
716
717 if (!hdm_ch->is_initialized)
718 return -EPERM;
719
6ebb3727 720 tasklet_disable(&dim2_tasklet);
ba3d7ddf 721 spin_lock_irqsave(&dim_lock, flags);
a5e4d891 722 hal_ret = dim_destroy_channel(&hdm_ch->ch);
ba3d7ddf
CG
723 hdm_ch->is_initialized = false;
724 if (ch_idx == dev->atx_idx)
725 dev->atx_idx = -1;
726 spin_unlock_irqrestore(&dim_lock, flags);
6ebb3727 727 tasklet_enable(&dim2_tasklet);
ba3d7ddf
CG
728 if (hal_ret != DIM_NO_ERROR) {
729 pr_err("HAL Failed to close channel %s\n", hdm_ch->name);
730 ret = -EFAULT;
731 }
732
733 complete_all_mbos(&hdm_ch->started_list);
734 complete_all_mbos(&hdm_ch->pending_list);
735
736 return ret;
737}
738
739/*
740 * dim2_probe - dim2 probe handler
741 * @pdev: platform device structure
742 *
743 * Register the dim2 interface with mostcore and initialize it.
744 * Return 0 on success, negative on failure.
745 */
746static int dim2_probe(struct platform_device *pdev)
747{
748 struct dim2_hdm *dev;
749 struct resource *res;
750 int ret, i;
751 struct kobject *kobj;
752
8661fca6 753 dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
ba3d7ddf
CG
754 if (!dev)
755 return -ENOMEM;
756
757 dev->atx_idx = -1;
758
759 platform_set_drvdata(pdev, dev);
760#if defined(ENABLE_HDM_TEST)
761 test_dev = dev;
762#else
763 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
bab469cd
AKC
764 dev->io_base = devm_ioremap_resource(&pdev->dev, res);
765 if (IS_ERR(dev->io_base))
766 return PTR_ERR(dev->io_base);
ba3d7ddf
CG
767
768 ret = platform_get_irq(pdev, 0);
769 if (ret < 0) {
a642bbbb 770 dev_err(&pdev->dev, "failed to get irq\n");
bab469cd 771 return -ENODEV;
ba3d7ddf
CG
772 }
773 dev->irq_ahb0 = ret;
774
3eced21a
AKC
775 ret = devm_request_irq(&pdev->dev, dev->irq_ahb0, dim2_ahb_isr, 0,
776 "mlb_ahb0", dev);
ba3d7ddf 777 if (ret) {
a642bbbb
AKC
778 dev_err(&pdev->dev, "failed to request IRQ: %d, err: %d\n",
779 dev->irq_ahb0, ret);
bab469cd 780 return ret;
ba3d7ddf
CG
781 }
782#endif
783 init_waitqueue_head(&dev->netinfo_waitq);
784 dev->deliver_netinfo = 0;
785 dev->netinfo_task = kthread_run(&deliver_netinfo_thread, (void *)dev,
786 "dim2_netinfo");
3eced21a 787 if (IS_ERR(dev->netinfo_task))
98217767 788 return PTR_ERR(dev->netinfo_task);
ba3d7ddf
CG
789
790 for (i = 0; i < DMA_CHANNELS; i++) {
791 struct most_channel_capability *cap = dev->capabilities + i;
792 struct hdm_channel *hdm_ch = dev->hch + i;
793
794 INIT_LIST_HEAD(&hdm_ch->pending_list);
795 INIT_LIST_HEAD(&hdm_ch->started_list);
796 hdm_ch->is_initialized = false;
797 snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2);
798
799 cap->name_suffix = hdm_ch->name;
800 cap->direction = MOST_CH_RX | MOST_CH_TX;
801 cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
802 MOST_CH_ISOC_AVP | MOST_CH_SYNC;
803 cap->num_buffers_packet = MAX_BUFFERS_PACKET;
804 cap->buffer_size_packet = MAX_BUF_SIZE_PACKET;
805 cap->num_buffers_streaming = MAX_BUFFERS_STREAMING;
806 cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING;
807 }
808
809 {
810 const char *fmt;
811
812 if (sizeof(res->start) == sizeof(long long))
813 fmt = "dim2-%016llx";
814 else if (sizeof(res->start) == sizeof(long))
815 fmt = "dim2-%016lx";
816 else
817 fmt = "dim2-%016x";
818
819 snprintf(dev->name, sizeof(dev->name), fmt, res->start);
820 }
821
822 dev->most_iface.interface = ITYPE_MEDIALB_DIM2;
823 dev->most_iface.description = dev->name;
824 dev->most_iface.num_channels = DMA_CHANNELS;
825 dev->most_iface.channel_vector = dev->capabilities;
826 dev->most_iface.configure = configure_channel;
827 dev->most_iface.enqueue = enqueue;
828 dev->most_iface.poison_channel = poison_channel;
829 dev->most_iface.request_netinfo = request_netinfo;
830
831 kobj = most_register_interface(&dev->most_iface);
832 if (IS_ERR(kobj)) {
833 ret = PTR_ERR(kobj);
9d521ca7 834 dev_err(&pdev->dev, "failed to register MOST interface\n");
ba3d7ddf
CG
835 goto err_stop_thread;
836 }
837
838 ret = dim2_sysfs_probe(&dev->bus, kobj);
839 if (ret)
840 goto err_unreg_iface;
841
842 ret = startup_dim(pdev);
843 if (ret) {
9d521ca7 844 dev_err(&pdev->dev, "failed to initialize DIM2\n");
ba3d7ddf
CG
845 goto err_destroy_bus;
846 }
847
848 return 0;
849
850err_destroy_bus:
851 dim2_sysfs_destroy(&dev->bus);
852err_unreg_iface:
853 most_deregister_interface(&dev->most_iface);
854err_stop_thread:
855 kthread_stop(dev->netinfo_task);
ba3d7ddf
CG
856
857 return ret;
858}
859
860/**
861 * dim2_remove - dim2 remove handler
862 * @pdev: platform device structure
863 *
864 * Unregister the interface from mostcore
865 */
866static int dim2_remove(struct platform_device *pdev)
867{
868 struct dim2_hdm *dev = platform_get_drvdata(pdev);
ba3d7ddf
CG
869 struct dim2_platform_data *pdata = pdev->dev.platform_data;
870 unsigned long flags;
871
872 spin_lock_irqsave(&dim_lock, flags);
50a45b17 873 dim_shutdown();
ba3d7ddf
CG
874 spin_unlock_irqrestore(&dim_lock, flags);
875
876 if (pdata && pdata->destroy)
877 pdata->destroy(pdata);
878
879 dim2_sysfs_destroy(&dev->bus);
880 most_deregister_interface(&dev->most_iface);
881 kthread_stop(dev->netinfo_task);
ba3d7ddf
CG
882
883 /*
884 * break link to local platform_device_id struct
885 * to prevent crash by unload platform device module
886 */
96d3064b 887 pdev->id_entry = NULL;
ba3d7ddf
CG
888
889 return 0;
890}
891
892static struct platform_device_id dim2_id[] = {
893 { "medialb_dim2" },
894 { }, /* Terminating entry */
895};
896
897MODULE_DEVICE_TABLE(platform, dim2_id);
898
899static struct platform_driver dim2_driver = {
900 .probe = dim2_probe,
901 .remove = dim2_remove,
902 .id_table = dim2_id,
903 .driver = {
904 .name = "hdm_dim2",
ba3d7ddf
CG
905 },
906};
907
8668984f 908module_platform_driver(dim2_driver);
ba3d7ddf
CG
909
910MODULE_AUTHOR("Jain Roy Ambi <JainRoy.Ambi@microchip.com>");
911MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>");
912MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module");
913MODULE_LICENSE("GPL");
This page took 0.21927 seconds and 5 git commands to generate.