regmap: Updates for v6.14

There's one big bit of work this time around, the addition of support for
 a greater range of MBQ access sizes to SoundWire devices together with
 support for deferred read/write.  The MBQ register maps generally have
 variable register sizes, the variable regiseter size support allows them
 to be handled much more naturally within regmap with less open coding in
 drivers.  The deferred read/write support avoids spurious errors when
 devices make use of a bus feature allowing them to indicate they're
 busy.  These changes pull in a supporting SoundWire change, and there's
 an ASoC change building off the new code.
 
 The remainder of the changes are code cleanups.
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmeOXxIACgkQJNaLcl1U
 h9Bktwf/ZG6r01FNIw7PTDFQoZ2YipaFffqyJZco7zj2kyK1SqPt9jRQuPD5K8CE
 gXwxhIDu/Z7XXhTBOQ42shtOAyhw6FQDXdfULYvVl0kZ6JIUtz/J7JCuh3v/UBMo
 PWvFCIutDsgte0ru7jdQCTxgGgc39sobEQQOyznlNP+If7+aRpWW6B95YqakTzCc
 zDy8aDE4YxpFy6Wgg+t4xuO1KGdltHBoNFgtUNuaQtfkQP38JlMo9Ma/BEVu8INK
 WlGYPjo3eo/oRCohN+MByq15qvbVheAFg0B9h3mNcTOGFGhvNLBkqc4DE85UBk6g
 JH/QY1LEqTqHKEpi3oRAgwUUgy3Tog==
 =6vTg
 -----END PGP SIGNATURE-----

Merge tag 'regmap-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "There's one big bit of work this time around, the addition of support
  for a greater range of MBQ access sizes to SoundWire devices together
  with support for deferred read/write.

  The MBQ register maps generally have variable register sizes, the
  variable regiseter size support allows them to be handled much more
  naturally within regmap with less open coding in drivers.

  The deferred read/write support avoids spurious errors when devices
  make use of a bus feature allowing them to indicate they're busy.
  These changes pull in a supporting SoundWire change, and there's an
  ASoC change building off the new code.

  The remainder of the changes are code cleanups"

* tag 'regmap-v6.14' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: sdw-mbq: Add support for SDCA deferred controls
  regmap: sdw-mbq: Add support for further MBQ register sizes
  ASoC: SDCA: Update list of entity_0 controls
  soundwire: SDCA: Add additional SDCA address macros
  regmap: regmap_multi_reg_read(): make register list const
  regmap: cache: rbtree: use krealloc_array() to replace krealloc()
  regmap: cache: mapple: use kmalloc_array() to replace kmalloc()
  regmap: place foo / 8 and foo % 8 closer to each other
  regmap: Use BITS_TO_BYTES()
  regmap: cache: Use BITS_TO_BYTES()
This commit is contained in:
Linus Torvalds 2025-01-22 08:57:52 -08:00
commit 6f10810ccd
8 changed files with 319 additions and 59 deletions

View File

@ -73,8 +73,7 @@ static int regcache_maple_write(struct regmap *map, unsigned int reg,
rcu_read_unlock();
entry = kmalloc((last - index + 1) * sizeof(unsigned long),
map->alloc_flags);
entry = kmalloc_array(last - index + 1, sizeof(*entry), map->alloc_flags);
if (!entry)
return -ENOMEM;
@ -204,7 +203,7 @@ static int regcache_maple_sync_block(struct regmap *map, unsigned long *entry,
* overheads.
*/
if (max - min > 1 && regmap_can_raw_write(map)) {
buf = kmalloc(val_bytes * (max - min), map->alloc_flags);
buf = kmalloc_array(max - min, val_bytes, map->alloc_flags);
if (!buf) {
ret = -ENOMEM;
goto out;
@ -320,7 +319,7 @@ static int regcache_maple_insert_block(struct regmap *map, int first,
unsigned long *entry;
int i, ret;
entry = kcalloc(last - first + 1, sizeof(unsigned long), map->alloc_flags);
entry = kmalloc_array(last - first + 1, sizeof(*entry), map->alloc_flags);
if (!entry)
return -ENOMEM;

View File

@ -275,18 +275,16 @@ static int regcache_rbtree_insert_to_block(struct regmap *map,
pos = (reg - base_reg) / map->reg_stride;
offset = (rbnode->base_reg - base_reg) / map->reg_stride;
blk = krealloc(rbnode->block,
blklen * map->cache_word_size,
map->alloc_flags);
blk = krealloc_array(rbnode->block, blklen, map->cache_word_size, map->alloc_flags);
if (!blk)
return -ENOMEM;
rbnode->block = blk;
if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) {
present = krealloc(rbnode->cache_present,
BITS_TO_LONGS(blklen) * sizeof(*present),
map->alloc_flags);
present = krealloc_array(rbnode->cache_present,
BITS_TO_LONGS(blklen), sizeof(*present),
map->alloc_flags);
if (!present)
return -ENOMEM;

View File

@ -154,7 +154,7 @@ int regcache_init(struct regmap *map, const struct regmap_config *config)
map->num_reg_defaults = config->num_reg_defaults;
map->num_reg_defaults_raw = config->num_reg_defaults_raw;
map->reg_defaults_raw = config->reg_defaults_raw;
map->cache_word_size = DIV_ROUND_UP(config->val_bits, 8);
map->cache_word_size = BITS_TO_BYTES(config->val_bits);
map->cache_size_raw = map->cache_word_size * config->num_reg_defaults_raw;
map->cache = NULL;

View File

@ -1,45 +1,187 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright(c) 2020 Intel Corporation.
#include <linux/bits.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <sound/sdca_function.h>
#include "internal.h"
struct regmap_mbq_context {
struct device *dev;
struct regmap_sdw_mbq_cfg cfg;
int val_size;
bool (*readable_reg)(struct device *dev, unsigned int reg);
};
static int regmap_sdw_mbq_size(struct regmap_mbq_context *ctx, unsigned int reg)
{
int size = ctx->val_size;
if (ctx->cfg.mbq_size) {
size = ctx->cfg.mbq_size(ctx->dev, reg);
if (!size || size > ctx->val_size)
return -EINVAL;
}
return size;
}
static bool regmap_sdw_mbq_deferrable(struct regmap_mbq_context *ctx, unsigned int reg)
{
if (ctx->cfg.deferrable)
return ctx->cfg.deferrable(ctx->dev, reg);
return false;
}
static int regmap_sdw_mbq_poll_busy(struct sdw_slave *slave, unsigned int reg,
struct regmap_mbq_context *ctx)
{
struct device *dev = &slave->dev;
int val, ret = 0;
dev_dbg(dev, "Deferring transaction for 0x%x\n", reg);
reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(reg), 0,
SDCA_CTL_ENTITY_0_FUNCTION_STATUS, 0);
if (ctx->readable_reg(dev, reg)) {
ret = read_poll_timeout(sdw_read_no_pm, val,
val < 0 || !(val & SDCA_CTL_ENTITY_0_FUNCTION_BUSY),
ctx->cfg.timeout_us, ctx->cfg.retry_us,
false, slave, reg);
if (val < 0)
return val;
if (ret)
dev_err(dev, "Function busy timed out 0x%x: %d\n", reg, val);
} else {
fsleep(ctx->cfg.timeout_us);
}
return ret;
}
static int regmap_sdw_mbq_write_impl(struct sdw_slave *slave,
unsigned int reg, unsigned int val,
int mbq_size, bool deferrable)
{
int shift = mbq_size * BITS_PER_BYTE;
int ret;
while (--mbq_size > 0) {
shift -= BITS_PER_BYTE;
ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg),
(val >> shift) & 0xff);
if (ret < 0)
return ret;
}
ret = sdw_write_no_pm(slave, reg, val & 0xff);
if (deferrable && ret == -ENODATA)
return -EAGAIN;
return ret;
}
static int regmap_sdw_mbq_write(void *context, unsigned int reg, unsigned int val)
{
struct device *dev = context;
struct regmap_mbq_context *ctx = context;
struct device *dev = ctx->dev;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
ret = sdw_write_no_pm(slave, SDW_SDCA_MBQ_CTL(reg), (val >> 8) & 0xff);
if (ret < 0)
return ret;
if (mbq_size < 0)
return mbq_size;
return sdw_write_no_pm(slave, reg, val & 0xff);
/*
* Technically the spec does allow a device to set itself to busy for
* internal reasons, but since it doesn't provide any information on
* how to handle timeouts in that case, for now the code will only
* process a single wait/timeout on function busy and a single retry
* of the transaction.
*/
ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, deferrable);
if (ret == -EAGAIN) {
ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
if (ret)
return ret;
ret = regmap_sdw_mbq_write_impl(slave, reg, val, mbq_size, false);
}
return ret;
}
static int regmap_sdw_mbq_read_impl(struct sdw_slave *slave,
unsigned int reg, unsigned int *val,
int mbq_size, bool deferrable)
{
int shift = BITS_PER_BYTE;
int read;
read = sdw_read_no_pm(slave, reg);
if (read < 0) {
if (deferrable && read == -ENODATA)
return -EAGAIN;
return read;
}
*val = read;
while (--mbq_size > 0) {
read = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
if (read < 0)
return read;
*val |= read << shift;
shift += BITS_PER_BYTE;
}
return 0;
}
static int regmap_sdw_mbq_read(void *context, unsigned int reg, unsigned int *val)
{
struct device *dev = context;
struct regmap_mbq_context *ctx = context;
struct device *dev = ctx->dev;
struct sdw_slave *slave = dev_to_sdw_dev(dev);
int read0;
int read1;
bool deferrable = regmap_sdw_mbq_deferrable(ctx, reg);
int mbq_size = regmap_sdw_mbq_size(ctx, reg);
int ret;
read0 = sdw_read_no_pm(slave, reg);
if (read0 < 0)
return read0;
if (mbq_size < 0)
return mbq_size;
read1 = sdw_read_no_pm(slave, SDW_SDCA_MBQ_CTL(reg));
if (read1 < 0)
return read1;
/*
* Technically the spec does allow a device to set itself to busy for
* internal reasons, but since it doesn't provide any information on
* how to handle timeouts in that case, for now the code will only
* process a single wait/timeout on function busy and a single retry
* of the transaction.
*/
ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, deferrable);
if (ret == -EAGAIN) {
ret = regmap_sdw_mbq_poll_busy(slave, reg, ctx);
if (ret)
return ret;
*val = (read1 << 8) | read0;
ret = regmap_sdw_mbq_read_impl(slave, reg, val, mbq_size, false);
}
return 0;
return ret;
}
static const struct regmap_bus regmap_sdw_mbq = {
@ -51,8 +193,7 @@ static const struct regmap_bus regmap_sdw_mbq = {
static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
{
/* MBQ-based controls are only 16-bits for now */
if (config->val_bits != 16)
if (config->val_bits > (sizeof(unsigned int) * BITS_PER_BYTE))
return -ENOTSUPP;
/* Registers are 32 bits wide */
@ -65,35 +206,69 @@ static int regmap_sdw_mbq_config_check(const struct regmap_config *config)
return 0;
}
static struct regmap_mbq_context *
regmap_sdw_mbq_gen_context(struct device *dev,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config)
{
struct regmap_mbq_context *ctx;
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return ERR_PTR(-ENOMEM);
ctx->dev = dev;
if (mbq_config)
ctx->cfg = *mbq_config;
ctx->val_size = config->val_bits / BITS_PER_BYTE;
ctx->readable_reg = config->readable_reg;
return ctx;
}
struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mbq_context *ctx;
int ret;
ret = regmap_sdw_mbq_config_check(config);
if (ret)
return ERR_PTR(ret);
return __regmap_init(&sdw->dev, &regmap_sdw_mbq,
&sdw->dev, config, lock_key, lock_name);
ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__regmap_init_sdw_mbq);
struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name)
{
struct regmap_mbq_context *ctx;
int ret;
ret = regmap_sdw_mbq_config_check(config);
if (ret)
return ERR_PTR(ret);
return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq,
&sdw->dev, config, lock_key, lock_name);
ctx = regmap_sdw_mbq_gen_context(&sdw->dev, config, mbq_config);
if (IS_ERR(ctx))
return ERR_CAST(ctx);
return __devm_regmap_init(&sdw->dev, &regmap_sdw_mbq, ctx,
config, lock_key, lock_name);
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_sdw_mbq);

View File

@ -769,14 +769,13 @@ struct regmap *__regmap_init(struct device *dev,
map->alloc_flags = GFP_KERNEL;
map->reg_base = config->reg_base;
map->reg_shift = config->pad_bits % 8;
map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8);
map->format.pad_bytes = config->pad_bits / 8;
map->format.reg_shift = config->reg_shift;
map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8);
map->format.buf_size = DIV_ROUND_UP(config->reg_bits +
config->val_bits + config->pad_bits, 8);
map->reg_shift = config->pad_bits % 8;
map->format.reg_bytes = BITS_TO_BYTES(config->reg_bits);
map->format.val_bytes = BITS_TO_BYTES(config->val_bits);
map->format.buf_size = BITS_TO_BYTES(config->reg_bits + config->val_bits + config->pad_bits);
if (config->reg_stride)
map->reg_stride = config->reg_stride;
else
@ -3116,7 +3115,7 @@ int regmap_fields_read(struct regmap_field *field, unsigned int id,
EXPORT_SYMBOL_GPL(regmap_fields_read);
static int _regmap_bulk_read(struct regmap *map, unsigned int reg,
unsigned int *regs, void *val, size_t val_count)
const unsigned int *regs, void *val, size_t val_count)
{
u32 *u32 = val;
u16 *u16 = val;
@ -3210,7 +3209,7 @@ EXPORT_SYMBOL_GPL(regmap_bulk_read);
* A value of zero will be returned on success, a negative errno will
* be returned in error cases.
*/
int regmap_multi_reg_read(struct regmap *map, unsigned int *regs, void *val,
int regmap_multi_reg_read(struct regmap *map, const unsigned int *regs, void *val,
size_t val_count)
{
if (val_count == 0)

View File

@ -506,6 +506,32 @@ struct regmap_range_cfg {
unsigned int window_len;
};
/**
* struct regmap_sdw_mbq_cfg - Configuration for Multi-Byte Quantities
*
* @mbq_size: Callback returning the actual size of the given register.
* @deferrable: Callback returning true if the hardware can defer
* transactions to the given register. Deferral should
* only be used by SDCA parts and typically which controls
* are deferrable will be specified in either as a hard
* coded list or from the DisCo tables in the platform
* firmware.
*
* @timeout_us: The time in microseconds after which waiting for a deferred
* transaction should time out.
* @retry_us: The time in microseconds between polls of the function busy
* status whilst waiting for an opportunity to retry a deferred
* transaction.
*
* Provides additional configuration required for SoundWire MBQ register maps.
*/
struct regmap_sdw_mbq_cfg {
int (*mbq_size)(struct device *dev, unsigned int reg);
bool (*deferrable)(struct device *dev, unsigned int reg);
unsigned long timeout_us;
unsigned long retry_us;
};
struct regmap_async;
typedef int (*regmap_hw_write)(void *context, const void *data,
@ -652,6 +678,7 @@ struct regmap *__regmap_init_sdw(struct sdw_slave *sdw,
const char *lock_name);
struct regmap *__regmap_init_sdw_mbq(struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__regmap_init_spi_avmm(struct spi_device *spi,
@ -713,6 +740,7 @@ struct regmap *__devm_regmap_init_sdw(struct sdw_slave *sdw,
const char *lock_name);
struct regmap *__devm_regmap_init_sdw_mbq(struct sdw_slave *sdw,
const struct regmap_config *config,
const struct regmap_sdw_mbq_cfg *mbq_config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__devm_regmap_init_slimbus(struct slim_device *slimbus,
@ -942,7 +970,22 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
*/
#define regmap_init_sdw_mbq(sdw, config) \
__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \
sdw, config)
sdw, config, NULL)
/**
* regmap_init_sdw_mbq_cfg() - Initialise MBQ SDW register map with config
*
* @sdw: Device that will be interacted with
* @config: Configuration for register map
* @mbq_config: Properties for the MBQ registers
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
#define regmap_init_sdw_mbq_cfg(sdw, config, mbq_config) \
__regmap_lockdep_wrapper(__regmap_init_sdw_mbq, #config, \
sdw, config, mbq_config)
/**
* regmap_init_spi_avmm() - Initialize register map for Intel SPI Slave
@ -1155,7 +1198,22 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
*/
#define devm_regmap_init_sdw_mbq(sdw, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, #config, \
sdw, config)
sdw, config, NULL)
/**
* devm_regmap_init_sdw_mbq_cfg() - Initialise managed MBQ SDW register map with config
*
* @sdw: Device that will be interacted with
* @config: Configuration for register map
* @mbq_config: Properties for the MBQ registers
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
#define devm_regmap_init_sdw_mbq_cfg(sdw, config, mbq_config) \
__regmap_lockdep_wrapper(__devm_regmap_init_sdw_mbq, \
#config, sdw, config, mbq_config)
/**
* devm_regmap_init_slimbus() - Initialise managed register map
@ -1244,7 +1302,7 @@ int regmap_noinc_read(struct regmap *map, unsigned int reg,
void *val, size_t val_len);
int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val,
size_t val_count);
int regmap_multi_reg_read(struct regmap *map, unsigned int *reg, void *val,
int regmap_multi_reg_read(struct regmap *map, const unsigned int *reg, void *val,
size_t val_count);
int regmap_update_bits_base(struct regmap *map, unsigned int reg,
unsigned int mask, unsigned int val,

View File

@ -4,6 +4,9 @@
#ifndef __SDW_REGISTERS_H
#define __SDW_REGISTERS_H
#include <linux/bitfield.h>
#include <linux/bits.h>
/*
* SDW registers as defined by MIPI 1.2 Spec
*/
@ -329,16 +332,27 @@
* 2:0 Control Number[2:0]
*/
#define SDW_SDCA_CTL(fun, ent, ctl, ch) (BIT(30) | \
(((fun) & 0x7) << 22) | \
(((ent) & 0x40) << 15) | \
(((ent) & 0x3f) << 7) | \
(((ctl) & 0x30) << 15) | \
(((ctl) & 0x0f) << 3) | \
(((ch) & 0x38) << 12) | \
((ch) & 0x07))
#define SDW_SDCA_CTL(fun, ent, ctl, ch) (BIT(30) | \
(((fun) & GENMASK(2, 0)) << 22) | \
(((ent) & BIT(6)) << 15) | \
(((ent) & GENMASK(5, 0)) << 7) | \
(((ctl) & GENMASK(5, 4)) << 15) | \
(((ctl) & GENMASK(3, 0)) << 3) | \
(((ch) & GENMASK(5, 3)) << 12) | \
((ch) & GENMASK(2, 0)))
#define SDW_SDCA_CTL_FUNC(reg) FIELD_GET(GENMASK(24, 22), (reg))
#define SDW_SDCA_CTL_ENT(reg) ((FIELD_GET(BIT(21), (reg)) << 6) | \
FIELD_GET(GENMASK(12, 7), (reg)))
#define SDW_SDCA_CTL_CSEL(reg) ((FIELD_GET(GENMASK(20, 19), (reg)) << 4) | \
FIELD_GET(GENMASK(6, 3), (reg)))
#define SDW_SDCA_CTL_CNUM(reg) ((FIELD_GET(GENMASK(17, 15), (reg)) << 3) | \
FIELD_GET(GENMASK(2, 0), (reg)))
#define SDW_SDCA_MBQ_CTL(reg) ((reg) | BIT(13))
#define SDW_SDCA_NEXT_CTL(reg) ((reg) | BIT(14))
/* Check the reserved and fixed bits in address */
#define SDW_SDCA_VALID_CTL(reg) (((reg) & (GENMASK(31, 25) | BIT(18) | BIT(13))) == BIT(30))
#endif /* __SDW_REGISTERS_H */

View File

@ -42,14 +42,31 @@ enum sdca_function_type {
#define SDCA_FUNCTION_TYPE_HID_NAME "HID"
enum sdca_entity0_controls {
SDCA_CONTROL_ENTITY_0_COMMIT_GROUP_MASK = 0x01,
SDCA_CONTROL_ENTITY_0_INTSTAT_CLEAR = 0x02,
SDCA_CONTROL_ENTITY_0_INT_ENABLE = 0x03,
SDCA_CONTROL_ENTITY_0_FUNCTION_SDCA_VERSION = 0x04,
SDCA_CONTROL_ENTITY_0_FUNCTION_TOPOLOGY = 0x05,
SDCA_CONTROL_ENTITY_0_FUNCTION_MANUFACTURER_ID = 0x06,
SDCA_CONTROL_ENTITY_0_FUNCTION_ID = 0x07,
SDCA_CONTROL_ENTITY_0_FUNCTION_VERSION = 0x08
SDCA_CTL_ENTITY_0_COMMIT_GROUP_MASK = 0x01,
SDCA_CTL_ENTITY_0_FUNCTION_SDCA_VERSION = 0x04,
SDCA_CTL_ENTITY_0_FUNCTION_TYPE = 0x05,
SDCA_CTL_ENTITY_0_FUNCTION_MANUFACTURER_ID = 0x06,
SDCA_CTL_ENTITY_0_FUNCTION_ID = 0x07,
SDCA_CTL_ENTITY_0_FUNCTION_VERSION = 0x08,
SDCA_CTL_ENTITY_0_FUNCTION_EXTENSION_ID = 0x09,
SDCA_CTL_ENTITY_0_FUNCTION_EXTENSION_VERSION = 0x0A,
SDCA_CTL_ENTITY_0_FUNCTION_STATUS = 0x10,
SDCA_CTL_ENTITY_0_FUNCTION_ACTION = 0x11,
SDCA_CTL_ENTITY_0_MATCHING_GUID = 0x12,
SDCA_CTL_ENTITY_0_DEVICE_MANUFACTURER_ID = 0x2C,
SDCA_CTL_ENTITY_0_DEVICE_PART_ID = 0x2D,
SDCA_CTL_ENTITY_0_DEVICE_VERSION = 0x2E,
SDCA_CTL_ENTITY_0_DEVICE_SDCA_VERSION = 0x2F,
/* Function Status Bits */
SDCA_CTL_ENTITY_0_DEVICE_NEWLY_ATTACHED = BIT(0),
SDCA_CTL_ENTITY_0_INTS_DISABLED_ABNORMALLY = BIT(1),
SDCA_CTL_ENTITY_0_STREAMING_STOPPED_ABNORMALLY = BIT(2),
SDCA_CTL_ENTITY_0_FUNCTION_FAULT = BIT(3),
SDCA_CTL_ENTITY_0_UMP_SEQUENCE_FAULT = BIT(4),
SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION = BIT(5),
SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET = BIT(6),
SDCA_CTL_ENTITY_0_FUNCTION_BUSY = BIT(7),
};
#endif