mirror of
https://github.com/phabrics/Run-Sun3-SunOS-4.1.1.git
synced 2026-04-29 11:02:59 -04:00
1095 lines
33 KiB
C
1095 lines
33 KiB
C
/* $Id: stp2024.c,v 1.3 2009/11/08 16:21:32 fredette Exp $ */
|
|
|
|
/* ic/stp2024.c - STP2024 (Aurora Personality Chip DMA) implementation: */
|
|
|
|
/*
|
|
* Copyright (c) 2009 Matt Fredette
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by Matt Fredette.
|
|
* 4. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <tme/common.h>
|
|
_TME_RCSID("$Id: stp2024.c,v 1.3 2009/11/08 16:21:32 fredette Exp $");
|
|
|
|
/* includes: */
|
|
#include <tme/element.h>
|
|
#undef TME_BUS_VERSION
|
|
#define TME_BUS_VERSION TME_X_VERSION(0, 0)
|
|
#include <tme/generic/bus.h>
|
|
#include <tme/completion.h>
|
|
|
|
/* macros: */
|
|
|
|
/* the device offsets: */
|
|
#define TME_STP2024_OFFSET_POWER_IDLE (0xa000000)
|
|
#define TME_STP2024_OFFSET_POWER_FANCTL (0xa000020)
|
|
#define TME_STP2024_OFFSET_POWER_CPOWER (0xa000024)
|
|
#define TME_STP2024_OFFSET_POWER_BPORT (0xa000030)
|
|
#define TME_STP2024_OFFSET_CODEC_REG0 (0xc000000)
|
|
#define TME_STP2024_OFFSET_CODEC_REG1 (0xc000004)
|
|
#define TME_STP2024_OFFSET_CODEC_REG2 (0xc000008)
|
|
#define TME_STP2024_OFFSET_CODEC_REG3 (0xc00000c)
|
|
#define TME_STP2024_OFFSET_DMA_CSR (0xc000010)
|
|
#define TME_STP2024_OFFSET_DMA_CVA (0xc000020)
|
|
#define TME_STP2024_OFFSET_DMA_CC (0xc000024)
|
|
#define TME_STP2024_OFFSET_DMA_CNVA (0xc000028)
|
|
#define TME_STP2024_OFFSET_DMA_CNC (0xc00002c)
|
|
#define TME_STP2024_OFFSET_DMA_PVA (0xc000030)
|
|
#define TME_STP2024_OFFSET_DMA_PC (0xc000034)
|
|
#define TME_STP2024_OFFSET_DMA_PNVA (0xc000038)
|
|
#define TME_STP2024_OFFSET_DMA_PNC (0xc00003c)
|
|
|
|
/* the fan control register: */
|
|
#define TME_STP2024_POWER_FANCTL_LOW (1 << 0)
|
|
#define TME_STP2024_POWER_FANCTL_ID (0xe)
|
|
|
|
/* the DMA CSR: */
|
|
#define TME_STP2024_DMA_CSR_ID (0xff << 24)
|
|
#define TME_STP2024_DMA_CSR_IP (1 << 23)
|
|
#define TME_STP2024_DMA_CSR_PI (1 << 22)
|
|
#define TME_STP2024_DMA_CSR_CI (1 << 21)
|
|
#define TME_STP2024_DMA_CSR_EI (1 << 20)
|
|
#define TME_STP2024_DMA_CSR_IE (1 << 19)
|
|
#define TME_STP2024_DMA_CSR_PIE (1 << 18)
|
|
#define TME_STP2024_DMA_CSR_CIE (1 << 17)
|
|
#define TME_STP2024_DMA_CSR_EIE (1 << 16)
|
|
#define TME_STP2024_DMA_CSR_PMI (1 << 15)
|
|
#define TME_STP2024_DMA_CSR_PM (1 << 14)
|
|
#define TME_STP2024_DMA_CSR_PD (1 << 13)
|
|
#define TME_STP2024_DMA_CSR_PMIE (1 << 12)
|
|
#define TME_STP2024_DMA_CSR_CM (1 << 11)
|
|
#define TME_STP2024_DMA_CSR_CD (1 << 10)
|
|
#define TME_STP2024_DMA_CSR_CMI (1 << 9)
|
|
#define TME_STP2024_DMA_CSR_CMIE (1 << 8)
|
|
#define TME_STP2024_DMA_CSR_PPAUSE (1 << 7)
|
|
#define TME_STP2024_DMA_CSR_CPAUSE (1 << 6)
|
|
#define TME_STP2024_DMA_CSR_PDN (1 << 5)
|
|
#define TME_STP2024_DMA_CSR_LOOPBACK (1 << 4)
|
|
#define TME_STP2024_DMA_CSR_PGO (1 << 3)
|
|
#define TME_STP2024_DMA_CSR_CGO (1 << 2)
|
|
/* bit 1 unused */
|
|
#define TME_STP2024_DMA_CSR_RESET (1 << 0)
|
|
|
|
/* the DMA CSR bits for the two pipelines: */
|
|
#define TME_STP2024_DMA_XCSR(pipeline) \
|
|
((pipeline) \
|
|
? (TME_STP2024_DMA_CSR_PI \
|
|
| TME_STP2024_DMA_CSR_PIE \
|
|
| TME_STP2024_DMA_CSR_PMI \
|
|
| TME_STP2024_DMA_CSR_PM \
|
|
| TME_STP2024_DMA_CSR_PD \
|
|
| TME_STP2024_DMA_CSR_PMIE \
|
|
| TME_STP2024_DMA_CSR_PPAUSE \
|
|
| TME_STP2024_DMA_CSR_PGO) \
|
|
: (TME_STP2024_DMA_CSR_CI \
|
|
| TME_STP2024_DMA_CSR_CIE \
|
|
| TME_STP2024_DMA_CSR_CMI \
|
|
| TME_STP2024_DMA_CSR_CM \
|
|
| TME_STP2024_DMA_CSR_CD \
|
|
| TME_STP2024_DMA_CSR_CMIE \
|
|
| TME_STP2024_DMA_CSR_CPAUSE \
|
|
| TME_STP2024_DMA_CSR_CGO))
|
|
|
|
/* connection types: */
|
|
#define TME_STP2024_CONN_NULL (0)
|
|
#define TME_STP2024_CONN_SBUS (1)
|
|
#define TME_STP2024_CONN_CODEC (2)
|
|
#define TME_STP2024_CONN_COUNT (3)
|
|
|
|
/* the codec divides its bus address space in half. the first
|
|
is for playback, the second is record: */
|
|
#define TME_STP2024_CODEC_ADDRESS_MASK ((0 - (tme_bus_addr_t) 1) >> 1)
|
|
|
|
#define TME_STP2024_BUS_TRANSITION (1)
|
|
|
|
/* structures: */
|
|
|
|
/* the device: */
|
|
struct tme_stp2024 {
|
|
|
|
/* backpointer to our element: */
|
|
struct tme_element *tme_stp2024_element;
|
|
|
|
/* the mutex protecting the device: */
|
|
tme_mutex_t tme_stp2024_mutex;
|
|
|
|
/* the bus connections: */
|
|
struct tme_bus_connection *tme_stp2024_conns[TME_STP2024_CONN_COUNT];
|
|
|
|
/* the fan control register: */
|
|
tme_uint8_t tme_stp2024_power_fanctl;
|
|
|
|
/* the DMA registers: */
|
|
tme_uint32_t tme_stp2024_dma_csr;
|
|
tme_uint32_t tme_stp2024_dma_xcsr[2];
|
|
tme_uint32_t tme_stp2024_dma_xva[2];
|
|
tme_uint32_t tme_stp2024_dma_xc[2];
|
|
tme_uint32_t tme_stp2024_dma_xnva[2];
|
|
tme_uint32_t tme_stp2024_dma_xnc[2];
|
|
tme_bus_addr_t tme_stp2024_dma_codec_xaddress[2];
|
|
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
|
|
/* the codec register TLB token: */
|
|
struct tme_token tme_stp2024_codec_cycle_tlb_token;
|
|
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
};
|
|
|
|
/* an stp2024 internal bus connection: */
|
|
struct tme_stp2024_connection {
|
|
|
|
/* the external bus connection: */
|
|
struct tme_bus_connection tme_stp2024_connection;
|
|
|
|
/* this is nonzero if a TME_CONNECTION_BUS_GENERIC chip connection
|
|
is for the registers: */
|
|
unsigned int tme_stp2024_connection_which;
|
|
};
|
|
|
|
/* globals: */
|
|
|
|
/* the audio bus subregion: */
|
|
static const struct tme_bus_subregion _tme_stp2024_subregion_audio = {
|
|
|
|
/* the first and last addresses, starting from zero, of this
|
|
subregion: */
|
|
TME_STP2024_OFFSET_CODEC_REG0,
|
|
TME_STP2024_OFFSET_DMA_PNC + sizeof(tme_uint32_t) - 1,
|
|
|
|
/* there are no other subregions for this bus connection: */
|
|
(const struct tme_bus_subregion *) NULL
|
|
};
|
|
|
|
/* the power-management bus subregion: */
|
|
static const struct tme_bus_subregion _tme_stp2024_subregion_power = {
|
|
|
|
/* the first and last addresses, starting from zero, of this
|
|
subregion: */
|
|
TME_STP2024_OFFSET_POWER_IDLE,
|
|
TME_STP2024_OFFSET_POWER_BPORT,
|
|
|
|
/* the next subregion for this bus connection: */
|
|
&_tme_stp2024_subregion_audio
|
|
};
|
|
|
|
#define _tme_stp2024_subregion_first _tme_stp2024_subregion_power
|
|
|
|
/* this advances a DMA pipeline: */
|
|
static int
|
|
_tme_stp2024_dma_advance(struct tme_stp2024 *stp2024,
|
|
tme_bus_addr_t codec_xaddress,
|
|
tme_uint32_t xcsr_empty)
|
|
{
|
|
int pipeline;
|
|
tme_uint32_t advance;
|
|
|
|
/* get the pipeline: */
|
|
pipeline = (codec_xaddress > TME_STP2024_CODEC_ADDRESS_MASK);
|
|
codec_xaddress &= TME_STP2024_CODEC_ADDRESS_MASK;
|
|
|
|
/* see how much the codec has advanced in this pipeline: */
|
|
advance
|
|
= (((tme_uint32_t)
|
|
(codec_xaddress
|
|
- stp2024->tme_stp2024_dma_codec_xaddress[pipeline]))
|
|
& (tme_uint32_t) TME_STP2024_CODEC_ADDRESS_MASK);
|
|
|
|
/* the codec can't advance more than the count of the current
|
|
buffer; it's supposed to advance to that boundary before going
|
|
beyond it to the next buffer: */
|
|
assert (advance <= stp2024->tme_stp2024_dma_xc[pipeline]);
|
|
|
|
/* advance the pipeline: */
|
|
stp2024->tme_stp2024_dma_xva[pipeline] += advance;
|
|
stp2024->tme_stp2024_dma_xc[pipeline] -= advance;
|
|
|
|
/* while the count is zero: */
|
|
for (; stp2024->tme_stp2024_dma_xc[pipeline] == 0; ) {
|
|
|
|
/* if the next buffer is dirty: */
|
|
if (stp2024->tme_stp2024_dma_xcsr[pipeline]
|
|
& (TME_STP2024_DMA_CSR_PD
|
|
| TME_STP2024_DMA_CSR_CD)) {
|
|
|
|
/* possibly mark this pipeline as empty: */
|
|
stp2024->tme_stp2024_dma_xcsr[pipeline] |= xcsr_empty;
|
|
break;
|
|
}
|
|
|
|
/* advance to the next buffer: */
|
|
stp2024->tme_stp2024_dma_xva[pipeline]
|
|
= stp2024->tme_stp2024_dma_xnva[pipeline];
|
|
stp2024->tme_stp2024_dma_xc[pipeline]
|
|
= stp2024->tme_stp2024_dma_xnc[pipeline];
|
|
|
|
/* the next buffer is dirty: */
|
|
stp2024->tme_stp2024_dma_xcsr[pipeline]
|
|
|= (TME_STP2024_DMA_CSR_PD
|
|
| TME_STP2024_DMA_CSR_CD);
|
|
}
|
|
|
|
return (pipeline);
|
|
}
|
|
|
|
/* this enters: */
|
|
static struct tme_stp2024 *
|
|
_tme_stp2024_enter(struct tme_stp2024 *stp2024)
|
|
{
|
|
|
|
/* lock the mutex: */
|
|
tme_mutex_lock(&stp2024->tme_stp2024_mutex);
|
|
|
|
return (stp2024);
|
|
}
|
|
#define _tme_stp2024_enter_bus(conn_bus) \
|
|
((struct tme_stp2024 *) _tme_stp2024_enter((struct tme_stp2024 *) (conn_bus)->tme_bus_connection.tme_connection_element->tme_element_private))
|
|
|
|
/* this leaves: */
|
|
#define _tme_stp2024_leave(stp2024) tme_mutex_unlock(&(stp2024)->tme_stp2024_mutex)
|
|
|
|
/* this busies a generic bus connection: */
|
|
#define _tme_stp2024_busy_bus(stp2024, conn_index) \
|
|
((stp2024)->tme_stp2024_conns[conn_index])
|
|
|
|
/* this unbusies a generic bus connection: */
|
|
#define _tme_stp2024_unbusy_bus(stp2024, conn_bus) \
|
|
do { } while (/* CONSTCOND */ 0 && (stp2024)->tme_stp2024_element && (conn_bus)->tme_bus_connection.tme_connection_element)
|
|
|
|
/* the stp2024 SBus cycle handler: */
|
|
static void
|
|
_tme_stp2024_cycle_sbus(struct tme_bus_connection *master_conn_bus,
|
|
struct tme_bus_cycle *master_cycle,
|
|
tme_uint32_t *_master_fast_cycle_types,
|
|
struct tme_completion *master_completion)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
tme_bus_addr32_t reg;
|
|
struct tme_bus_connection *codec_conn_bus;
|
|
struct tme_bus_connection *codec_conn_bus_other;
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
struct tme_bus_tlb tlb_local;
|
|
int rc;
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
tme_uint32_t dma_csr;
|
|
int pipeline;
|
|
tme_uint32_t value32;
|
|
tme_uint8_t value8;
|
|
|
|
/* enter: */
|
|
stp2024 = _tme_stp2024_enter_bus(master_conn_bus);
|
|
|
|
/* get the register: */
|
|
reg = master_cycle->tme_bus_cycle_address;
|
|
|
|
/* if this is an eight-bit cycle to the codec: */
|
|
if (master_cycle->tme_bus_cycle_size == sizeof(tme_uint8_t)
|
|
&& (reg >= TME_STP2024_OFFSET_CODEC_REG0
|
|
&& reg <= TME_STP2024_OFFSET_CODEC_REG3)) {
|
|
|
|
/* convert the cycle address for the codec: */
|
|
master_cycle->tme_bus_cycle_address
|
|
= ((reg - TME_STP2024_OFFSET_CODEC_REG0)
|
|
/ sizeof(tme_uint32_t));
|
|
|
|
/* busy the codec connection: */
|
|
codec_conn_bus = _tme_stp2024_busy_bus(stp2024, TME_STP2024_CONN_CODEC);
|
|
codec_conn_bus_other = (struct tme_bus_connection *) codec_conn_bus->tme_bus_connection.tme_connection_other;
|
|
|
|
/* leave: */
|
|
_tme_stp2024_leave(stp2024);
|
|
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
|
|
/* fill a TLB entry for this address: */
|
|
tlb_local.tme_bus_tlb_token = &stp2024->tme_stp2024_codec_cycle_tlb_token;
|
|
rc = ((*codec_conn_bus_other->tme_bus_tlb_fill)
|
|
(codec_conn_bus_other,
|
|
&tlb_local,
|
|
master_cycle->tme_bus_cycle_address,
|
|
master_cycle->tme_bus_cycle_type));
|
|
assert (rc == TME_OK);
|
|
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
|
|
/* pass this cycle to the codec: */
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
rc = ((*tlb_local.tme_bus_tlb_cycle)
|
|
(tlb_local.tme_bus_tlb_cycle_private,
|
|
master_cycle));
|
|
master_completion->tme_completion_error = rc;
|
|
tme_completion_validate(master_completion);
|
|
#else /* !TME_STP2024_BUS_TRANSITION */
|
|
#error WRITEME
|
|
#endif /* !TME_STP2024_BUS_TRANSITION */
|
|
|
|
/* reenter: */
|
|
_tme_stp2024_enter(stp2024);
|
|
}
|
|
|
|
/* otherwise, this is not a codec cycle: */
|
|
else {
|
|
|
|
/* if this is an aligned 32-bit access: */
|
|
if (master_cycle->tme_bus_cycle_size == sizeof(tme_uint32_t)
|
|
&& (reg % sizeof(tme_uint32_t)) == 0) {
|
|
|
|
/* make the DMA CSR value: */
|
|
dma_csr
|
|
= ((stp2024->tme_stp2024_dma_csr
|
|
& (TME_STP2024_DMA_XCSR(0)
|
|
| TME_STP2024_DMA_XCSR(1)))
|
|
| (stp2024->tme_stp2024_dma_xcsr[0]
|
|
& TME_STP2024_DMA_XCSR(0))
|
|
| (stp2024->tme_stp2024_dma_xcsr[1]
|
|
& TME_STP2024_DMA_XCSR(1)));
|
|
|
|
/* assume that this is a pipeline-specific register: */
|
|
pipeline = (reg >= TME_STP2024_OFFSET_DMA_PVA);
|
|
|
|
/* if this is a write: */
|
|
if (master_cycle->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
|
|
|
|
/* run the bus cycle: */
|
|
tme_bus_cycle_xfer_reg(master_cycle,
|
|
&value32,
|
|
TME_BUS32_LOG2);
|
|
|
|
/* dispatch on the register: */
|
|
switch (reg) {
|
|
default: abort();
|
|
|
|
case TME_STP2024_OFFSET_DMA_CSR:
|
|
dma_csr
|
|
= ((dma_csr
|
|
& (TME_STP2024_DMA_CSR_ID
|
|
| TME_STP2024_DMA_CSR_IP
|
|
| TME_STP2024_DMA_CSR_PM
|
|
| TME_STP2024_DMA_CSR_PD
|
|
| TME_STP2024_DMA_CSR_CM
|
|
| TME_STP2024_DMA_CSR_CD))
|
|
| ((dma_csr
|
|
& ~value32)
|
|
& (TME_STP2024_DMA_CSR_PI
|
|
| TME_STP2024_DMA_CSR_CI
|
|
| TME_STP2024_DMA_CSR_EI
|
|
| TME_STP2024_DMA_CSR_PMI
|
|
| TME_STP2024_DMA_CSR_CMI))
|
|
| (value32
|
|
& (TME_STP2024_DMA_CSR_IE
|
|
| TME_STP2024_DMA_CSR_PIE
|
|
| TME_STP2024_DMA_CSR_CIE
|
|
| TME_STP2024_DMA_CSR_EIE
|
|
| TME_STP2024_DMA_CSR_PMIE
|
|
| TME_STP2024_DMA_CSR_CMIE
|
|
| TME_STP2024_DMA_CSR_PPAUSE
|
|
| TME_STP2024_DMA_CSR_CPAUSE
|
|
| TME_STP2024_DMA_CSR_PDN
|
|
| TME_STP2024_DMA_CSR_LOOPBACK
|
|
| TME_STP2024_DMA_CSR_PGO
|
|
| TME_STP2024_DMA_CSR_CGO
|
|
| TME_STP2024_DMA_CSR_RESET)));
|
|
stp2024->tme_stp2024_dma_csr = dma_csr;
|
|
stp2024->tme_stp2024_dma_xcsr[0] = (dma_csr & TME_STP2024_DMA_XCSR(0));
|
|
stp2024->tme_stp2024_dma_xcsr[1] = (dma_csr & TME_STP2024_DMA_XCSR(1));
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CVA:
|
|
case TME_STP2024_OFFSET_DMA_PVA:
|
|
stp2024->tme_stp2024_dma_xva[pipeline] = value32;
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CC:
|
|
case TME_STP2024_OFFSET_DMA_PC:
|
|
stp2024->tme_stp2024_dma_xc[pipeline] = value32;
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CNVA:
|
|
case TME_STP2024_OFFSET_DMA_PNVA:
|
|
stp2024->tme_stp2024_dma_xnva[pipeline] = value32;
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CNC:
|
|
case TME_STP2024_OFFSET_DMA_PNC:
|
|
stp2024->tme_stp2024_dma_xnc[pipeline] = value32;
|
|
stp2024->tme_stp2024_dma_xcsr[pipeline]
|
|
&= ~(TME_STP2024_DMA_CSR_PD
|
|
| TME_STP2024_DMA_CSR_CD);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* otherwise, this must be a read: */
|
|
else {
|
|
assert (master_cycle->tme_bus_cycle_type == TME_BUS_CYCLE_READ);
|
|
|
|
/* dispatch on the register: */
|
|
switch (reg) {
|
|
default: abort();
|
|
|
|
case TME_STP2024_OFFSET_DMA_CSR:
|
|
value32 = dma_csr;
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CVA:
|
|
case TME_STP2024_OFFSET_DMA_PVA:
|
|
value32 = stp2024->tme_stp2024_dma_xva[pipeline];
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CC:
|
|
case TME_STP2024_OFFSET_DMA_PC:
|
|
value32 = stp2024->tme_stp2024_dma_xc[pipeline];
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CNVA:
|
|
case TME_STP2024_OFFSET_DMA_PNVA:
|
|
value32 = stp2024->tme_stp2024_dma_xnva[pipeline];
|
|
break;
|
|
|
|
case TME_STP2024_OFFSET_DMA_CNC:
|
|
case TME_STP2024_OFFSET_DMA_PNC:
|
|
value32 = stp2024->tme_stp2024_dma_xnc[pipeline];
|
|
break;
|
|
}
|
|
|
|
/* run the bus cycle: */
|
|
tme_bus_cycle_xfer_reg(master_cycle,
|
|
&value32,
|
|
TME_BUS32_LOG2);
|
|
}
|
|
}
|
|
|
|
/* otherwise, if this is an eight-bit access: */
|
|
else if (master_cycle->tme_bus_cycle_size == sizeof(tme_uint8_t)) {
|
|
|
|
/* if this is a write: */
|
|
if (master_cycle->tme_bus_cycle_type == TME_BUS_CYCLE_WRITE) {
|
|
|
|
/* XXX WRITEME */
|
|
abort();
|
|
}
|
|
|
|
/* otherwise, this must be a read: */
|
|
else {
|
|
assert (master_cycle->tme_bus_cycle_type == TME_BUS_CYCLE_READ);
|
|
|
|
/* dispatch on the register: */
|
|
switch (reg) {
|
|
default: abort();
|
|
|
|
case TME_STP2024_OFFSET_POWER_FANCTL:
|
|
value8 = stp2024->tme_stp2024_power_fanctl;
|
|
break;
|
|
}
|
|
|
|
/* run the bus cycle: */
|
|
tme_bus_cycle_xfer_reg(master_cycle,
|
|
&value8,
|
|
TME_BUS8_LOG2);
|
|
}
|
|
}
|
|
|
|
/* complete the cycle: */
|
|
master_completion->tme_completion_error = TME_OK;
|
|
tme_memory_barrier(master_completion, sizeof(*master_completion), TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE);
|
|
tme_completion_validate(master_completion);
|
|
*_master_fast_cycle_types = 0;
|
|
}
|
|
|
|
/* leave: */
|
|
_tme_stp2024_leave(stp2024);
|
|
}
|
|
|
|
/* the stp2024 SBus TLB filler: */
|
|
static void
|
|
_tme_stp2024_tlb_fill_sbus(struct tme_bus_connection *master_conn_bus,
|
|
struct tme_bus_tlb *tlb,
|
|
tme_bus_addr_t address_wider,
|
|
unsigned int cycles)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
tme_bus_addr32_t address;
|
|
const struct tme_bus_subregion *subregion;
|
|
|
|
/* recover our data structures: */
|
|
stp2024 = master_conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
|
|
|
|
/* get our address: */
|
|
address = address_wider;
|
|
|
|
/* initialize the TLB entry: */
|
|
tme_bus_tlb_initialize(tlb);
|
|
|
|
/* find the subregion: */
|
|
subregion = &_tme_stp2024_subregion_first;
|
|
do {
|
|
if (address >= subregion->tme_bus_subregion_address_first
|
|
&& address <= subregion->tme_bus_subregion_address_last) {
|
|
break;
|
|
}
|
|
subregion = subregion->tme_bus_subregion_next;
|
|
} while (subregion != NULL);
|
|
assert (subregion != NULL);
|
|
|
|
/* this TLB entry covers only this subregion: */
|
|
tlb->tme_bus_tlb_addr_first = subregion->tme_bus_subregion_address_first;
|
|
tlb->tme_bus_tlb_addr_last = subregion->tme_bus_subregion_address_last;
|
|
}
|
|
|
|
/* the stp2024 codec cycle handler: */
|
|
static void
|
|
_tme_stp2024_cycle_codec(struct tme_bus_connection *codec_conn_bus,
|
|
struct tme_bus_cycle *codec_cycle,
|
|
tme_uint32_t *_codec_fast_cycle_types,
|
|
struct tme_completion *codec_completion)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
int pipeline;
|
|
|
|
/* enter: */
|
|
stp2024 = _tme_stp2024_enter_bus(codec_conn_bus);
|
|
|
|
/* advance the capture or playback pipeline: */
|
|
pipeline
|
|
= _tme_stp2024_dma_advance(stp2024,
|
|
codec_cycle->tme_bus_cycle_address,
|
|
(codec_cycle->tme_bus_cycle_size > 0
|
|
? (TME_STP2024_DMA_CSR_PM
|
|
| TME_STP2024_DMA_CSR_CM)
|
|
: 0));
|
|
|
|
/* if this is an empty bus cycle: */
|
|
if (codec_cycle->tme_bus_cycle_size == 0) {
|
|
|
|
/* complete with success: */
|
|
codec_completion->tme_completion_error = TME_OK;
|
|
tme_memory_barrier(codec_completion, sizeof(*codec_completion), TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE);
|
|
tme_completion_validate(codec_completion);
|
|
}
|
|
|
|
/* otherwise, this bus cycle is not empty: */
|
|
|
|
/* if the pipeline is empty or paused: */
|
|
else if (stp2024->tme_stp2024_dma_xcsr[pipeline]
|
|
& (TME_STP2024_DMA_CSR_PM
|
|
| TME_STP2024_DMA_CSR_CM
|
|
| TME_STP2024_DMA_CSR_PPAUSE
|
|
| TME_STP2024_DMA_CSR_CPAUSE)) {
|
|
|
|
/* complete with EAGAIN: */
|
|
codec_completion->tme_completion_error = EAGAIN;
|
|
tme_memory_barrier(codec_completion, sizeof(*codec_completion), TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE);
|
|
tme_completion_validate(codec_completion);
|
|
*_codec_fast_cycle_types = 0;
|
|
}
|
|
|
|
/* otherwise, the pipeline is not empty: */
|
|
else {
|
|
|
|
/* convert the cycle address into an SBus address: */
|
|
codec_cycle->tme_bus_cycle_address = stp2024->tme_stp2024_dma_xva[pipeline];
|
|
|
|
/* XXX WRITEME: */
|
|
abort();
|
|
}
|
|
|
|
/* leave: */
|
|
_tme_stp2024_leave(stp2024);
|
|
}
|
|
|
|
/* the stp2024 TLB filler for the codec: */
|
|
static void
|
|
_tme_stp2024_tlb_fill_codec(struct tme_bus_connection *codec_conn_bus,
|
|
struct tme_bus_tlb *tlb,
|
|
tme_bus_addr_t codec_xaddress,
|
|
unsigned int cycles)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
struct tme_bus_tlb tlb_mapping;
|
|
int pipeline;
|
|
tme_uint32_t lookahead;
|
|
tme_uint32_t count;
|
|
tme_bus_addr_t slave_address;
|
|
struct tme_bus_connection *slave_conn_bus;
|
|
struct tme_bus_connection *slave_conn_bus_other;
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
int rc;
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
|
|
/* enter: */
|
|
stp2024 = _tme_stp2024_enter_bus(codec_conn_bus);
|
|
|
|
/* save the raw codec address in the mapping structure: */
|
|
tlb_mapping.tme_bus_tlb_addr_first = codec_xaddress;
|
|
|
|
/* get the pipeline: */
|
|
pipeline = (codec_xaddress > TME_STP2024_CODEC_ADDRESS_MASK);
|
|
codec_xaddress &= TME_STP2024_CODEC_ADDRESS_MASK;
|
|
|
|
/* lock our mutex: */
|
|
tme_mutex_lock(&stp2024->tme_stp2024_mutex);
|
|
|
|
/* see how much the codec wants to look ahead in the pipeline: */
|
|
lookahead
|
|
= (((tme_uint32_t)
|
|
(codec_xaddress
|
|
- stp2024->tme_stp2024_dma_codec_xaddress[pipeline]))
|
|
& (tme_uint32_t) TME_STP2024_CODEC_ADDRESS_MASK);
|
|
|
|
/* if the pipeline is not empty and not paused: */
|
|
if ((stp2024->tme_stp2024_dma_xcsr[pipeline]
|
|
& (TME_STP2024_DMA_CSR_PM
|
|
| TME_STP2024_DMA_CSR_CM
|
|
| TME_STP2024_DMA_CSR_PPAUSE
|
|
| TME_STP2024_DMA_CSR_CPAUSE)) == 0) {
|
|
|
|
/* assume that the codec wants to look ahead in the first buffer: */
|
|
count = stp2024->tme_stp2024_dma_xc[pipeline];
|
|
slave_address = stp2024->tme_stp2024_dma_xva[pipeline];
|
|
|
|
/* if the codec wants to look ahead past the first buffer: */
|
|
if (lookahead >= count) {
|
|
|
|
/* assume that the codec wants to look ahead in the second buffer: */
|
|
lookahead -= count;
|
|
count = stp2024->tme_stp2024_dma_xnc[pipeline];
|
|
slave_address = stp2024->tme_stp2024_dma_xnva[pipeline];
|
|
|
|
/* if the second buffer is dirty, or if the codec wants to look
|
|
ahead past the second buffer: */
|
|
if ((stp2024->tme_stp2024_dma_xcsr[pipeline]
|
|
& (TME_STP2024_DMA_CSR_PD
|
|
| TME_STP2024_DMA_CSR_CD))
|
|
|| lookahead >= count) {
|
|
|
|
/* we can't let the codec look ahead: */
|
|
count = 0;
|
|
}
|
|
}
|
|
|
|
/* if we can let the codec look ahead: */
|
|
if (count > 0) {
|
|
|
|
/* if we have an SBus connection: */
|
|
slave_conn_bus = _tme_stp2024_busy_bus(stp2024, TME_STP2024_CONN_SBUS);
|
|
if (slave_conn_bus != NULL) {
|
|
|
|
/* leave: */
|
|
_tme_stp2024_leave(stp2024);
|
|
|
|
/* advance in the buffer to the lookahead point: */
|
|
slave_address += lookahead;
|
|
count -= lookahead;
|
|
|
|
/* fill this TLB entry: */
|
|
slave_conn_bus_other = (struct tme_bus_connection *) slave_conn_bus->tme_bus_connection.tme_connection_other;
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
rc =
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
(*slave_conn_bus_other->tme_bus_tlb_fill)
|
|
(slave_conn_bus_other,
|
|
tlb,
|
|
slave_address,
|
|
cycles);
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
assert (rc == TME_OK);
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
|
|
/* reenter: */
|
|
_tme_stp2024_enter(stp2024);
|
|
|
|
/* unbusy the SBus connection: */
|
|
_tme_stp2024_unbusy_bus(stp2024, slave_conn_bus);
|
|
}
|
|
|
|
/* map the filled TLB entry: */
|
|
tlb_mapping.tme_bus_tlb_addr_last
|
|
= (tlb_mapping.tme_bus_tlb_addr_first
|
|
+ (count - 1));
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
tlb_mapping.tme_bus_tlb_cycles_ok = (TME_BUS_CYCLE_READ | TME_BUS_CYCLE_WRITE);
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
tme_bus_tlb_map(tlb, slave_address, &tlb_mapping, tlb_mapping.tme_bus_tlb_addr_first);
|
|
|
|
/* leave: */
|
|
_tme_stp2024_leave(stp2024);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* initialize the TLB entry: */
|
|
tme_bus_tlb_initialize(tlb);
|
|
|
|
/* this TLB entry covers only the raw codec address: */
|
|
tlb->tme_bus_tlb_addr_first = tlb_mapping.tme_bus_tlb_addr_first;
|
|
tlb->tme_bus_tlb_addr_last = tlb_mapping.tme_bus_tlb_addr_first;
|
|
|
|
/* leave: */
|
|
_tme_stp2024_leave(stp2024);
|
|
}
|
|
|
|
/* the stp2024 TLB adder for the codec DMA: */
|
|
static void
|
|
_tme_stp2024_tlb_set_add(struct tme_bus_connection *codec_conn_bus,
|
|
struct tme_bus_tlb_set_info *tlb_set_info,
|
|
struct tme_completion *codec_completion)
|
|
{
|
|
|
|
/* if this TLB set provides a bus context register: */
|
|
if (tlb_set_info->tme_bus_tlb_set_info_bus_context != NULL) {
|
|
|
|
/* this bus only has one context: */
|
|
*tlb_set_info->tme_bus_tlb_set_info_bus_context = 0;
|
|
tlb_set_info->tme_bus_tlb_set_info_bus_context_max = 0;
|
|
}
|
|
|
|
/* XXX WRITEME - this should call through to the SBus: */
|
|
codec_completion->tme_completion_error = TME_OK;
|
|
tme_memory_barrier(0, 0, TME_MEMORY_BARRIER_WRITE_BEFORE_WRITE);
|
|
tme_completion_validate(codec_completion);
|
|
|
|
/* unused: */
|
|
codec_conn_bus = 0;
|
|
}
|
|
|
|
#if TME_STP2024_BUS_TRANSITION
|
|
|
|
/* this is the bus cycle transition glue: */
|
|
static int
|
|
_tme_stp2024_cycle_transition(void *_master_conn_bus,
|
|
struct tme_bus_cycle *master_cycle)
|
|
{
|
|
struct tme_completion completion_buffer;
|
|
struct tme_stp2024 *stp2024;
|
|
struct tme_bus_connection *master_conn_bus;
|
|
tme_uint32_t master_fast_cycle_types;
|
|
|
|
tme_completion_init(&completion_buffer);
|
|
|
|
master_conn_bus = (struct tme_bus_connection *) _master_conn_bus;
|
|
stp2024 = (struct tme_stp2024 *) master_conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
|
|
(master_conn_bus == stp2024->tme_stp2024_conns[TME_STP2024_CONN_SBUS]
|
|
? _tme_stp2024_cycle_sbus
|
|
: _tme_stp2024_cycle_codec)
|
|
(master_conn_bus,
|
|
master_cycle,
|
|
&master_fast_cycle_types,
|
|
&completion_buffer);
|
|
return (completion_buffer.tme_completion_error);
|
|
}
|
|
|
|
/* this is the TLB fill transition glue: */
|
|
static int
|
|
_tme_stp2024_tlb_fill_transition(struct tme_bus_connection *agent_conn_bus,
|
|
struct tme_bus_tlb *tlb,
|
|
tme_bus_addr_t address_wider,
|
|
unsigned int cycle_type)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
|
|
stp2024 = (struct tme_stp2024 *) agent_conn_bus->tme_bus_connection.tme_connection_element->tme_element_private;
|
|
(agent_conn_bus == stp2024->tme_stp2024_conns[TME_STP2024_CONN_SBUS]
|
|
? _tme_stp2024_tlb_fill_sbus
|
|
: _tme_stp2024_tlb_fill_codec)
|
|
(agent_conn_bus,
|
|
tlb,
|
|
address_wider,
|
|
cycle_type);
|
|
|
|
/* we always handle any slow cycles: */
|
|
tlb->tme_bus_tlb_cycles_ok |= cycle_type;
|
|
tlb->tme_bus_tlb_addr_offset = 0;
|
|
tlb->tme_bus_tlb_addr_shift = 0;
|
|
tlb->tme_bus_tlb_cycle = _tme_stp2024_cycle_transition;
|
|
tlb->tme_bus_tlb_cycle_private = agent_conn_bus;
|
|
assert (tlb->tme_bus_tlb_fault_handler_count == 0);
|
|
|
|
return (TME_OK);
|
|
}
|
|
#define _tme_stp2024_tlb_fill_sbus _tme_stp2024_tlb_fill_transition
|
|
#define _tme_stp2024_tlb_fill_codec _tme_stp2024_tlb_fill_transition
|
|
|
|
/* this is the bus TLB set add transition glue: */
|
|
static int
|
|
_tme_stp2024_tlb_set_add_transition(struct tme_bus_connection *codec_conn_bus,
|
|
struct tme_bus_tlb_set_info *tlb_set_info)
|
|
{
|
|
struct tme_completion completion_buffer;
|
|
tme_completion_init(&completion_buffer);
|
|
_tme_stp2024_tlb_set_add(codec_conn_bus,
|
|
tlb_set_info,
|
|
&completion_buffer);
|
|
return (completion_buffer.tme_completion_error);
|
|
}
|
|
#define _tme_stp2024_tlb_set_add _tme_stp2024_tlb_set_add_transition
|
|
|
|
#endif /* TME_STP2024_BUS_TRANSITION */
|
|
|
|
/* this scores a new connection: */
|
|
static int
|
|
_tme_stp2024_connection_score(struct tme_connection *conn,
|
|
unsigned int *_score)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
struct tme_stp2024_connection *conn_stp2024;
|
|
|
|
/* both sides must be generic bus connections: */
|
|
assert(conn->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
|
|
assert(conn->tme_connection_other->tme_connection_type
|
|
== conn->tme_connection_type);
|
|
|
|
/* recover our data structures: */
|
|
stp2024 = conn->tme_connection_element->tme_element_private;
|
|
conn_stp2024 = (struct tme_stp2024_connection *) conn;
|
|
|
|
/* this is a generic bus connection, so just score it nonzero and
|
|
return. note that there's no good way to differentiate a
|
|
connection to a bus from a connection to just another chip, so we
|
|
always return a nonzero score here: */
|
|
*_score = 1;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this makes a new connection: */
|
|
static int
|
|
_tme_stp2024_connection_make(struct tme_connection *conn,
|
|
unsigned int state)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
struct tme_stp2024_connection *conn_stp2024;
|
|
struct tme_bus_connection *conn_bus;
|
|
|
|
/* both sides must be generic bus connections: */
|
|
assert(conn->tme_connection_type == TME_CONNECTION_BUS_GENERIC);
|
|
assert(conn->tme_connection_other->tme_connection_type
|
|
== conn->tme_connection_type);
|
|
|
|
/* recover our data structures: */
|
|
stp2024 = conn->tme_connection_element->tme_element_private;
|
|
conn_stp2024 = (struct tme_stp2024_connection *) conn;
|
|
conn_bus = &conn_stp2024->tme_stp2024_connection;
|
|
|
|
/* we're always set up to answer calls across the connection, so we
|
|
only have to do work when the connection has gone full, namely
|
|
taking the other side of the connection: */
|
|
if (state == TME_CONNECTION_FULL) {
|
|
|
|
/* lock our mutex: */
|
|
tme_mutex_lock(&stp2024->tme_stp2024_mutex);
|
|
|
|
/* save our connection: */
|
|
switch (conn_stp2024->tme_stp2024_connection_which) {
|
|
default: assert(FALSE);
|
|
case TME_STP2024_CONN_SBUS:
|
|
case TME_STP2024_CONN_CODEC:
|
|
stp2024->tme_stp2024_conns[conn_stp2024->tme_stp2024_connection_which]
|
|
= (struct tme_bus_connection *) conn;
|
|
break;
|
|
}
|
|
|
|
/* unlock our mutex: */
|
|
tme_mutex_unlock(&stp2024->tme_stp2024_mutex);
|
|
}
|
|
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* this breaks a connection: */
|
|
static int
|
|
_tme_stp2024_connection_break(struct tme_connection *conn, unsigned int state)
|
|
{
|
|
abort();
|
|
}
|
|
|
|
/* this makes a new connection side for an stp2024: */
|
|
static int
|
|
_tme_stp2024_connections_new(struct tme_element *element,
|
|
const char * const *args,
|
|
struct tme_connection **_conns,
|
|
char **_output)
|
|
{
|
|
struct tme_stp2024 *stp2024;
|
|
struct tme_stp2024_connection *conn_stp2024;
|
|
struct tme_bus_connection *conn_bus;
|
|
struct tme_connection *conn;
|
|
unsigned int conn_which;
|
|
int usage;
|
|
int rc;
|
|
|
|
/* recover our data structure: */
|
|
stp2024 = (struct tme_stp2024 *) element->tme_element_private;
|
|
|
|
/* we don't bother locking the mutex simply to check if connections
|
|
already exist: */
|
|
|
|
/* check our arguments: */
|
|
usage = FALSE;
|
|
rc = 0;
|
|
conn_which = TME_STP2024_CONN_NULL;
|
|
|
|
/* if this is the SBus connection: */
|
|
if (args[1] == NULL) {
|
|
conn_which = TME_STP2024_CONN_SBUS;
|
|
}
|
|
|
|
/* otherwise, if this connection is for the codec: */
|
|
else if (TME_ARG_IS(args[1], "codec")) {
|
|
conn_which = TME_STP2024_CONN_CODEC;
|
|
}
|
|
|
|
/* otherwise, this is a bad argument: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s, ",
|
|
args[1],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
}
|
|
|
|
/* if this is for a connection that already exists: */
|
|
if (conn_which != TME_STP2024_CONN_NULL
|
|
&& stp2024->tme_stp2024_conns[conn_which] != NULL) {
|
|
rc = EEXIST;
|
|
}
|
|
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s [ codec ]",
|
|
_("usage:"),
|
|
args[0]);
|
|
rc = EINVAL;
|
|
}
|
|
|
|
if (rc) {
|
|
return (rc);
|
|
}
|
|
|
|
/* make a new connection: */
|
|
conn_stp2024 = tme_new0(struct tme_stp2024_connection, 1);
|
|
conn_bus = &conn_stp2024->tme_stp2024_connection;
|
|
conn = &conn_bus->tme_bus_connection;
|
|
|
|
/* fill in the generic connection: */
|
|
conn->tme_connection_next = *_conns;
|
|
conn->tme_connection_type = TME_CONNECTION_BUS_GENERIC;
|
|
conn->tme_connection_score = _tme_stp2024_connection_score;
|
|
conn->tme_connection_make = _tme_stp2024_connection_make;
|
|
conn->tme_connection_break = _tme_stp2024_connection_break;
|
|
|
|
/* fill in the generic bus connection: */
|
|
switch (conn_which) {
|
|
default: assert(FALSE);
|
|
case TME_STP2024_CONN_CODEC:
|
|
conn_bus->tme_bus_subregions.tme_bus_subregion_address_first = 0;
|
|
conn_bus->tme_bus_subregions.tme_bus_subregion_address_last = 0 - (tme_bus_addr_t) 1;
|
|
conn_bus->tme_bus_subregions.tme_bus_subregion_next = NULL;
|
|
conn_bus->tme_bus_signals_add = NULL;
|
|
conn_bus->tme_bus_tlb_set_add = _tme_stp2024_tlb_set_add;
|
|
conn_bus->tme_bus_tlb_fill = _tme_stp2024_tlb_fill_codec;
|
|
break;
|
|
case TME_STP2024_CONN_SBUS:
|
|
conn_bus->tme_bus_tlb_fill = _tme_stp2024_tlb_fill_sbus;
|
|
conn_bus->tme_bus_subregions = _tme_stp2024_subregion_first;
|
|
break;
|
|
}
|
|
|
|
/* fill in the internal information: */
|
|
conn_stp2024->tme_stp2024_connection_which = conn_which;
|
|
|
|
/* return the connection side possibility: */
|
|
*_conns = conn;
|
|
return (TME_OK);
|
|
}
|
|
|
|
/* the new stp2024 function: */
|
|
TME_ELEMENT_NEW_DECL(tme_ic_stp2024) {
|
|
int usage;
|
|
int arg_i;
|
|
tme_bus_addr_t power_fanctl;
|
|
struct tme_stp2024 *stp2024;
|
|
|
|
/* check our arguments: */
|
|
usage = FALSE;
|
|
arg_i = 1;
|
|
power_fanctl = TME_STP2024_POWER_FANCTL_ID + 1;
|
|
for (;;) {
|
|
|
|
if (TME_ARG_IS(args[arg_i], "id")) {
|
|
power_fanctl
|
|
= (tme_bus_addr_parse(args[arg_i + 1], TME_STP2024_POWER_FANCTL_ID + 1)
|
|
* _TME_FIELD_MASK_FACTOR(TME_STP2024_POWER_FANCTL_ID));
|
|
if (power_fanctl > TME_STP2024_POWER_FANCTL_ID) {
|
|
break;
|
|
}
|
|
arg_i += 2;
|
|
}
|
|
|
|
/* if we ran out of arguments: */
|
|
else if (args[arg_i] == NULL) {
|
|
|
|
break;
|
|
}
|
|
|
|
/* otherwise this is a bad argument: */
|
|
else {
|
|
tme_output_append_error(_output,
|
|
"%s %s, ",
|
|
args[arg_i],
|
|
_("unexpected"));
|
|
usage = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (power_fanctl > TME_STP2024_POWER_FANCTL_ID) {
|
|
usage = TRUE;
|
|
}
|
|
|
|
if (usage) {
|
|
tme_output_append_error(_output,
|
|
"%s %s id %s",
|
|
_("usage:"),
|
|
args[0],
|
|
_("ID"));
|
|
return (EINVAL);
|
|
}
|
|
|
|
/* start the stp2024 structure: */
|
|
stp2024 = tme_new0(struct tme_stp2024, 1);
|
|
tme_mutex_init(&stp2024->tme_stp2024_mutex);
|
|
stp2024->tme_stp2024_element = element;
|
|
stp2024->tme_stp2024_power_fanctl = power_fanctl;
|
|
|
|
/* fill the element: */
|
|
element->tme_element_private = stp2024;
|
|
element->tme_element_connections_new = _tme_stp2024_connections_new;
|
|
|
|
return (TME_OK);
|
|
}
|