Ejemplo n.º 1
0
/* Waits for a synchronized write to commit, using either polling or
 * an interrupt-driven waitqueue.
 */
static int32_t await_synced_write(struct audio_packetizer *packetizer) {
  int32_t returnValue = 0;

  /* Determine whether to use an interrupt or polling */
  if(packetizer->irq != NO_IRQ_SUPPLIED) {
    int32_t waitResult;

    /* Place ourselves onto a wait queue if the synced write is flagged as
     * pending by the hardware, as this indicates that the microengine is active,
     * and we need to wait for it to finish a pass through all the microcode 
     * before the hardware will commit the write to its destination (a register
     * or microcode RAM.)  If the engine is inactive or the write already 
     * committed, we will not actually enter the wait queue.
     */
    waitResult =
      wait_event_interruptible_timeout(packetizer->syncedWriteQueue,
				       ((XIo_In32(REGISTER_ADDRESS(packetizer, SYNC_REG)) & 
					 SYNC_PENDING) == 0),
				       msecs_to_jiffies(SYNCED_WRITE_TIMEOUT_MSECS));

    /* If the wait returns zero, then the timeout elapsed; if negative, a signal
     * interrupted the wait.
     */
    if(waitResult == 0) returnValue = -ETIMEDOUT;
    else if(waitResult < 0) returnValue = -EAGAIN;
  } else {
    /* No interrupt was supplied during the device probe, simply poll for the bit. */
    /* TODO: Need to introduce timeout semantics to this mode as well! */
    while(XIo_In32(REGISTER_ADDRESS(packetizer, SYNC_REG)) & SYNC_PENDING);
  }

  /* Return success or "timed out" */
  return(returnValue);
}
Ejemplo n.º 2
0
void get_local_time(struct ptp_device *ptp, PtpTime *time) {
  uint32_t timeWord;
  unsigned long flags;

  /* Write to the capture flag in the upper seconds word to initiate a capture,
   * then poll the same bit to determine when it has completed.  The capture only
   * takes a few RTC clocks, so this busy wait can only consume tens of nanoseconds.
   *
   * This will *not* modify the time, since we don't write the nanoseconds register.
   */
  preempt_disable();
  spin_lock_irqsave(&ptp->mutex, flags);
  iowrite32(PTP_RTC_LOCAL_CAPTURE_FLAG, REGISTER_ADDRESS(ptp, 0, PTP_LOCAL_SECONDS_HIGH_REG));
  do {
    timeWord = ioread32(REGISTER_ADDRESS(ptp, 0, PTP_LOCAL_SECONDS_HIGH_REG));
  } while((timeWord & PTP_RTC_LOCAL_CAPTURE_FLAG) != 0);

  /* Now read the entire captured time and pack it into the structure.  The last
   * value read during polling is perfectly valid.
   */
  time->secondsUpper = (uint16_t) timeWord;
  time->secondsLower = ioread32(REGISTER_ADDRESS(ptp, 0, PTP_LOCAL_SECONDS_LOW_REG));
  time->nanoseconds = ioread32(REGISTER_ADDRESS(ptp, 0, PTP_LOCAL_NANOSECONDS_REG));
  spin_unlock_irqrestore(&ptp->mutex, flags);
  preempt_enable();
}
Ejemplo n.º 3
0
/* Interrupt service routine for the instance */
static irqreturn_t mailbox_interrupt(int irq, void *dev_id) {
  struct labx_mailbox *mailbox = (struct labx_mailbox*) dev_id;
  uint32_t maskedFlags;
  uint32_t irqMask;
  irqreturn_t returnValue = IRQ_NONE;

  /* Read the interrupt flags and immediately clear them */
  maskedFlags = XIo_In32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_FLAGS_REG));
  irqMask = XIo_In32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_MASK_REG));
  
  maskedFlags &= irqMask;
  XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_FLAGS_REG), maskedFlags);

  /* Detect the host-to-supervisor message wait_received IRQ */
  if((maskedFlags & SUPRV_IRQ_0) != 0) {
    /* Set message ready flag before waking up thread */
    mailbox->messageReadyFlag = MESSAGE_READY;
    
    /* Wake up all threads waiting for a synchronization event */
    wake_up_interruptible(&(mailbox->messageReadQueue));
    returnValue = IRQ_HANDLED;
  }

  /* Return whether this was an IRQ we handled or not */
  return(returnValue);
}
Ejemplo n.º 4
0
/* Enables the passed instance */
static void enable_mailbox(struct spi_mailbox *mailbox) {
  uint32_t controlRegister;

  DBG("Enabling the mailbox\n");
  controlRegister = XIo_In32(REGISTER_ADDRESS(mailbox, CONTROL_REG));
  controlRegister |= MAILBOX_ENABLE;
  XIo_Out32(REGISTER_ADDRESS(mailbox, CONTROL_REG), controlRegister);
}
Ejemplo n.º 5
0
/* Configures a clock domain, including whether it is enabled */
static void configure_clock_domain(struct audio_packetizer *packetizer, 
                                   ClockDomainSettings *clockDomainSettings) {

  uint32_t controlRegister;
  uint32_t sampleRate = SINGLE_SAMPLE_RATE;

  DBG("Configure clock domain: sytInterval %d, enabled %d\n", clockDomainSettings->sytInterval, (int)clockDomainSettings->enabled);

  /* Set the timestamp interval, then enable or disable since we need to enable
   * last (it doesn't really matter if we disable last or not.)
   *
   * Actually use the SYT interval setting minus one, as the hardware uses this
   * as a terminal count value.
   */
  XIo_Out32(CLOCK_DOMAIN_REGISTER_ADDRESS(packetizer, clockDomainSettings->clockDomain, 
                                          TS_INTERVAL_REG),
            (clockDomainSettings->sytInterval - 1));

  /* Set the timestamp capture edge for audio samples. TODO: This should really
   * be a per-clock-domain setting, but isn't currently...
   */
  controlRegister = XIo_In32(REGISTER_ADDRESS(packetizer, CONTROL_REG));
  if (clockDomainSettings->sampleEdge == DOMAIN_SAMPLE_EDGE_RISING) {
    controlRegister |= SAMPLE_RISING_EDGE;
  } else {
    controlRegister &= ~SAMPLE_RISING_EDGE;
  }
  XIo_Out32(REGISTER_ADDRESS(packetizer, CONTROL_REG), controlRegister);

  /* Set the sample rate for the clock domain */
  switch(clockDomainSettings->sampleRate) {
  case ENGINE_SAMPLE_RATE_32_KHZ:
  case ENGINE_SAMPLE_RATE_44_1_KHZ:
  case ENGINE_SAMPLE_RATE_48_KHZ:
    sampleRate = SINGLE_SAMPLE_RATE;
    break;

  case ENGINE_SAMPLE_RATE_88_2_KHZ:
  case ENGINE_SAMPLE_RATE_96_KHZ:
    sampleRate = DOUBLE_SAMPLE_RATE;
    break;
  
  case ENGINE_SAMPLE_RATE_176_4_KHZ:
  case ENGINE_SAMPLE_RATE_192_KHZ:
    sampleRate = QUAD_SAMPLE_RATE;
    break;

  default:
    ;
  }
  XIo_Out32(REGISTER_ADDRESS(packetizer, SAMPLE_RATE_REG), sampleRate);

  /* Enable the clock domain */
  XIo_Out32(CLOCK_DOMAIN_REGISTER_ADDRESS(packetizer, clockDomainSettings->clockDomain,
                                          DOMAIN_ENABLE_REG),
            ((clockDomainSettings->enabled != 0) ? DOMAIN_ENABLED : DOMAIN_DISABLED));
}
Ejemplo n.º 6
0
/* Enables the passed instance */
void enable_mailbox(struct labx_mailbox *mailbox) {
  uint32_t controlRegister;

  DBG("Enabling the mailbox\n");
  controlRegister = XIo_In32(REGISTER_ADDRESS(mailbox, SUPRV_CONTROL_REG));
  controlRegister |= (MAILBOX_ENABLE | MAILBOX_API_ENABLE);
  XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_CONTROL_REG), controlRegister);
  DBG("Mailbox enabled\n")
}
Ejemplo n.º 7
0
void ptp_enable_irqs(struct ptp_device *ptp, int port)
{
    uint32_t irqMask;

    irqMask = (PTP_TIMER_IRQ | PTP_RX_IRQ | 
               PTP_TX_IRQ(PTP_TX_SYNC_BUFFER) |
               PTP_TX_IRQ(PTP_TX_DELAY_REQ_BUFFER) |
               PTP_TX_IRQ(PTP_TX_PDELAY_REQ_BUFFER) |
               PTP_TX_IRQ(PTP_TX_PDELAY_RESP_BUFFER));
    iowrite32(irqMask, REGISTER_ADDRESS(ptp, port, PTP_IRQ_FLAGS_REG));
    iowrite32(irqMask, REGISTER_ADDRESS(ptp, port, PTP_IRQ_MASK_REG));
}
Ejemplo n.º 8
0
/* Enables the passed instance */
static void enable_packetizer(struct audio_packetizer *packetizer) {
  uint32_t controlRegister;

  DBG("Enabling the packetizer\n");

  /* Only enable the microengine globally - individual outputs' enable status
   * is managed by a different method.
   */
  controlRegister = XIo_In32(REGISTER_ADDRESS(packetizer, CONTROL_REG));
  controlRegister |= PACKETIZER_ENABLE;
  XIo_Out32(REGISTER_ADDRESS(packetizer, CONTROL_REG), controlRegister);
}
Ejemplo n.º 9
0
/* Sets a new RTC time from the passed structure */
void set_rtc_time(struct ptp_device *ptp, PtpTime *time) {
  unsigned long flags;

  /* Write to the time register, beginning with the seconds.  The write to the 
   * nanoseconds register is what actually effects the change to the RTC.
   */
  preempt_disable();
  spin_lock_irqsave(&ptp->mutex, flags);
  iowrite32(time->secondsUpper, REGISTER_ADDRESS(ptp, 0, PTP_SECONDS_HIGH_REG));
  iowrite32(time->secondsLower, REGISTER_ADDRESS(ptp, 0, PTP_SECONDS_LOW_REG));
  iowrite32(time->nanoseconds, REGISTER_ADDRESS(ptp, 0, PTP_NANOSECONDS_REG));
  spin_unlock_irqrestore(&ptp->mutex, flags);
  preempt_enable();
}
Ejemplo n.º 10
0
/* Retrieve message length and message contents
 */
static uint32_t read_mailbox_message(struct spi_mailbox *mailbox, uint8_t* mailboxMessage) {
  uint32_t messageLength;
  uint32_t controlReg;
  int i;

  DBG("Read mailbox message \n");
  messageLength  = XIo_In32(REGISTER_ADDRESS(mailbox, HOST_MSG_LEN_REG));
  for(i=0; i < ((messageLength + 3)/4); i++) { 
    ((uint32_t*)mailboxMessage)[i] = XIo_In32(MSG_RAM_BASE(mailbox)+(i*4));
  }
  /* Toggle bit in control register to acknowledge message has been picked up */
  controlReg = XIo_In32(REGISTER_ADDRESS(mailbox, CONTROL_REG));
  XIo_Out32(REGISTER_ADDRESS(mailbox, CONTROL_REG), (controlReg | HOST_MESSAGE_CONSUMED));
  return(messageLength);
}
Ejemplo n.º 11
0
/* Read IRQ mask
 */
static uint8_t read_spi_irq_mask(struct spi_mailbox *mailbox) {
  uint8_t irqMask;
	
  DBG("Reading mailbox mask \n");
  irqMask = XIo_In32(REGISTER_ADDRESS(mailbox, SPI_IRQ_MASK_REG));
  return(irqMask);
}
Ejemplo n.º 12
0
/* Read IRQ flags
 */
static uint8_t read_spi_irq_flags(struct spi_mailbox *mailbox) {
  uint8_t irqFlags;
	
  DBG("Clearing mailbox flags \n");
  irqFlags = XIo_In32(REGISTER_ADDRESS(mailbox, SPI_IRQ_FLAGS_SET_REG));
  return(irqFlags);
}
Ejemplo n.º 13
0
/* Loads the passed microcode descriptor into the instance */
static int32_t load_descriptor(struct audio_packetizer *packetizer,
                               ConfigWords *descriptor) {
  uint32_t wordIndex;
  uintptr_t wordAddress;
  uint32_t lastIndex;
  int32_t returnValue = 0;

  /* Handle the last write specially for interlocks */
  lastIndex = descriptor->numWords;
  if(descriptor->interlockedLoad) lastIndex--;

  wordAddress = (MICROCODE_RAM_BASE(packetizer) + (descriptor->offset * sizeof(uint32_t)));
  DBG("Loading descriptor @ %p (%d), numWords %d\n", (void*)wordAddress, descriptor->offset, descriptor->numWords);
  for(wordIndex = 0; wordIndex < lastIndex; wordIndex++) {
    XIo_Out32(wordAddress, descriptor->configWords[wordIndex]);
    wordAddress += sizeof(uint32_t);
  }

  /* If an interlocked load is requested, issue a sync command on the last write */
  if(descriptor->interlockedLoad) {
    /* Request a synchronized write for the last word and wait for it */
    XIo_Out32(REGISTER_ADDRESS(packetizer, SYNC_REG), SYNC_NEXT_WRITE);
    XIo_Out32(wordAddress, descriptor->configWords[wordIndex]);
    returnValue = await_synced_write(packetizer);
  }

  return(returnValue);
}
Ejemplo n.º 14
0
/* Configures the credit-based shaper stage */
static void configure_shaper(struct audio_packetizer *packetizer,
                             uint32_t enabled, int32_t sendSlope,
                             int32_t idleSlope) {
  uint32_t controlRegister;
  uint32_t irqMask;

  controlRegister = XIo_In32(REGISTER_ADDRESS(packetizer, CONTROL_REG));
  if(enabled == CREDIT_SHAPER_ENABLED) {
    /* Going to be enabling the shaper; re-arm the error IRQs associated with
     * bandwidth problems.  This allows us to detect and subsequently report any
     * problems arising from misconfiguration of the shaper, but not get slammed
     * with ongoing interrupts.
     */
    XIo_Out32(REGISTER_ADDRESS(packetizer, IRQ_FLAGS_REG), 
              (ENGINE_LATE_IRQ | OVERRUN_IRQ));
    irqMask = XIo_In32(REGISTER_ADDRESS(packetizer, IRQ_MASK_REG));
    irqMask |= (ENGINE_LATE_IRQ | OVERRUN_IRQ);
    XIo_Out32(REGISTER_ADDRESS(packetizer, IRQ_MASK_REG), irqMask);

    /* The client is responsible for ensuring that the slopes are properly-scaled
     * signed fractional quantities, per the number of fractional bits advertised
     * in the capabilities register.  Writing to the idleSlope register triggers
     * an atomic load of both slopes into the credit accumulator.
     */
    XIo_Out32(REGISTER_ADDRESS(packetizer, SEND_SLOPE_REG), sendSlope);
    XIo_Out32(REGISTER_ADDRESS(packetizer, IDLE_SLOPE_REG), idleSlope);
    controlRegister |= SHAPER_ENABLE;
  } else {
    /* Simply disable the shaper, ignoring the slope parameters */
    controlRegister &= ~SHAPER_ENABLE;
  }
  XIo_Out32(REGISTER_ADDRESS(packetizer, CONTROL_REG), controlRegister);
}
Ejemplo n.º 15
0
/* Write response out to a received message
 */
static void send_message_response(struct spi_mailbox *mailbox, 
                                  MessageData *data) {
  int i;
  DBG("Writing response message \n");

  for(i=0; i < ((data->length + 3)/4); i++) {
    XIo_Out32(MSG_RAM_BASE(mailbox)+(i*4), ((uint32_t*)data->messageContent)[i]);
  }
  XIo_Out32(REGISTER_ADDRESS(mailbox, HOST_MSG_LEN_REG), data->length);
}
Ejemplo n.º 16
0
static int32_t set_output_enabled(struct audio_packetizer *packetizer,
                                  uint32_t whichOutput, 
                                  uint32_t enable) {
  uint32_t outputMask;
  uint32_t controlRegister;

  /* Sanity-check the whichOutput parameter */
  if(whichOutput >= packetizer->capabilities.numOutputs) {
    return(-EINVAL);
  }

  /* Enable or disable the requested output in the control register */
  outputMask = (OUTPUT_A_ENABLE << whichOutput);
  controlRegister = XIo_In32(REGISTER_ADDRESS(packetizer, CONTROL_REG));
  if(enable == PACKETIZER_OUTPUT_ENABLE) {
    controlRegister |= outputMask;
  } else controlRegister &= ~outputMask;
  XIo_Out32(REGISTER_ADDRESS(packetizer, CONTROL_REG), controlRegister);

  return 0;
}
Ejemplo n.º 17
0
/* Interrupt service routine for AES stream statuses */
static irqreturn_t aes3_rx_interrupt(int irq, void *dev_id) {
  struct aes3_rx *rx = (struct aes3_rx*) dev_id;

  /* reading the status reg will clear the interrupt */
  XIo_In32(REGISTER_ADDRESS(rx, AES_RX_STREAM_STATUS_REG));

  /* Wake up the Netlink thread to consume status data */
  rx->statusReadyAes = AES_NEW_STATUS_READY;
  wake_up_interruptible(&(rx->statusFifoQueue));

  return(IRQ_HANDLED);
}
Ejemplo n.º 18
0
/* Interrupt service routine for metering status */
static irqreturn_t meter_interrupt(int irq, void *dev_id) {
  struct aes3_rx *rx = (struct aes3_rx*) dev_id;

  /* Reading either metering register clears the interrupt */
  XIo_In32(REGISTER_ADDRESS(rx, AES_TX_AUDIO_METER_REG));

  /* Wake up the Netlink thread to consume status data */
  rx->statusReadyMeter = AES_NEW_STATUS_READY;
  wake_up_interruptible(&(rx->statusFifoQueue));

  return(IRQ_HANDLED);
}
Ejemplo n.º 19
0
/* Transmits the packet within the specified buffer.  The first word of the
 * buffer must contain the packet length minus one, in bytes.
 */
void transmit_packet(struct ptp_device *ptp, uint32_t port, uint8_t * txBuffer) {
  int i;
  for (i=0; i<PTP_TX_BUFFER_COUNT; i++) {
    if ((uintptr_t)txBuffer == PTP_TX_PACKET_BUFFER(ptp, port, i)) break;
  }

  /* Don't bother checking the busy flag; it only exists to ensure that we know the
   * pending flags are valid.  We're not using them anyways - the only hazard that
   * exists is if we attempt to send two packets from the same buffer simultaneously.
   */
  iowrite32(((1<<i) | PTP_TX_ENABLE), REGISTER_ADDRESS(ptp, port, PTP_TX_REG));
}
Ejemplo n.º 20
0
/* Interrupt service routine for the instance */
static irqreturn_t biamp_spi_mailbox_interrupt(int irq, void *dev_id) {
  struct spi_mailbox *mailbox = (struct spi_mailbox*) dev_id;
  uint32_t maskedFlags;
  uint32_t irqMask;

  /* Read the interrupt flags and immediately clear them */
  maskedFlags = XIo_In32(REGISTER_ADDRESS(mailbox, IRQ_FLAGS_REG));
  irqMask = XIo_In32(REGISTER_ADDRESS(mailbox, IRQ_MASK_REG));

  maskedFlags &= irqMask;
  XIo_Out32(REGISTER_ADDRESS(mailbox, IRQ_FLAGS_REG), maskedFlags);

  /* Detect the slave-to-host message wait_received IRQ */
  if((maskedFlags & IRQ_S2H_MSG_RX) != 0) {
    /* Set message ready flag before waking up thread */
    mailbox->messageReadyFlag = MESSAGE_READY;
    /* Wake up all threads waiting for a synchronization event */
    wake_up_interruptible(&(mailbox->messageReadQueue));
  }

  return(IRQ_HANDLED);
}
Ejemplo n.º 21
0
/* Sets the RTC increment, simultaneously enabling the RTC */
void set_rtc_increment(struct ptp_device *ptp, RtcIncrement *increment) {
  uint32_t incrementWord;

  /* Save the current increment if anyone needs it */
  ptp->currentIncrement = *increment;

  /* Assemble a single value from the increment components */
  incrementWord = ((increment->mantissa & RTC_MANTISSA_MASK) << RTC_MANTISSA_SHIFT);
  incrementWord |= (increment->fraction & RTC_FRACTION_MASK);
  incrementWord |= PTP_RTC_ENABLE;

  /* The actual write is already atomic, so no need to ensure mutual exclusion */
  iowrite32(incrementWord, REGISTER_ADDRESS(ptp, 0, PTP_RTC_INC_REG));
}
Ejemplo n.º 22
0
void ptp_process_rx(struct ptp_device *ptp, int port)
{
    uint32_t newRxBuffer;
    uint32_t bufferBase;

    /* Process all messages received since the last time we ran */
    newRxBuffer = (ioread32(REGISTER_ADDRESS(ptp, port, PTP_RX_REG)) & PTP_RX_BUFFER_MASK);
    while(ptp->ports[port].lastRxBuffer != newRxBuffer) {
      /* Advance the last buffer circularly around the available Rx buffers */
      ptp->ports[port].lastRxBuffer = ((ptp->ports[port].lastRxBuffer + 1) & PTP_RX_BUFFER_MASK);

      /* Fetch the word containing the LTF and the message type */
      bufferBase = PTP_RX_PACKET_BUFFER(ptp, port, ptp->ports[port].lastRxBuffer);
      process_rx_buffer(ptp,port,(uint8_t *)bufferBase);
    }
}
Ejemplo n.º 23
0
void ptp_platform_init(struct ptp_device *ptp, int port)
{
    ptp->ports[port].lastRxBuffer = (ioread32(REGISTER_ADDRESS(ptp, port, PTP_RX_REG)) & PTP_RX_BUFFER_MASK);
}
Ejemplo n.º 24
0
/* Disables the RTC */
void disable_rtc(struct ptp_device *ptp) {
  iowrite32(PTP_RTC_DISABLE, REGISTER_ADDRESS(ptp, 0, PTP_RTC_INC_REG));
}
Ejemplo n.º 25
0
/* Function containing the "meat" of the probe mechanism - this is used by
 * the OpenFirmware probe as well as the standard platform device mechanism.
 * @param name - Name of the instance
 * @param pdev - Platform device structure
 * @param addressRange - Resource describing the hardware's I/O range
 * @param irq          - Resource describing the hardware's IRQ
 */
static int mailbox_probe(const char *name, 
                             struct platform_device *pdev,
                             struct resource *addressRange,
                             struct resource *irq) {
  struct labx_mailbox *mailbox;
  int returnValue;
  int i;

  /* Create and populate a device structure */
  mailbox = (struct labx_mailbox*) kmalloc(sizeof(struct labx_mailbox), GFP_KERNEL);
  if(!mailbox) return(-ENOMEM);

  /* Request and map the device's I/O memory region into uncacheable space */
  mailbox->physicalAddress = addressRange->start;
  mailbox->addressRangeSize = ((addressRange->end - addressRange->start) + 1);
  snprintf(mailbox->name, NAME_MAX_SIZE, "%s", name);
  mailbox->name[NAME_MAX_SIZE - 1] = '\0';
  if(request_mem_region(mailbox->physicalAddress, mailbox->addressRangeSize,
                        mailbox->name) == NULL) {
    returnValue = -ENOMEM;
    goto free;
  }

  mailbox->virtualAddress = 
    (void*) ioremap_nocache(mailbox->physicalAddress, mailbox->addressRangeSize);
  if(!mailbox->virtualAddress) {
    returnValue = -ENOMEM;
    goto release;
  }

  /* Ensure that the mailbox and its interrupts are disabled */
  disable_mailbox(mailbox);
  XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_MASK_REG), NO_IRQS);

  /* Clear the message ready flag for the first time */
  mailbox->messageReadyFlag = MESSAGE_NOT_READY;
  
  /* Retain the IRQ and register our handler, if an IRQ resource was supplied. */
  if(irq != NULL) {
    mailbox->irq = irq->start;
    returnValue = request_irq(mailbox->irq, &mailbox_interrupt, IRQF_DISABLED,
                              mailbox->name, mailbox);
    if (returnValue) {
      printk(KERN_ERR "%s : Could not allocate Mailbox interrupt (%d).\n",
             mailbox->name, mailbox->irq);
      goto unmap;
    }
  } else mailbox->irq = NO_IRQ_SUPPLIED;
  
  /* Announce the device */
  printk(KERN_INFO "%s: Found mailbox at 0x%08X, ",
         mailbox->name, (uint32_t)mailbox->physicalAddress);
  if(mailbox->irq == NO_IRQ_SUPPLIED) {
    printk("polled interlocks\n");
  } else {
    printk("IRQ %d\n", mailbox->irq);
  }

  /* Initialize other resources */
  spin_lock_init(&mailbox->mutex);
  mailbox->opened = true;

  /* Provide navigation between the device structures */
  platform_set_drvdata(pdev, mailbox);
  mailbox->pdev = pdev;

  /* Reset the state of the mailbox */
  reset_mailbox(mailbox);

  /* Initialize the waitqueue used for synchronized writes */
  init_waitqueue_head(&(mailbox->messageReadQueue));

  /* Initialize the netlink state and start the thread */
  mailbox->netlinkSequence = 0;
  mailbox->netlinkTask = kthread_run(netlink_thread, (void*)mailbox, "%s:netlink", mailbox->name);
  if (IS_ERR(mailbox->netlinkTask)) {
    printk(KERN_ERR "Mailbox netlink task creation failed.\n");
    returnValue = -EIO;
    goto free;
  }
  
  /* Now that the device is configured, enable interrupts if they are to be used */
  if(mailbox->irq != NO_IRQ_SUPPLIED) {
    XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_MASK_REG), ALL_IRQS);
    XIo_Out32(REGISTER_ADDRESS(mailbox, SUPRV_IRQ_FLAGS_REG), ALL_IRQS);
  }

  // Add the mailbox instance to the list of current devices
  for(i=0;i<MAX_MAILBOX_DEVICES;i++) {
    if(NULL == labx_mailboxes[i]) {
      labx_mailboxes[i] = mailbox;
      printk("Adding mailbox: %s\n", labx_mailboxes[i]->name);
      break;
    }
  }

  DBG("Mailbox initialized\n");

  /* Return success */
  return(0);

 unmap:
  iounmap(mailbox->virtualAddress);
 release:
  release_mem_region(mailbox->physicalAddress, mailbox->addressRangeSize);
 free:
  kfree(mailbox);
  return(returnValue);
}
Ejemplo n.º 26
0
/* Interrupt service routine for the instance */
static irqreturn_t labx_ptp_interrupt(int irq, void *dev_id)
{
  struct ptp_device *ptp = dev_id;
  uint32_t maskedFlags;
  uint32_t txCompletedFlags;
  uint32_t newRxBuffer;
  unsigned long flags;
  int i;

  for (i=0; i<ptp->numPorts; i++) {
    /* Read the interrupt flags and immediately clear them */
    maskedFlags = ioread32(REGISTER_ADDRESS(ptp, i, PTP_IRQ_FLAGS_REG));
    maskedFlags &= ioread32(REGISTER_ADDRESS(ptp, i, PTP_IRQ_MASK_REG));
    iowrite32(maskedFlags, REGISTER_ADDRESS(ptp, i, PTP_IRQ_FLAGS_REG));

    /* Detect the timer IRQ */
    if((maskedFlags & PTP_TIMER_IRQ) != 0) {

      preempt_disable();
      spin_lock_irqsave(&ptp->mutex, flags);
      ptp->timerTicks++;
      spin_unlock_irqrestore(&ptp->mutex, flags);
      preempt_enable();

#ifdef CONFIG_LABX_PTP_NO_TASKLET
      labx_ptp_timer_state_task((uintptr_t)ptp);
#else
      /* Kick off the timer tasklet */
      tasklet_hi_schedule(&ptp->timerTasklet);
#endif
    }

    /* Detect the Tx IRQ from any enabled buffer bits */
    txCompletedFlags = (maskedFlags & PTP_TX_IRQ_MASK);
    if(txCompletedFlags != PTP_TX_BUFFER_NONE) {
      /* Add the new pending Tx buffer IRQ flags to the mask in the device
       * structure for the tasklet to whittle away at.  Lock the mutex so we
       * avoid a race condition with the Tx tasklet.
       */
      preempt_disable();
      spin_lock_irqsave(&ptp->mutex, flags);
      ptp->ports[i].pendingTxFlags |= txCompletedFlags;
      spin_unlock_irqrestore(&ptp->mutex, flags);
      preempt_enable();

#ifdef CONFIG_LABX_PTP_NO_TASKLET
      labx_ptp_tx_state_task((uintptr_t)ptp);
#else
      /* Now kick off the Tx tasklet */
      tasklet_hi_schedule(&ptp->txTasklet);
#endif
    }

    /* Detect the Rx IRQ */
    newRxBuffer = (ioread32(REGISTER_ADDRESS(ptp, i, PTP_RX_REG)) & PTP_RX_BUFFER_MASK);
    if ( ((maskedFlags & PTP_RX_IRQ) != 0) || (ptp->ports[i].lastRxBuffer != newRxBuffer) ) {
#ifdef CONFIG_LABX_PTP_NO_TASKLET
      labx_ptp_rx_state_task((uintptr_t)ptp);
#else
      /* Kick off the Rx tasklet */
      tasklet_hi_schedule(&ptp->rxTasklet);
#endif
    }
  }
  return(IRQ_HANDLED);
}
Ejemplo n.º 27
0
/* Function containing the "meat" of the probe mechanism - this is used by
 * the OpenFirmware probe as well as the standard platform device mechanism.
 * This is exported to allow polymorphic drivers to invoke it.
 * @param name - Name of the instance
 * @param pdev - Platform device structure
 * @param addressRange - Resource describing the hardware's I/O 
 * @param irq          - Resource describing the hardware's interrupt
 */
int aes3_rx_probe(const char *name, 
                  struct platform_device *pdev,
                  struct resource *addressRange,
                  struct resource *irq,
                  struct file_operations *derivedFops,
                  void *derivedData,
                  struct aes3_rx **newInstance) {
  struct aes3_rx *rx;
  int returnValue;

  /* Create and populate a device structure */
  rx = (struct aes3_rx*) kmalloc(sizeof(struct aes3_rx), GFP_KERNEL);
  if(!rx) return(-ENOMEM);

  /* Request and map the device's I/O memory region into uncacheable space */
  rx->physicalAddress = addressRange->start;
  rx->addressRangeSize = ((addressRange->end - addressRange->start) + 1);
  snprintf(rx->name, NAME_MAX_SIZE, "%s", name);
  rx->name[NAME_MAX_SIZE - 1] = '\0';
  if(request_mem_region(rx->physicalAddress, rx->addressRangeSize,
                        rx->name) == NULL) {
    returnValue = -ENOMEM;
    goto free;
  }

  rx->virtualAddress = 
    (void*) ioremap_nocache(rx->physicalAddress, rx->addressRangeSize);
  if(!rx->virtualAddress) {
    returnValue = -ENOMEM;
    goto release;
  }
  
  /* Initialize the waitqueue used for status FIFO events */
  init_waitqueue_head(&(rx->statusFifoQueue));


  /* Ensure that the engine and its interrupts are disabled */
  XIo_Out32(REGISTER_ADDRESS(rx, AES_RX_STREAM_STATUS_REG), NO_IRQS);

  /*Initialize the Stream mask, enable all the 8 streams*/
  XIo_Out32(REGISTER_ADDRESS(rx, AES_STREAM_MASK_REG), 0xff);

  /* Run the thread assigned to converting status FIFO words into Netlink packets
   * after initializing its state to wait for the ISR
   */
  rx->statusReadyAes = AES_STATUS_IDLE;
  rx->statusReadyMeter = AES_STATUS_IDLE;
  rx->netlinkSequence = 0;
  rx->netlinkTask = kthread_run(netlink_thread, (void*) rx, "%s:netlink", rx->name);
  if (IS_ERR(rx->netlinkTask)) {
    printk(KERN_ERR "%s: AES Netlink task creation failed\n", rx->name);
    return(-EIO);
  }

  /* Retain the IRQ and register our handler, if an IRQ resource was supplied. */
  if(irq != NULL) {
    rx->meter_irq = irq[0].start;
    returnValue = request_irq(rx->meter_irq, &meter_interrupt, IRQF_DISABLED, rx->name, rx);
    if (returnValue) {
      printk(KERN_ERR "%s : Could not allocate Lab X Mosaic AES3 RX metering interrupt (%d).\n",
             rx->name, rx->meter_irq);
      goto unmap;
    }
    rx->aes_irq = irq[1].start;
    returnValue = request_irq(rx->aes_irq, &aes3_rx_interrupt, IRQF_DISABLED, rx->name, rx);
    if (returnValue) {
      printk(KERN_ERR "%s : Could not allocate Lab X Mosaic AES3 RX channel interrupt (%d).\n",
             rx->name, rx->aes_irq);
      goto unmap;
    }
  } else {
    rx->meter_irq = NO_IRQ_SUPPLIED;
    rx->aes_irq = NO_IRQ_SUPPLIED;
  }

  if(rx->meter_irq == NO_IRQ_SUPPLIED && rx->aes_irq == NO_IRQ_SUPPLIED) {
    printk("%s: polled commands\n", rx->name);
  } else {
    printk("%s: IRQ %d, %d\n", rx->name, rx->meter_irq, rx->aes_irq);
  }

  /* Initialize other resources */
  spin_lock_init(&rx->mutex);

  /* Provide navigation between the device structures */
  platform_set_drvdata(pdev, rx);
  rx->pdev = pdev;

  /* Add as a character device to make the instance available for use */
  cdev_init(&rx->cdev, &aes3_rx_fops);
  rx->cdev.owner = THIS_MODULE;
  rx->instanceNumber = instanceCount++;
  kobject_set_name(&rx->cdev.kobj, "%s.%d", rx->name, rx->instanceNumber);
  cdev_add(&rx->cdev, MKDEV(DRIVER_MAJOR, rx->instanceNumber), 1);

  /*Initialize deviceNode, to be used in the netlink messages */
  rx->deviceNode = MKDEV(DRIVER_MAJOR, rx->instanceNumber);

  /* Retain any derived file operations & data to dispatch to */
  rx->derivedFops = derivedFops;
  rx->derivedData = derivedData;

  /* Return success, setting the return pointer if valid */
  if(newInstance != NULL) *newInstance = rx;
  return(0);

 unmap:
  iounmap(rx->virtualAddress);
 release:
  release_mem_region(rx->physicalAddress, rx->addressRangeSize);
 free:
  kfree(rx);
  return(returnValue);
      
}
Ejemplo n.º 28
0
/* I/O control operations for the driver */
static int aes3_rx_ioctl(struct inode *inode, 
                                   struct file *filp,
                                   unsigned int command, 
                                   unsigned long arg) {
 int returnValue = 0;
 uint32_t Value;
 struct aes3_rx *rx = (struct aes3_rx*)filp->private_data;

 switch(command) {
     
 case IOC_READ_RX_STREAM_STATUS:
   {
     /* Get the rx stream status, then copy into the userspace pointer */
     Value = XIo_In32(REGISTER_ADDRESS(rx, AES_RX_STREAM_STATUS_REG));
     if(copy_to_user((void __user*)arg, &Value, 
                     sizeof(uint32_t)) != 0) {
       return(-EFAULT);
     }
   }
   break;

 case IOC_READ_TX_STREAM_STATUS:
   {
     /* Get the tx stream status, then copy into the userspace pointer */
     Value = XIo_In32(REGISTER_ADDRESS(rx, AES_TX_STREAM_STATUS_REG));
     if(copy_to_user((void __user*)arg, &Value, 
                     sizeof(uint32_t)) != 0) {
       return(-EFAULT);
     }
   }
   break;  

 case IOC_CONFIG_AES: 
  {
   /* Write the AES configuration register. */
   if(copy_from_user(&Value, (void __user*)arg, sizeof(Value)) != 0) {
        return(-EFAULT);
   }
   XIo_Out32(REGISTER_ADDRESS(rx, AES_CONTROL_REG), Value);
  }
   break;
   
 case IOC_CONFIG_RX_PCM_MODE:
  {
   /* Write the RX PCM configuration register. */
   if(copy_from_user(&Value, (void __user*)arg, sizeof(Value)) != 0) {
        return(-EFAULT);
   }
   XIo_Out32(REGISTER_ADDRESS(rx, AES_RX_PCM_MODE_REG), Value);
  }
   break;
   
 case IOC_CONFIG_TX_PCM_MODE:
  {
   /* Write the TX PCM configuration register. */
   if(copy_from_user(&Value, (void __user*)arg, sizeof(Value)) != 0) {
        return(-EFAULT);
   }
   XIo_Out32(REGISTER_ADDRESS(rx, AES_TX_PCM_MODE_REG), Value);
  }
   break;
   
 case IOC_CONFIG_2CHAN_MODE:
  {
   /* Write the two-channel mode configuration register. */
   if(copy_from_user(&Value, (void __user*)arg, sizeof(Value)) != 0) {
        return(-EFAULT);
   }
   XIo_Out32(REGISTER_ADDRESS(rx, AES_TX_2CHAN_MODE_REG), Value);
  }
   break;
   
  case IOC_READ_AES_MASK:
    {
      /* Get the stream mask, then copy into the userspace pointer */
      Value = XIo_In32(REGISTER_ADDRESS(rx, AES_STREAM_MASK_REG));
      if(copy_to_user((void __user*)arg, &Value, 
                      sizeof(uint32_t)) != 0) {
        return(-EFAULT);
      }
    }
    break;
   
  case IOC_SET_AES_MASK: 
    {
      if(copy_from_user(&Value, (void __user*)arg, sizeof(Value)) != 0) {
           return(-EFAULT);
      }
      XIo_Out32(REGISTER_ADDRESS(rx, AES_STREAM_MASK_REG), Value);
    }
    break;

  case IOC_READ_RX_METER_STATUS:
    {
      /* Get the stream mask, then copy into the userspace pointer */
      Value = XIo_In32(REGISTER_ADDRESS(rx, AES_RX_AUDIO_METER_REG));
      if(copy_to_user((void __user*)arg, &Value, sizeof(uint32_t)) != 0) {
        return(-EFAULT);
      }
    }
    break;
   
  case IOC_READ_TX_METER_STATUS:
    {
      /* Get the stream mask, then copy into the userspace pointer */
      Value = XIo_In32(REGISTER_ADDRESS(rx, AES_RX_AUDIO_METER_REG));
      if(copy_to_user((void __user*)arg, &Value, sizeof(uint32_t)) != 0) {
        return(-EFAULT);
      }
    }
    break;
   
  default:
    if((rx->derivedFops != NULL) && 
       (rx->derivedFops->ioctl != NULL)) {
      returnValue = rx->derivedFops->ioctl(inode, filp, command, arg);
    } else returnValue = -EINVAL;  
 
  }
  /* Return an error code appropriate to the command */
  return(returnValue);
}
Ejemplo n.º 29
0
/* Method to conditionally transmit a Netlink packet containing one or more
 * packets of information from the status FIFO */
static int tx_netlink_status(struct aes3_rx *rx) {
  struct sk_buff *skb;
  void *msgHead;
  int returnValue = 0;
  uint32_t status;

  /* Make sure we have something to do */
  if(rx->statusReadyAes != AES_NEW_STATUS_READY && rx->statusReadyMeter != AES_NEW_STATUS_READY) {
    return(returnValue);
  }

  /* We will send a packet, allocate a socket buffer */
  skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
  if(skb == NULL) return(-ENOMEM);

  /* Create the message headers. AES status messages get priority. */
  msgHead = genlmsg_put(skb, 0, rx->netlinkSequence++, &events_genl_family, 0, (rx->statusReadyAes == AES_NEW_STATUS_READY ? LABX_AES_EVENTS_C_AES_STATUS_PACKETS : LABX_AES_EVENTS_C_METER_STATUS_PACKETS));
  if(msgHead == NULL) {
    returnValue = -ENOMEM;
    goto tx_failure;
  }

  /* Write the AES device's ID, properly translated for userspace, to identify the
   * message source.  The full ID is required since this driver can be used encapsulated 
   * within any number of more complex devices. */
  returnValue = nla_put_u32(skb, LABX_AES_EVENTS_A_AES_DEVICE, new_encode_dev(rx->deviceNode));
  if(returnValue != 0) goto tx_failure;

  /* AES interrupts get priority. Calling code must make sure to call us multiple
   * times if there are multiple statuses to send out. */
  if(rx->statusReadyAes == AES_NEW_STATUS_READY) {
    /* Clear the AES status flag */
    rx->statusReadyAes = AES_STATUS_IDLE;

    /* Read the AES rx status register and send it out */
    status = XIo_In32(REGISTER_ADDRESS(rx, AES_RX_STREAM_STATUS_REG));
    returnValue = nla_put_u32(skb, LABX_AES_EVENTS_A_AES_RX_STATUS, status);
    if(returnValue != 0) goto tx_failure;

    /* Read the AES tx status register and send it out */
    status = XIo_In32(REGISTER_ADDRESS(rx, AES_TX_STREAM_STATUS_REG));
    returnValue = nla_put_u32(skb, LABX_AES_EVENTS_A_AES_TX_STATUS, status);
    if(returnValue != 0) goto tx_failure;
  } else if(rx->statusReadyMeter == AES_NEW_STATUS_READY) {
    /* Clear the metering status flag */
    rx->statusReadyMeter = AES_STATUS_IDLE;

    /* Read the metering rx status register and send it out */
    status = XIo_In32(REGISTER_ADDRESS(rx, AES_RX_AUDIO_METER_REG));
    returnValue = nla_put_u32(skb, LABX_AES_EVENTS_A_METER_RX_STATUS, status);
    if(returnValue != 0) goto tx_failure;

    /* Read the metering tx status register and send it out */
    status = XIo_In32(REGISTER_ADDRESS(rx, AES_TX_AUDIO_METER_REG));
    returnValue = nla_put_u32(skb, LABX_AES_EVENTS_A_METER_TX_STATUS, status);
    if(returnValue != 0) goto tx_failure;
  }

  /* Finalize the message and multicast it */
  genlmsg_end(skb, msgHead);
  returnValue = genlmsg_multicast(skb, 0, labx_aes_mcast.id, GFP_ATOMIC);

  switch(returnValue) {
  case 0:
  case -ESRCH:
    /* Success or no process was listening, simply break */
    break;
  default:
    /* This is an actual error, print the return code */
    printk(KERN_INFO DRIVER_NAME ": Failure delivering multicast Netlink message: %d\n", returnValue);
    goto tx_failure;
  }

 tx_failure:
  return(returnValue);
}
Ejemplo n.º 30
0
/* Configure the prescaler and divider used to generate a 10 msec event timer.
 * The register values are terminal counts, so are one less than the count value.
 */
void ptp_setup_event_timer(struct ptp_device *ptp, int port, PtpPlatformData *platformData)
{
    iowrite32( (((platformData->timerPrescaler - 1) & PTP_PRESCALER_MASK) |
               (((platformData->timerDivider - 1) & PTP_DIVIDER_MASK) << PTP_DIVIDER_SHIFT)), 
                                                    REGISTER_ADDRESS(ptp, 0, PTP_TIMER_REG));
}