linux/drivers/staging/most/dim2/dim2.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * dim2.c - MediaLB DIM2 Hardware Dependent Module
   4 *
   5 * Copyright (C) 2015-2016, Microchip Technology Germany II GmbH & Co. KG
   6 */
   7
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9
  10#include <linux/module.h>
  11#include <linux/of_platform.h>
  12#include <linux/printk.h>
  13#include <linux/kernel.h>
  14#include <linux/init.h>
  15#include <linux/platform_device.h>
  16#include <linux/interrupt.h>
  17#include <linux/slab.h>
  18#include <linux/io.h>
  19#include <linux/clk.h>
  20#include <linux/dma-mapping.h>
  21#include <linux/sched.h>
  22#include <linux/kthread.h>
  23#include <linux/most.h>
  24#include "hal.h"
  25#include "errors.h"
  26#include "sysfs.h"
  27
  28#define DMA_CHANNELS (32 - 1)  /* channel 0 is a system channel */
  29
  30#define MAX_BUFFERS_PACKET      32
  31#define MAX_BUFFERS_STREAMING   32
  32#define MAX_BUF_SIZE_PACKET     2048
  33#define MAX_BUF_SIZE_STREAMING  (8 * 1024)
  34
  35/*
  36 * The parameter representing the number of frames per sub-buffer for
  37 * synchronous channels.  Valid values: [0 .. 6].
  38 *
  39 * The values 0, 1, 2, 3, 4, 5, 6 represent corresponding number of frames per
  40 * sub-buffer 1, 2, 4, 8, 16, 32, 64.
  41 */
  42static u8 fcnt = 4;  /* (1 << fcnt) frames per subbuffer */
  43module_param(fcnt, byte, 0000);
  44MODULE_PARM_DESC(fcnt, "Num of frames per sub-buffer for sync channels as a power of 2");
  45
  46static DEFINE_SPINLOCK(dim_lock);
  47
  48static void dim2_tasklet_fn(unsigned long data);
  49static DECLARE_TASKLET_OLD(dim2_tasklet, dim2_tasklet_fn);
  50
  51/**
  52 * struct hdm_channel - private structure to keep channel specific data
  53 * @name: channel name
  54 * @is_initialized: identifier to know whether the channel is initialized
  55 * @ch: HAL specific channel data
  56 * @reset_dbr_size: reset DBR data buffer size
  57 * @pending_list: list to keep MBO's before starting transfer
  58 * @started_list: list to keep MBO's after starting transfer
  59 * @direction: channel direction (TX or RX)
  60 * @data_type: channel data type
  61 */
  62struct hdm_channel {
  63        char name[sizeof "caNNN"];
  64        bool is_initialized;
  65        struct dim_channel ch;
  66        u16 *reset_dbr_size;
  67        struct list_head pending_list;  /* before dim_enqueue_buffer() */
  68        struct list_head started_list;  /* after dim_enqueue_buffer() */
  69        enum most_channel_direction direction;
  70        enum most_channel_data_type data_type;
  71};
  72
  73/*
  74 * struct dim2_hdm - private structure to keep interface specific data
  75 * @hch: an array of channel specific data
  76 * @most_iface: most interface structure
  77 * @capabilities: an array of channel capability data
  78 * @io_base: I/O register base address
  79 * @netinfo_task: thread to deliver network status
  80 * @netinfo_waitq: waitq for the thread to sleep
  81 * @deliver_netinfo: to identify whether network status received
  82 * @mac_addrs: INIC mac address
  83 * @link_state: network link state
  84 * @atx_idx: index of async tx channel
  85 */
  86struct dim2_hdm {
  87        struct device dev;
  88        struct hdm_channel hch[DMA_CHANNELS];
  89        struct most_channel_capability capabilities[DMA_CHANNELS];
  90        struct most_interface most_iface;
  91        char name[16 + sizeof "dim2-"];
  92        void __iomem *io_base;
  93        u8 clk_speed;
  94        struct clk *clk;
  95        struct clk *clk_pll;
  96        struct task_struct *netinfo_task;
  97        wait_queue_head_t netinfo_waitq;
  98        int deliver_netinfo;
  99        unsigned char mac_addrs[6];
 100        unsigned char link_state;
 101        int atx_idx;
 102        struct medialb_bus bus;
 103        void (*on_netinfo)(struct most_interface *most_iface,
 104                           unsigned char link_state, unsigned char *addrs);
 105        void (*disable_platform)(struct platform_device *pdev);
 106};
 107
 108struct dim2_platform_data {
 109        int (*enable)(struct platform_device *pdev);
 110        void (*disable)(struct platform_device *pdev);
 111};
 112
 113#define iface_to_hdm(iface) container_of(iface, struct dim2_hdm, most_iface)
 114
 115/* Macro to identify a network status message */
 116#define PACKET_IS_NET_INFO(p)  \
 117        (((p)[1] == 0x18) && ((p)[2] == 0x05) && ((p)[3] == 0x0C) && \
 118         ((p)[13] == 0x3C) && ((p)[14] == 0x00) && ((p)[15] == 0x0A))
 119
 120bool dim2_sysfs_get_state_cb(void)
 121{
 122        bool state;
 123        unsigned long flags;
 124
 125        spin_lock_irqsave(&dim_lock, flags);
 126        state = dim_get_lock_state();
 127        spin_unlock_irqrestore(&dim_lock, flags);
 128
 129        return state;
 130}
 131
 132/**
 133 * dimcb_on_error - callback from HAL to report miscommunication between
 134 * HDM and HAL
 135 * @error_id: Error ID
 136 * @error_message: Error message. Some text in a free format
 137 */
 138void dimcb_on_error(u8 error_id, const char *error_message)
 139{
 140        pr_err("%s: error_id - %d, error_message - %s\n", __func__, error_id,
 141               error_message);
 142}
 143
 144/**
 145 * try_start_dim_transfer - try to transfer a buffer on a channel
 146 * @hdm_ch: channel specific data
 147 *
 148 * Transfer a buffer from pending_list if the channel is ready
 149 */
 150static int try_start_dim_transfer(struct hdm_channel *hdm_ch)
 151{
 152        u16 buf_size;
 153        struct list_head *head = &hdm_ch->pending_list;
 154        struct mbo *mbo;
 155        unsigned long flags;
 156        struct dim_ch_state_t st;
 157
 158        BUG_ON(!hdm_ch);
 159        BUG_ON(!hdm_ch->is_initialized);
 160
 161        spin_lock_irqsave(&dim_lock, flags);
 162        if (list_empty(head)) {
 163                spin_unlock_irqrestore(&dim_lock, flags);
 164                return -EAGAIN;
 165        }
 166
 167        if (!dim_get_channel_state(&hdm_ch->ch, &st)->ready) {
 168                spin_unlock_irqrestore(&dim_lock, flags);
 169                return -EAGAIN;
 170        }
 171
 172        mbo = list_first_entry(head, struct mbo, list);
 173        buf_size = mbo->buffer_length;
 174
 175        if (dim_dbr_space(&hdm_ch->ch) < buf_size) {
 176                spin_unlock_irqrestore(&dim_lock, flags);
 177                return -EAGAIN;
 178        }
 179
 180        BUG_ON(mbo->bus_address == 0);
 181        if (!dim_enqueue_buffer(&hdm_ch->ch, mbo->bus_address, buf_size)) {
 182                list_del(head->next);
 183                spin_unlock_irqrestore(&dim_lock, flags);
 184                mbo->processed_length = 0;
 185                mbo->status = MBO_E_INVAL;
 186                mbo->complete(mbo);
 187                return -EFAULT;
 188        }
 189
 190        list_move_tail(head->next, &hdm_ch->started_list);
 191        spin_unlock_irqrestore(&dim_lock, flags);
 192
 193        return 0;
 194}
 195
 196/**
 197 * deliver_netinfo_thread - thread to deliver network status to mostcore
 198 * @data: private data
 199 *
 200 * Wait for network status and deliver it to mostcore once it is received
 201 */
 202static int deliver_netinfo_thread(void *data)
 203{
 204        struct dim2_hdm *dev = data;
 205
 206        while (!kthread_should_stop()) {
 207                wait_event_interruptible(dev->netinfo_waitq,
 208                                         dev->deliver_netinfo ||
 209                                         kthread_should_stop());
 210
 211                if (dev->deliver_netinfo) {
 212                        dev->deliver_netinfo--;
 213                        if (dev->on_netinfo) {
 214                                dev->on_netinfo(&dev->most_iface,
 215                                                dev->link_state,
 216                                                dev->mac_addrs);
 217                        }
 218                }
 219        }
 220
 221        return 0;
 222}
 223
 224/**
 225 * retrieve_netinfo - retrieve network status from received buffer
 226 * @dev: private data
 227 * @mbo: received MBO
 228 *
 229 * Parse the message in buffer and get node address, link state, MAC address.
 230 * Wake up a thread to deliver this status to mostcore
 231 */
 232static void retrieve_netinfo(struct dim2_hdm *dev, struct mbo *mbo)
 233{
 234        u8 *data = mbo->virt_address;
 235
 236        pr_info("Node Address: 0x%03x\n", (u16)data[16] << 8 | data[17]);
 237        dev->link_state = data[18];
 238        pr_info("NIState: %d\n", dev->link_state);
 239        memcpy(dev->mac_addrs, data + 19, 6);
 240        dev->deliver_netinfo++;
 241        wake_up_interruptible(&dev->netinfo_waitq);
 242}
 243
 244/**
 245 * service_done_flag - handle completed buffers
 246 * @dev: private data
 247 * @ch_idx: channel index
 248 *
 249 * Return back the completed buffers to mostcore, using completion callback
 250 */
 251static void service_done_flag(struct dim2_hdm *dev, int ch_idx)
 252{
 253        struct hdm_channel *hdm_ch = dev->hch + ch_idx;
 254        struct dim_ch_state_t st;
 255        struct list_head *head;
 256        struct mbo *mbo;
 257        int done_buffers;
 258        unsigned long flags;
 259        u8 *data;
 260
 261        BUG_ON(!hdm_ch);
 262        BUG_ON(!hdm_ch->is_initialized);
 263
 264        spin_lock_irqsave(&dim_lock, flags);
 265
 266        done_buffers = dim_get_channel_state(&hdm_ch->ch, &st)->done_buffers;
 267        if (!done_buffers) {
 268                spin_unlock_irqrestore(&dim_lock, flags);
 269                return;
 270        }
 271
 272        if (!dim_detach_buffers(&hdm_ch->ch, done_buffers)) {
 273                spin_unlock_irqrestore(&dim_lock, flags);
 274                return;
 275        }
 276        spin_unlock_irqrestore(&dim_lock, flags);
 277
 278        head = &hdm_ch->started_list;
 279
 280        while (done_buffers) {
 281                spin_lock_irqsave(&dim_lock, flags);
 282                if (list_empty(head)) {
 283                        spin_unlock_irqrestore(&dim_lock, flags);
 284                        pr_crit("hard error: started_mbo list is empty whereas DIM2 has sent buffers\n");
 285                        break;
 286                }
 287
 288                mbo = list_first_entry(head, struct mbo, list);
 289                list_del(head->next);
 290                spin_unlock_irqrestore(&dim_lock, flags);
 291
 292                data = mbo->virt_address;
 293
 294                if (hdm_ch->data_type == MOST_CH_ASYNC &&
 295                    hdm_ch->direction == MOST_CH_RX &&
 296                    PACKET_IS_NET_INFO(data)) {
 297                        retrieve_netinfo(dev, mbo);
 298
 299                        spin_lock_irqsave(&dim_lock, flags);
 300                        list_add_tail(&mbo->list, &hdm_ch->pending_list);
 301                        spin_unlock_irqrestore(&dim_lock, flags);
 302                } else {
 303                        if (hdm_ch->data_type == MOST_CH_CONTROL ||
 304                            hdm_ch->data_type == MOST_CH_ASYNC) {
 305                                u32 const data_size =
 306                                        (u32)data[0] * 256 + data[1] + 2;
 307
 308                                mbo->processed_length =
 309                                        min_t(u32, data_size,
 310                                              mbo->buffer_length);
 311                        } else {
 312                                mbo->processed_length = mbo->buffer_length;
 313                        }
 314                        mbo->status = MBO_SUCCESS;
 315                        mbo->complete(mbo);
 316                }
 317
 318                done_buffers--;
 319        }
 320}
 321
 322static struct dim_channel **get_active_channels(struct dim2_hdm *dev,
 323                                                struct dim_channel **buffer)
 324{
 325        int idx = 0;
 326        int ch_idx;
 327
 328        for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) {
 329                if (dev->hch[ch_idx].is_initialized)
 330                        buffer[idx++] = &dev->hch[ch_idx].ch;
 331        }
 332        buffer[idx++] = NULL;
 333
 334        return buffer;
 335}
 336
 337static irqreturn_t dim2_mlb_isr(int irq, void *_dev)
 338{
 339        struct dim2_hdm *dev = _dev;
 340        unsigned long flags;
 341
 342        spin_lock_irqsave(&dim_lock, flags);
 343        dim_service_mlb_int_irq();
 344        spin_unlock_irqrestore(&dim_lock, flags);
 345
 346        if (dev->atx_idx >= 0 && dev->hch[dev->atx_idx].is_initialized)
 347                while (!try_start_dim_transfer(dev->hch + dev->atx_idx))
 348                        continue;
 349
 350        return IRQ_HANDLED;
 351}
 352
 353/**
 354 * dim2_tasklet_fn - tasklet function
 355 * @data: private data
 356 *
 357 * Service each initialized channel, if needed
 358 */
 359static void dim2_tasklet_fn(unsigned long data)
 360{
 361        struct dim2_hdm *dev = (struct dim2_hdm *)data;
 362        unsigned long flags;
 363        int ch_idx;
 364
 365        for (ch_idx = 0; ch_idx < DMA_CHANNELS; ch_idx++) {
 366                if (!dev->hch[ch_idx].is_initialized)
 367                        continue;
 368
 369                spin_lock_irqsave(&dim_lock, flags);
 370                dim_service_channel(&dev->hch[ch_idx].ch);
 371                spin_unlock_irqrestore(&dim_lock, flags);
 372
 373                service_done_flag(dev, ch_idx);
 374                while (!try_start_dim_transfer(dev->hch + ch_idx))
 375                        continue;
 376        }
 377}
 378
 379/**
 380 * dim2_ahb_isr - interrupt service routine
 381 * @irq: irq number
 382 * @_dev: private data
 383 *
 384 * Acknowledge the interrupt and schedule a tasklet to service channels.
 385 * Return IRQ_HANDLED.
 386 */
 387static irqreturn_t dim2_ahb_isr(int irq, void *_dev)
 388{
 389        struct dim2_hdm *dev = _dev;
 390        struct dim_channel *buffer[DMA_CHANNELS + 1];
 391        unsigned long flags;
 392
 393        spin_lock_irqsave(&dim_lock, flags);
 394        dim_service_ahb_int_irq(get_active_channels(dev, buffer));
 395        spin_unlock_irqrestore(&dim_lock, flags);
 396
 397        dim2_tasklet.data = (unsigned long)dev;
 398        tasklet_schedule(&dim2_tasklet);
 399        return IRQ_HANDLED;
 400}
 401
 402/**
 403 * complete_all_mbos - complete MBO's in a list
 404 * @head: list head
 405 *
 406 * Delete all the entries in list and return back MBO's to mostcore using
 407 * completion call back.
 408 */
 409static void complete_all_mbos(struct list_head *head)
 410{
 411        unsigned long flags;
 412        struct mbo *mbo;
 413
 414        for (;;) {
 415                spin_lock_irqsave(&dim_lock, flags);
 416                if (list_empty(head)) {
 417                        spin_unlock_irqrestore(&dim_lock, flags);
 418                        break;
 419                }
 420
 421                mbo = list_first_entry(head, struct mbo, list);
 422                list_del(head->next);
 423                spin_unlock_irqrestore(&dim_lock, flags);
 424
 425                mbo->processed_length = 0;
 426                mbo->status = MBO_E_CLOSE;
 427                mbo->complete(mbo);
 428        }
 429}
 430
 431/**
 432 * configure_channel - initialize a channel
 433 * @most_iface: interface the channel belongs to
 434 * @ch_idx: channel index to be configured
 435 * @ccfg: structure that holds the configuration information
 436 *
 437 * Receives configuration information from mostcore and initialize
 438 * the corresponding channel. Return 0 on success, negative on failure.
 439 */
 440static int configure_channel(struct most_interface *most_iface, int ch_idx,
 441                             struct most_channel_config *ccfg)
 442{
 443        struct dim2_hdm *dev = iface_to_hdm(most_iface);
 444        bool const is_tx = ccfg->direction == MOST_CH_TX;
 445        u16 const sub_size = ccfg->subbuffer_size;
 446        u16 const buf_size = ccfg->buffer_size;
 447        u16 new_size;
 448        unsigned long flags;
 449        u8 hal_ret;
 450        int const ch_addr = ch_idx * 2 + 2;
 451        struct hdm_channel *const hdm_ch = dev->hch + ch_idx;
 452
 453        BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS);
 454
 455        if (hdm_ch->is_initialized)
 456                return -EPERM;
 457
 458        /* do not reset if the property was set by user, see poison_channel */
 459        hdm_ch->reset_dbr_size = ccfg->dbr_size ? NULL : &ccfg->dbr_size;
 460
 461        /* zero value is default dbr_size, see dim2 hal */
 462        hdm_ch->ch.dbr_size = ccfg->dbr_size;
 463
 464        switch (ccfg->data_type) {
 465        case MOST_CH_CONTROL:
 466                new_size = dim_norm_ctrl_async_buffer_size(buf_size);
 467                if (new_size == 0) {
 468                        pr_err("%s: too small buffer size\n", hdm_ch->name);
 469                        return -EINVAL;
 470                }
 471                ccfg->buffer_size = new_size;
 472                if (new_size != buf_size)
 473                        pr_warn("%s: fixed buffer size (%d -> %d)\n",
 474                                hdm_ch->name, buf_size, new_size);
 475                spin_lock_irqsave(&dim_lock, flags);
 476                hal_ret = dim_init_control(&hdm_ch->ch, is_tx, ch_addr,
 477                                           is_tx ? new_size * 2 : new_size);
 478                break;
 479        case MOST_CH_ASYNC:
 480                new_size = dim_norm_ctrl_async_buffer_size(buf_size);
 481                if (new_size == 0) {
 482                        pr_err("%s: too small buffer size\n", hdm_ch->name);
 483                        return -EINVAL;
 484                }
 485                ccfg->buffer_size = new_size;
 486                if (new_size != buf_size)
 487                        pr_warn("%s: fixed buffer size (%d -> %d)\n",
 488                                hdm_ch->name, buf_size, new_size);
 489                spin_lock_irqsave(&dim_lock, flags);
 490                hal_ret = dim_init_async(&hdm_ch->ch, is_tx, ch_addr,
 491                                         is_tx ? new_size * 2 : new_size);
 492                break;
 493        case MOST_CH_ISOC:
 494                new_size = dim_norm_isoc_buffer_size(buf_size, sub_size);
 495                if (new_size == 0) {
 496                        pr_err("%s: invalid sub-buffer size or too small buffer size\n",
 497                               hdm_ch->name);
 498                        return -EINVAL;
 499                }
 500                ccfg->buffer_size = new_size;
 501                if (new_size != buf_size)
 502                        pr_warn("%s: fixed buffer size (%d -> %d)\n",
 503                                hdm_ch->name, buf_size, new_size);
 504                spin_lock_irqsave(&dim_lock, flags);
 505                hal_ret = dim_init_isoc(&hdm_ch->ch, is_tx, ch_addr, sub_size);
 506                break;
 507        case MOST_CH_SYNC:
 508                new_size = dim_norm_sync_buffer_size(buf_size, sub_size);
 509                if (new_size == 0) {
 510                        pr_err("%s: invalid sub-buffer size or too small buffer size\n",
 511                               hdm_ch->name);
 512                        return -EINVAL;
 513                }
 514                ccfg->buffer_size = new_size;
 515                if (new_size != buf_size)
 516                        pr_warn("%s: fixed buffer size (%d -> %d)\n",
 517                                hdm_ch->name, buf_size, new_size);
 518                spin_lock_irqsave(&dim_lock, flags);
 519                hal_ret = dim_init_sync(&hdm_ch->ch, is_tx, ch_addr, sub_size);
 520                break;
 521        default:
 522                pr_err("%s: configure failed, bad channel type: %d\n",
 523                       hdm_ch->name, ccfg->data_type);
 524                return -EINVAL;
 525        }
 526
 527        if (hal_ret != DIM_NO_ERROR) {
 528                spin_unlock_irqrestore(&dim_lock, flags);
 529                pr_err("%s: configure failed (%d), type: %d, is_tx: %d\n",
 530                       hdm_ch->name, hal_ret, ccfg->data_type, (int)is_tx);
 531                return -ENODEV;
 532        }
 533
 534        hdm_ch->data_type = ccfg->data_type;
 535        hdm_ch->direction = ccfg->direction;
 536        hdm_ch->is_initialized = true;
 537
 538        if (hdm_ch->data_type == MOST_CH_ASYNC &&
 539            hdm_ch->direction == MOST_CH_TX &&
 540            dev->atx_idx < 0)
 541                dev->atx_idx = ch_idx;
 542
 543        spin_unlock_irqrestore(&dim_lock, flags);
 544        ccfg->dbr_size = hdm_ch->ch.dbr_size;
 545
 546        return 0;
 547}
 548
 549/**
 550 * enqueue - enqueue a buffer for data transfer
 551 * @most_iface: intended interface
 552 * @ch_idx: ID of the channel the buffer is intended for
 553 * @mbo: pointer to the buffer object
 554 *
 555 * Push the buffer into pending_list and try to transfer one buffer from
 556 * pending_list. Return 0 on success, negative on failure.
 557 */
 558static int enqueue(struct most_interface *most_iface, int ch_idx,
 559                   struct mbo *mbo)
 560{
 561        struct dim2_hdm *dev = iface_to_hdm(most_iface);
 562        struct hdm_channel *hdm_ch = dev->hch + ch_idx;
 563        unsigned long flags;
 564
 565        BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS);
 566
 567        if (!hdm_ch->is_initialized)
 568                return -EPERM;
 569
 570        if (mbo->bus_address == 0)
 571                return -EFAULT;
 572
 573        spin_lock_irqsave(&dim_lock, flags);
 574        list_add_tail(&mbo->list, &hdm_ch->pending_list);
 575        spin_unlock_irqrestore(&dim_lock, flags);
 576
 577        (void)try_start_dim_transfer(hdm_ch);
 578
 579        return 0;
 580}
 581
 582/**
 583 * request_netinfo - triggers retrieving of network info
 584 * @most_iface: pointer to the interface
 585 * @ch_idx: corresponding channel ID
 586 * @on_netinfo: call-back used to deliver network status to mostcore
 587 *
 588 * Send a command to INIC which triggers retrieving of network info by means of
 589 * "Message exchange over MDP/MEP". Return 0 on success, negative on failure.
 590 */
 591static void request_netinfo(struct most_interface *most_iface, int ch_idx,
 592                            void (*on_netinfo)(struct most_interface *,
 593                                               unsigned char, unsigned char *))
 594{
 595        struct dim2_hdm *dev = iface_to_hdm(most_iface);
 596        struct mbo *mbo;
 597        u8 *data;
 598
 599        dev->on_netinfo = on_netinfo;
 600        if (!on_netinfo)
 601                return;
 602
 603        if (dev->atx_idx < 0) {
 604                pr_err("Async Tx Not initialized\n");
 605                return;
 606        }
 607
 608        mbo = most_get_mbo(&dev->most_iface, dev->atx_idx, NULL);
 609        if (!mbo)
 610                return;
 611
 612        mbo->buffer_length = 5;
 613
 614        data = mbo->virt_address;
 615
 616        data[0] = 0x00; /* PML High byte */
 617        data[1] = 0x03; /* PML Low byte */
 618        data[2] = 0x02; /* PMHL */
 619        data[3] = 0x08; /* FPH */
 620        data[4] = 0x40; /* FMF (FIFO cmd msg - Triggers NAOverMDP) */
 621
 622        most_submit_mbo(mbo);
 623}
 624
 625/**
 626 * poison_channel - poison buffers of a channel
 627 * @most_iface: pointer to the interface the channel to be poisoned belongs to
 628 * @ch_idx: corresponding channel ID
 629 *
 630 * Destroy a channel and complete all the buffers in both started_list &
 631 * pending_list. Return 0 on success, negative on failure.
 632 */
 633static int poison_channel(struct most_interface *most_iface, int ch_idx)
 634{
 635        struct dim2_hdm *dev = iface_to_hdm(most_iface);
 636        struct hdm_channel *hdm_ch = dev->hch + ch_idx;
 637        unsigned long flags;
 638        u8 hal_ret;
 639        int ret = 0;
 640
 641        BUG_ON(ch_idx < 0 || ch_idx >= DMA_CHANNELS);
 642
 643        if (!hdm_ch->is_initialized)
 644                return -EPERM;
 645
 646        tasklet_disable(&dim2_tasklet);
 647        spin_lock_irqsave(&dim_lock, flags);
 648        hal_ret = dim_destroy_channel(&hdm_ch->ch);
 649        hdm_ch->is_initialized = false;
 650        if (ch_idx == dev->atx_idx)
 651                dev->atx_idx = -1;
 652        spin_unlock_irqrestore(&dim_lock, flags);
 653        tasklet_enable(&dim2_tasklet);
 654        if (hal_ret != DIM_NO_ERROR) {
 655                pr_err("HAL Failed to close channel %s\n", hdm_ch->name);
 656                ret = -EFAULT;
 657        }
 658
 659        complete_all_mbos(&hdm_ch->started_list);
 660        complete_all_mbos(&hdm_ch->pending_list);
 661        if (hdm_ch->reset_dbr_size)
 662                *hdm_ch->reset_dbr_size = 0;
 663
 664        return ret;
 665}
 666
 667static void *dma_alloc(struct mbo *mbo, u32 size)
 668{
 669        struct device *dev = mbo->ifp->driver_dev;
 670
 671        return dma_alloc_coherent(dev, size, &mbo->bus_address, GFP_KERNEL);
 672}
 673
 674static void dma_free(struct mbo *mbo, u32 size)
 675{
 676        struct device *dev = mbo->ifp->driver_dev;
 677
 678        dma_free_coherent(dev, size, mbo->virt_address, mbo->bus_address);
 679}
 680
 681static const struct of_device_id dim2_of_match[];
 682
 683static struct {
 684        const char *clock_speed;
 685        u8 clk_speed;
 686} clk_mt[] = {
 687        { "256fs", CLK_256FS },
 688        { "512fs", CLK_512FS },
 689        { "1024fs", CLK_1024FS },
 690        { "2048fs", CLK_2048FS },
 691        { "3072fs", CLK_3072FS },
 692        { "4096fs", CLK_4096FS },
 693        { "6144fs", CLK_6144FS },
 694        { "8192fs", CLK_8192FS },
 695};
 696
 697/**
 698 * get_dim2_clk_speed - converts string to DIM2 clock speed value
 699 *
 700 * @clock_speed: string in the format "{NUMBER}fs"
 701 * @val: pointer to get one of the CLK_{NUMBER}FS values
 702 *
 703 * By success stores one of the CLK_{NUMBER}FS in the *val and returns 0,
 704 * otherwise returns -EINVAL.
 705 */
 706static int get_dim2_clk_speed(const char *clock_speed, u8 *val)
 707{
 708        int i;
 709
 710        for (i = 0; i < ARRAY_SIZE(clk_mt); i++) {
 711                if (!strcmp(clock_speed, clk_mt[i].clock_speed)) {
 712                        *val = clk_mt[i].clk_speed;
 713                        return 0;
 714                }
 715        }
 716        return -EINVAL;
 717}
 718
 719/*
 720 * dim2_probe - dim2 probe handler
 721 * @pdev: platform device structure
 722 *
 723 * Register the dim2 interface with mostcore and initialize it.
 724 * Return 0 on success, negative on failure.
 725 */
 726static int dim2_probe(struct platform_device *pdev)
 727{
 728        const struct dim2_platform_data *pdata;
 729        const struct of_device_id *of_id;
 730        const char *clock_speed;
 731        struct dim2_hdm *dev;
 732        struct resource *res;
 733        int ret, i;
 734        u8 hal_ret;
 735        int irq;
 736
 737        enum { MLB_INT_IDX, AHB0_INT_IDX };
 738
 739        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
 740        if (!dev)
 741                return -ENOMEM;
 742
 743        dev->atx_idx = -1;
 744
 745        platform_set_drvdata(pdev, dev);
 746
 747        ret = of_property_read_string(pdev->dev.of_node,
 748                                      "microchip,clock-speed", &clock_speed);
 749        if (ret) {
 750                dev_err(&pdev->dev, "missing dt property clock-speed\n");
 751                return ret;
 752        }
 753
 754        ret = get_dim2_clk_speed(clock_speed, &dev->clk_speed);
 755        if (ret) {
 756                dev_err(&pdev->dev, "bad dt property clock-speed\n");
 757                return ret;
 758        }
 759
 760        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 761        dev->io_base = devm_ioremap_resource(&pdev->dev, res);
 762        if (IS_ERR(dev->io_base))
 763                return PTR_ERR(dev->io_base);
 764
 765        of_id = of_match_node(dim2_of_match, pdev->dev.of_node);
 766        pdata = of_id->data;
 767        ret = pdata && pdata->enable ? pdata->enable(pdev) : 0;
 768        if (ret)
 769                return ret;
 770
 771        dev->disable_platform = pdata ? pdata->disable : NULL;
 772
 773        dev_info(&pdev->dev, "sync: num of frames per sub-buffer: %u\n", fcnt);
 774        hal_ret = dim_startup(dev->io_base, dev->clk_speed, fcnt);
 775        if (hal_ret != DIM_NO_ERROR) {
 776                dev_err(&pdev->dev, "dim_startup failed: %d\n", hal_ret);
 777                ret = -ENODEV;
 778                goto err_disable_platform;
 779        }
 780
 781        irq = platform_get_irq(pdev, AHB0_INT_IDX);
 782        if (irq < 0) {
 783                ret = irq;
 784                goto err_shutdown_dim;
 785        }
 786
 787        ret = devm_request_irq(&pdev->dev, irq, dim2_ahb_isr, 0,
 788                               "dim2_ahb0_int", dev);
 789        if (ret) {
 790                dev_err(&pdev->dev, "failed to request ahb0_int irq %d\n", irq);
 791                goto err_shutdown_dim;
 792        }
 793
 794        irq = platform_get_irq(pdev, MLB_INT_IDX);
 795        if (irq < 0) {
 796                ret = irq;
 797                goto err_shutdown_dim;
 798        }
 799
 800        ret = devm_request_irq(&pdev->dev, irq, dim2_mlb_isr, 0,
 801                               "dim2_mlb_int", dev);
 802        if (ret) {
 803                dev_err(&pdev->dev, "failed to request mlb_int irq %d\n", irq);
 804                goto err_shutdown_dim;
 805        }
 806
 807        init_waitqueue_head(&dev->netinfo_waitq);
 808        dev->deliver_netinfo = 0;
 809        dev->netinfo_task = kthread_run(&deliver_netinfo_thread, dev,
 810                                        "dim2_netinfo");
 811        if (IS_ERR(dev->netinfo_task)) {
 812                ret = PTR_ERR(dev->netinfo_task);
 813                goto err_shutdown_dim;
 814        }
 815
 816        for (i = 0; i < DMA_CHANNELS; i++) {
 817                struct most_channel_capability *cap = dev->capabilities + i;
 818                struct hdm_channel *hdm_ch = dev->hch + i;
 819
 820                INIT_LIST_HEAD(&hdm_ch->pending_list);
 821                INIT_LIST_HEAD(&hdm_ch->started_list);
 822                hdm_ch->is_initialized = false;
 823                snprintf(hdm_ch->name, sizeof(hdm_ch->name), "ca%d", i * 2 + 2);
 824
 825                cap->name_suffix = hdm_ch->name;
 826                cap->direction = MOST_CH_RX | MOST_CH_TX;
 827                cap->data_type = MOST_CH_CONTROL | MOST_CH_ASYNC |
 828                                 MOST_CH_ISOC | MOST_CH_SYNC;
 829                cap->num_buffers_packet = MAX_BUFFERS_PACKET;
 830                cap->buffer_size_packet = MAX_BUF_SIZE_PACKET;
 831                cap->num_buffers_streaming = MAX_BUFFERS_STREAMING;
 832                cap->buffer_size_streaming = MAX_BUF_SIZE_STREAMING;
 833        }
 834
 835        {
 836                const char *fmt;
 837
 838                if (sizeof(res->start) == sizeof(long long))
 839                        fmt = "dim2-%016llx";
 840                else if (sizeof(res->start) == sizeof(long))
 841                        fmt = "dim2-%016lx";
 842                else
 843                        fmt = "dim2-%016x";
 844
 845                snprintf(dev->name, sizeof(dev->name), fmt, res->start);
 846        }
 847
 848        dev->most_iface.interface = ITYPE_MEDIALB_DIM2;
 849        dev->most_iface.description = dev->name;
 850        dev->most_iface.num_channels = DMA_CHANNELS;
 851        dev->most_iface.channel_vector = dev->capabilities;
 852        dev->most_iface.configure = configure_channel;
 853        dev->most_iface.enqueue = enqueue;
 854        dev->most_iface.dma_alloc = dma_alloc;
 855        dev->most_iface.dma_free = dma_free;
 856        dev->most_iface.poison_channel = poison_channel;
 857        dev->most_iface.request_netinfo = request_netinfo;
 858        dev->most_iface.driver_dev = &pdev->dev;
 859        dev->most_iface.dev = &dev->dev;
 860        dev->dev.init_name = "dim2_state";
 861        dev->dev.parent = &pdev->dev;
 862
 863        ret = most_register_interface(&dev->most_iface);
 864        if (ret) {
 865                dev_err(&pdev->dev, "failed to register MOST interface\n");
 866                goto err_stop_thread;
 867        }
 868
 869        ret = dim2_sysfs_probe(&dev->dev);
 870        if (ret) {
 871                dev_err(&pdev->dev, "failed to create sysfs attribute\n");
 872                goto err_unreg_iface;
 873        }
 874
 875        return 0;
 876
 877err_unreg_iface:
 878        most_deregister_interface(&dev->most_iface);
 879err_stop_thread:
 880        kthread_stop(dev->netinfo_task);
 881err_shutdown_dim:
 882        dim_shutdown();
 883err_disable_platform:
 884        if (dev->disable_platform)
 885                dev->disable_platform(pdev);
 886
 887        return ret;
 888}
 889
 890/**
 891 * dim2_remove - dim2 remove handler
 892 * @pdev: platform device structure
 893 *
 894 * Unregister the interface from mostcore
 895 */
 896static int dim2_remove(struct platform_device *pdev)
 897{
 898        struct dim2_hdm *dev = platform_get_drvdata(pdev);
 899        unsigned long flags;
 900
 901        dim2_sysfs_destroy(&dev->dev);
 902        most_deregister_interface(&dev->most_iface);
 903        kthread_stop(dev->netinfo_task);
 904
 905        spin_lock_irqsave(&dim_lock, flags);
 906        dim_shutdown();
 907        spin_unlock_irqrestore(&dim_lock, flags);
 908
 909        if (dev->disable_platform)
 910                dev->disable_platform(pdev);
 911
 912        return 0;
 913}
 914
 915/* platform specific functions [[ */
 916
 917static int fsl_mx6_enable(struct platform_device *pdev)
 918{
 919        struct dim2_hdm *dev = platform_get_drvdata(pdev);
 920        int ret;
 921
 922        dev->clk = devm_clk_get(&pdev->dev, "mlb");
 923        if (IS_ERR_OR_NULL(dev->clk)) {
 924                dev_err(&pdev->dev, "unable to get mlb clock\n");
 925                return -EFAULT;
 926        }
 927
 928        ret = clk_prepare_enable(dev->clk);
 929        if (ret) {
 930                dev_err(&pdev->dev, "%s\n", "clk_prepare_enable failed");
 931                return ret;
 932        }
 933
 934        if (dev->clk_speed >= CLK_2048FS) {
 935                /* enable pll */
 936                dev->clk_pll = devm_clk_get(&pdev->dev, "pll8_mlb");
 937                if (IS_ERR_OR_NULL(dev->clk_pll)) {
 938                        dev_err(&pdev->dev, "unable to get mlb pll clock\n");
 939                        clk_disable_unprepare(dev->clk);
 940                        return -EFAULT;
 941                }
 942
 943                writel(0x888, dev->io_base + 0x38);
 944                clk_prepare_enable(dev->clk_pll);
 945        }
 946
 947        return 0;
 948}
 949
 950static void fsl_mx6_disable(struct platform_device *pdev)
 951{
 952        struct dim2_hdm *dev = platform_get_drvdata(pdev);
 953
 954        if (dev->clk_speed >= CLK_2048FS)
 955                clk_disable_unprepare(dev->clk_pll);
 956
 957        clk_disable_unprepare(dev->clk);
 958}
 959
 960static int rcar_h2_enable(struct platform_device *pdev)
 961{
 962        struct dim2_hdm *dev = platform_get_drvdata(pdev);
 963        int ret;
 964
 965        dev->clk = devm_clk_get(&pdev->dev, NULL);
 966        if (IS_ERR(dev->clk)) {
 967                dev_err(&pdev->dev, "cannot get clock\n");
 968                return PTR_ERR(dev->clk);
 969        }
 970
 971        ret = clk_prepare_enable(dev->clk);
 972        if (ret) {
 973                dev_err(&pdev->dev, "%s\n", "clk_prepare_enable failed");
 974                return ret;
 975        }
 976
 977        if (dev->clk_speed >= CLK_2048FS) {
 978                /* enable MLP pll and LVDS drivers */
 979                writel(0x03, dev->io_base + 0x600);
 980                /* set bias */
 981                writel(0x888, dev->io_base + 0x38);
 982        } else {
 983                /* PLL */
 984                writel(0x04, dev->io_base + 0x600);
 985        }
 986
 987
 988        /* BBCR = 0b11 */
 989        writel(0x03, dev->io_base + 0x500);
 990        writel(0x0002FF02, dev->io_base + 0x508);
 991
 992        return 0;
 993}
 994
 995static void rcar_h2_disable(struct platform_device *pdev)
 996{
 997        struct dim2_hdm *dev = platform_get_drvdata(pdev);
 998
 999        clk_disable_unprepare(dev->clk);
1000
1001        /* disable PLLs and LVDS drivers */
1002        writel(0x0, dev->io_base + 0x600);
1003}
1004
1005static int rcar_m3_enable(struct platform_device *pdev)
1006{
1007        struct dim2_hdm *dev = platform_get_drvdata(pdev);
1008        u32 enable_512fs = dev->clk_speed == CLK_512FS;
1009        int ret;
1010
1011        dev->clk = devm_clk_get(&pdev->dev, NULL);
1012        if (IS_ERR(dev->clk)) {
1013                dev_err(&pdev->dev, "cannot get clock\n");
1014                return PTR_ERR(dev->clk);
1015        }
1016
1017        ret = clk_prepare_enable(dev->clk);
1018        if (ret) {
1019                dev_err(&pdev->dev, "%s\n", "clk_prepare_enable failed");
1020                return ret;
1021        }
1022
1023        /* PLL */
1024        writel(0x04, dev->io_base + 0x600);
1025
1026        writel(enable_512fs, dev->io_base + 0x604);
1027
1028        /* BBCR = 0b11 */
1029        writel(0x03, dev->io_base + 0x500);
1030        writel(0x0002FF02, dev->io_base + 0x508);
1031
1032        return 0;
1033}
1034
1035static void rcar_m3_disable(struct platform_device *pdev)
1036{
1037        struct dim2_hdm *dev = platform_get_drvdata(pdev);
1038
1039        clk_disable_unprepare(dev->clk);
1040
1041        /* disable PLLs and LVDS drivers */
1042        writel(0x0, dev->io_base + 0x600);
1043}
1044
1045/* ]] platform specific functions */
1046
1047enum dim2_platforms { FSL_MX6, RCAR_H2, RCAR_M3 };
1048
1049static struct dim2_platform_data plat_data[] = {
1050        [FSL_MX6] = { .enable = fsl_mx6_enable, .disable = fsl_mx6_disable },
1051        [RCAR_H2] = { .enable = rcar_h2_enable, .disable = rcar_h2_disable },
1052        [RCAR_M3] = { .enable = rcar_m3_enable, .disable = rcar_m3_disable },
1053};
1054
1055static const struct of_device_id dim2_of_match[] = {
1056        {
1057                .compatible = "fsl,imx6q-mlb150",
1058                .data = plat_data + FSL_MX6
1059        },
1060        {
1061                .compatible = "renesas,mlp",
1062                .data = plat_data + RCAR_H2
1063        },
1064        {
1065                .compatible = "rcar,medialb-dim2",
1066                .data = plat_data + RCAR_M3
1067        },
1068        {
1069                .compatible = "xlnx,axi4-os62420_3pin-1.00.a",
1070        },
1071        {
1072                .compatible = "xlnx,axi4-os62420_6pin-1.00.a",
1073        },
1074        {},
1075};
1076
1077MODULE_DEVICE_TABLE(of, dim2_of_match);
1078
1079static struct platform_driver dim2_driver = {
1080        .probe = dim2_probe,
1081        .remove = dim2_remove,
1082        .driver = {
1083                .name = "hdm_dim2",
1084                .of_match_table = dim2_of_match,
1085        },
1086};
1087
1088module_platform_driver(dim2_driver);
1089
1090MODULE_AUTHOR("Andrey Shvetsov <andrey.shvetsov@k2l.de>");
1091MODULE_DESCRIPTION("MediaLB DIM2 Hardware Dependent Module");
1092MODULE_LICENSE("GPL");
1093