// SPDX-License-Identifier: GPL-2.0-only
//
// KUnit test for the Cirrus Logic cs35l56-shared module.
//
// Copyright (C) 2026 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
#include <kunit/resource.h>
#include <kunit/test.h>
#include <kunit/static_stub.h>
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/device/faux.h>
#include <linux/gpio/driver.h>
#include <linux/module.h>
#include <linux/random.h>
#include <linux/regmap.h>
#include <linux/seq_buf.h>
#include <sound/cs35l56.h>
struct cs35l56_shared_test_mock_gpio {
unsigned int pin_state;
struct gpio_chip chip;
};
struct cs35l56_shared_test_priv {
struct kunit *test;
struct faux_device *amp_dev;
struct faux_device *gpio_dev;
struct cs35l56_shared_test_mock_gpio *gpio_priv;
struct regmap *registers;
struct cs35l56_base *cs35l56_base;
u8 applied_pad_pull_state[CS35L56_MAX_GPIO];
};
struct cs35l56_shared_test_param {
int spkid_gpios[4];
int spkid_pulls[4];
unsigned long gpio_status;
int spkid;
};
KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
struct faux_device *)
KUNIT_DEFINE_ACTION_WRAPPER(regmap_exit_wrapper, regmap_exit, struct regmap *)
KUNIT_DEFINE_ACTION_WRAPPER(device_remove_software_node_wrapper,
device_remove_software_node,
struct device *)
static int cs35l56_shared_test_mock_gpio_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
return GPIO_LINE_DIRECTION_IN;
}
static int cs35l56_shared_test_mock_gpio_direction_in(struct gpio_chip *chip,
unsigned int offset)
{
return 0;
}
static int cs35l56_shared_test_mock_gpio_get(struct gpio_chip *chip, unsigned int offset)
{
struct cs35l56_shared_test_mock_gpio *gpio_priv = gpiochip_get_data(chip);
return !!(gpio_priv->pin_state & BIT(offset));
}
static const struct gpio_chip cs35l56_shared_test_mock_gpio_chip = {
.label = "cs35l56_shared_test_mock_gpio",
.owner = THIS_MODULE,
.get_direction = cs35l56_shared_test_mock_gpio_get_direction,
.direction_input = cs35l56_shared_test_mock_gpio_direction_in,
.get = cs35l56_shared_test_mock_gpio_get,
.base = -1,
.ngpio = 32,
};
/* software_node referencing the gpio driver */
static const struct software_node cs35l56_shared_test_mock_gpio_swnode = {
.name = "cs35l56_shared_test_mock_gpio",
};
static int cs35l56_shared_test_mock_gpio_probe(struct faux_device *fdev)
{
struct cs35l56_shared_test_mock_gpio *gpio_priv;
struct device *dev = &fdev->dev;
int ret;
gpio_priv = devm_kzalloc(dev, sizeof(*gpio_priv), GFP_KERNEL);
if (!gpio_priv)
return -ENOMEM;
ret = device_add_software_node(dev, &cs35l56_shared_test_mock_gpio_swnode);
if (ret)
return ret;
ret = devm_add_action_or_reset(dev, device_remove_software_node_wrapper, dev);
if (ret)
return ret;
/* GPIO core modifies our struct gpio_chip so use a copy */
gpio_priv->chip = cs35l56_shared_test_mock_gpio_chip;
gpio_priv->chip.parent = dev;
ret = devm_gpiochip_add_data(dev, &gpio_priv->chip, gpio_priv);
if (ret)
return dev_err_probe(dev, ret, "Failed to add gpiochip\n");
dev_set_drvdata(dev, gpio_priv);
return 0;
}
static struct faux_device_ops cs35l56_shared_test_mock_gpio_drv = {
.probe = cs35l56_shared_test_mock_gpio_probe,
};
static void _cs35l56_shared_test_create_dummy_gpio(struct kunit *test)
{
struct cs35l56_shared_test_priv *priv = test->priv;
priv->gpio_dev = faux_device_create("cs35l56_shared_test_mock_gpio", NULL,
&cs35l56_shared_test_mock_gpio_drv);
KUNIT_ASSERT_NOT_NULL(test, priv->gpio_dev);
KUNIT_ASSERT_EQ(test, 0,
kunit_add_action_or_reset(test,
faux_device_destroy_wrapper,
priv->gpio_dev));
priv->gpio_priv = dev_get_drvdata(&priv->gpio_dev->dev);
KUNIT_ASSERT_NOT_NULL(test, priv->gpio_priv);
}
static const struct regmap_config cs35l56_shared_test_mock_registers_regmap = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
.max_register = CS35L56_DSP1_PMEM_5114,
.cache_type = REGCACHE_MAPLE,
};
static const struct regmap_bus cs35l56_shared_test_mock_registers_regmap_bus = {
/* No handlers because it is always in cache-only */
};
static unsigned int cs35l56_shared_test_read_gpio_status(struct cs35l56_shared_test_priv *priv)
{
const struct cs35l56_shared_test_param *param = priv->test->param_value;
unsigned int reg_offs, pad_cfg, val;
unsigned int status = 0;
unsigned int mask = 1;
for (reg_offs = 0; reg_offs < CS35L56_MAX_GPIO *