/* PCIMgr_Veneers.c
 *
 * useful C veneers for accessing MicroDigital PCIManager SWI calls
 *
 * This library code is (c) 2003 MicroDigital Ltd,
 * but is made available on an open-source basis to assist programmers
 * in developing Risc OS drivers for PCI expansion cards.
 * If you use any of it in your own code, please mention MicroDigital in the credits.
 * You may redistribute this library providing it is complete and unmodified.
 * If you make any improvements, please email them to "dprosser@microdigital.co.uk"
 * to include in future releases, so other programmers can benefit.
 */

#include "kernel.h"
#include "swis.h"

#include "PCIMgr_Ven.h"
#include <stdio.h>

long PCIMgr_ReturnNumberOfDevices (void)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  err = _kernel_swi(PCI_ReturnNumberOfDevices, &regs, &regs);

  if (err)
    return (long) (err);
  else
    return (regs.r[0]);
}


void PCIMgr_ReadID (long LogicalDevID, PCIMgr_ReadIDInfo *Info)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[3] = (int) LogicalDevID;
  err = _kernel_swi(PCI_ReadID, &regs, &regs);

  if (err)
    Info->VenDevID = 0x0ffffffff;
  else {
    Info->VenDevID  = regs.r[0];
    Info->SlotNum   = regs.r[1];
    Info->ClassCode = regs.r[2];
    Info->SubVenDevID = regs.r[3];
    Info->CMOSBase  = regs.r[4];
  }
}

long PCIMgr_RegisterDriver (unsigned long VenDevID, unsigned long InterestMask, unsigned long PrivateId, char *name)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[1] = (int) VenDevID;
  regs.r[2] = (int) InterestMask;
  regs.r[4] = (int) PrivateId;
  regs.r[5] = (int) name;
  err = _kernel_swi(PCI_RegisterDriver, &regs, &regs);

  if (err)
    return (-1);
  else
    return (regs.r[0]);
}


long PCIMgr_DeRegisterDriver (long LogicalDevID, unsigned long PrivateId)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[3] = (int) LogicalDevID;
  regs.r[4] = (int) PrivateId;
  err = _kernel_swi(PCI_DeRegisterDriver, &regs, &regs);

  if (err)
    return (-1);
  else
    return (regs.r[0]);
}

unsigned long PCIMgr_ConfigurationRead (long LogicalDevID, unsigned long Offset)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[1] = (int) Offset;
  regs.r[3] = (int) LogicalDevID;
  err = _kernel_swi(PCI_ConfigurationRead, &regs, &regs);

  if (err)
    return (unsigned long) (err);
  else
    return (regs.r[2]);
}

unsigned long PCIMgr_ConfigurationWrite (long LogicalDevID, unsigned long Offset, unsigned long Data)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[1] = (int) Offset;
  regs.r[2] = (int) Data;
  regs.r[3] = (int) LogicalDevID;
  err = _kernel_swi(PCI_ConfigurationWrite, &regs, &regs);

  return (unsigned long) (err);
}

unsigned long PCIMgr_AddressMapping (long LogicalDevID, PCIMgr_AdrsMap *Buffer, long BufLength)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[1] = (int) Buffer;
  regs.r[2] = (int) BufLength;
  regs.r[3] = (int) LogicalDevID;
  err = _kernel_swi(PCI_AddressMapping, &regs, &regs);

  if (err)
    return (0);
  else
    return (regs.r[0]);
}

unsigned long PCIMgr_ConfigurationReadBlock (long LogicalDevID, unsigned char *Buffer)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[2] = (int) Buffer;
  regs.r[3] = (int) LogicalDevID;
  err = _kernel_swi(PCI_ConfigurationReadBlock, &regs, &regs);

  return (unsigned long) (err);
}

unsigned long PCIMgr_InterruptMapping (long LogicalDevID, PCIMgr_IntrMap *Buffer)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = 0;
  regs.r[1] = (int) Buffer;
  regs.r[3] = (int) LogicalDevID;
  err = _kernel_swi(PCI_InterruptMapping, &regs, &regs);

  if (err)
    return (0);
  else
    return (regs.r[0]);
}

void PCIMgr_FlushCaches (void *Area, long Length)
{
  _kernel_swi_regs regs;

  regs.r[0] = (int) Area;
  regs.r[1] = (int) Length;
  _kernel_swi(PCI_FlushCaches, &regs, &regs);
}

/* claim interrupt vector
 *
 * *NB* _do_not_use_ Buffer->StatusReg & Buffer->Mask as the Reg and Mask
 * because that reg bit could be shared with another device
 * You need a Reg and Mask in the registers of your actual device
 */

void PCIMgr_ClaimDeviceVector (PCIMgr_IntrMap *Buffer, volatile unsigned long *Reg, unsigned long Mask,
                               int (*Handler)(_kernel_swi_regs *, void *), void *WkSpc)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;

  regs.r[0] = (int) Buffer->Dev;
  regs.r[1] = (int) Handler;
  regs.r[2] = (int) WkSpc;
  regs.r[3] = (int) Reg;
  regs.r[4] = (int) Mask;
  if (Buffer->Dev >= 128) 
    err = _kernel_swi(MicroDigital_ClaimDeviceVector, &regs, &regs);
  else
    err = _kernel_swi(OS_ClaimDeviceVector, &regs, &regs);

/* now enable the irq */
  if (!err) {
    unsigned long oldstate;

    oldstate = ensure_irqs_off();
    *Buffer->EnableReg |= Buffer->Mask;
    restore_irqs(oldstate);
  }
}


void PCIMgr_ReleaseDeviceVector (PCIMgr_IntrMap *Buffer, volatile unsigned long *Reg, unsigned long Mask,
                                 int (*Handler)(_kernel_swi_regs *, void *), void *WkSpc)
{
  _kernel_swi_regs regs;

  regs.r[0] = (int) Buffer->Dev;
  regs.r[1] = (int) Handler;
  regs.r[2] = (int) WkSpc;
  regs.r[3] = (int) Reg;
  regs.r[4] = (int) Mask;
  if (Buffer->Dev >= 128) 
    _kernel_swi(MicroDigital_ReleaseDeviceVector, &regs, &regs);
  else
    _kernel_swi(OS_ReleaseDeviceVector, &regs, &regs);
}

/* use this to convert a list of buffer addresses into physical equivalent for passing to
 * a hardware DMA controller
 * Note that the Omega uses a 1 to 1 mapping (for SDRAM) between cpu physical space and 
 * PCI physical adrs space
 *
 * enter with the Pages[].Logical filled in with allocated buffer address
 * returns with the Pages[].Physical containing the physical equivalent
 */

void PCIMgr_Log2Phys (int PageCount, PageInfo *Pages)
{
  _kernel_swi_regs regs;

  regs.r[0] = 0x02200;
  regs.r[1] = (int) Pages;
  regs.r[2] = PageCount;
  _kernel_swi(OS_Memory, &regs, &regs);
}

/*
 * enter with the Pages[].Physical filled in with DMA buffer address
 * returns with the Pages[].Logical containing the logical equivalent
 */

void PCIMgr_Phys2Log (int PageCount, PageInfo *Pages)
{
  _kernel_swi_regs regs;

  regs.r[0] = 0x01400;
  regs.r[1] = (int) Pages;
  regs.r[2] = PageCount;
  _kernel_swi(OS_Memory, &regs, &regs);
}

/*
 * use this to create a Dynamic Area for hardware DMA buffer(s)
 * ie one with the memory pages being contiguous in physical as well as logical adrs space
 * this code does NOT anticipate any need to subsequently extend the DA
 *
 * enter with a name, the required (minimum) size in bytes, and ptr to buffer for result info
 * boundary is log2 of start boundary, eg 12 for 4K; 16 for 64K
 */

_kernel_oserror *PCIMgr_CreateDynamicArea (char *name, int size, int boundary, PCIMgr_DABufferInfo *result)
{
  _kernel_oserror *err;
  _kernel_swi_regs regs;
  int firstpage;
  PageInfo pages[1];

/* first get recommended page number for contiguous block */
  regs.r[0] = 12;
  regs.r[1] = size;
  regs.r[2] = boundary;
  err = _kernel_swi(OS_Memory, &regs, &regs);

/* now make the DA: */
  if (!err) {
    firstpage = regs.r[3];

    regs.r[0] = 0;
    regs.r[1] = -1;
    regs.r[2] = size;
    regs.r[3] = -1;
    regs.r[4] = 0x0180;      /* flags: needs specific physical pages; size is fixed, not draggable */
    regs.r[5] = size;
    regs.r[6] = (int) PCIMgr_DA_handler;  /* NB assembler routine; not 'C' so veneer not needed */
    regs.r[7] = (int) &firstpage;
    regs.r[8] = (int) name;
    err = _kernel_swi(OS_DynamicArea, &regs, &regs);
  }
  if (!err) {
    result->DA_handle = (unsigned long) regs.r[1];
    result->Size = (unsigned long) regs.r[5];
    pages[0].Logical =
    result->Logical = (void *) regs.r[3];

    PCIMgr_Log2Phys (1, pages);
    result->Physical = pages[0].Physical;

    return (0);
  }
  else {
    result->DA_handle = 0;
    return (err);
  }
}

void PCIMgr_RemoveDynamicArea (PCIMgr_DABufferInfo *area)
{
  _kernel_swi_regs regs;

  if (area->DA_handle) {
    regs.r[0] = 1;
    regs.r[1] = (int) area->DA_handle;
    _kernel_swi(OS_DynamicArea, &regs, &regs);
    area->DA_handle = 0;
  }
}

int PCIMgr_ReadMonotonicTime(void)
{
  _kernel_swi_regs regs;

  _kernel_swi(OS_ReadMonotonicTime, &regs, &regs);
  return (regs.r[0]);
}


/* end */
