mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
synced 2026-05-02 18:17:50 -04:00
Merge tag 'mfd-next-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd
Pull MFD updates from Lee Jones:
"New Drivers:
- ROHM BD96801 Power Management IC
- Cirrus Logic CS40L50 Haptic Driver with Waveform Memory
- Marvell 88PM886 Power Management IC
New Device Support:
- Keyboard Backlight to ChromeOS Embedded Controller
- LEDs to ChromeOS Embedded Controller
- Charge Control to ChromeOS Embedded Controller
- HW Monitoring Service to ChromeOS Embedded Controller
- AUXADCs to MediaTek MT635{7,8,9} Power Management ICs
New Functionality:
- Allow Syscon consumers to supply their own Regmaps on registration
Fix-ups:
- Constify/staticise applicable data structures
- Remove superfluous/duplicated/unused sections
- Device Tree binding adaptions/conversions/creation
- Trivial; spelling, whitespace, coding-style adaptions
- Utilise centrally provided helpers and macros to aid
simplicity/duplication
- Drop i2c_device_id::driver_data where the value is unused
- Replace ACPI/DT firmware helpers with agnostic variants
- Move over to GPIOD (descriptor-based) APIs
- Annotate a bunch of __counted_by() cases
- Straighten out some includes
Bug Fixes:
- Ensure potentially asserted recent lines are deasserted during
initialisation
- Avoid "<module>.ko is added to multiple modules" warnings
- Supply a bunch of MODULE_DESCRIPTIONs to silence modpost warnings
- Fix Wvoid-pointer-to-enum-cast warnings"
* tag 'mfd-next-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (87 commits)
mfd: timberdale: Attach device properties to TSC2007 board info
mfd: tmio: Move header to platform_data
mfd: tmio: Sanitize comments
mfd: tmio: Update include files
mmc: tmio/sdhi: Fix includes
mfd: tmio: Remove obsolete io accessors
mfd: tmio: Remove obsolete platform_data
watchdog: bd96801_wdt: Add missing include for FIELD_*()
dt-bindings: mfd: syscon: Add APM poweroff mailbox
dt-bindings: mfd: syscon: Split and enforce documenting MFD children
dt-bindings: mfd: rk817: Merge support for RK809
dt-bindings: mfd: rk817: Fixup clocks and reference dai-common
dt-bindings: mfd: syscon: Add TI's opp table compatible
mfd: omap-usb-tll: Use struct_size to allocate tll
dt-bindings: mfd: Explain lack of child dependency in simple-mfd
dt-bindings: mfd: Dual licensing for st,stpmic1 bindings
mfd: omap-usb-tll: Annotate struct usbtll_omap with __counted_by
mfd: tps6594-core: Remove unneeded semicolon in tps6594_check_crc_mode()
mfd: lm3533: Move to new GPIO descriptor-based APIs
mfd: tps65912: Use devm helper functions to simplify probe
...
This commit is contained in:
@@ -275,6 +275,12 @@
|
||||
#define HALO_MPU_VIO_ERR_SRC_MASK 0x00007fff
|
||||
#define HALO_MPU_VIO_ERR_SRC_SHIFT 0
|
||||
|
||||
/*
|
||||
* Write Sequence
|
||||
*/
|
||||
#define WSEQ_OP_MAX_WORDS 3
|
||||
#define WSEQ_END_OF_SCRIPT 0xFFFFFF
|
||||
|
||||
struct cs_dsp_ops {
|
||||
bool (*validate_version)(struct cs_dsp *dsp, unsigned int version);
|
||||
unsigned int (*parse_sizes)(struct cs_dsp *dsp,
|
||||
@@ -3495,6 +3501,278 @@ int cs_dsp_chunk_read(struct cs_dsp_chunk *ch, int nbits)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_read, FW_CS_DSP);
|
||||
|
||||
|
||||
struct cs_dsp_wseq_op {
|
||||
struct list_head list;
|
||||
u32 address;
|
||||
u32 data;
|
||||
u16 offset;
|
||||
u8 operation;
|
||||
};
|
||||
|
||||
static void cs_dsp_wseq_clear(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq)
|
||||
{
|
||||
struct cs_dsp_wseq_op *op, *op_tmp;
|
||||
|
||||
list_for_each_entry_safe(op, op_tmp, &wseq->ops, list) {
|
||||
list_del(&op->list);
|
||||
devm_kfree(dsp->dev, op);
|
||||
}
|
||||
}
|
||||
|
||||
static int cs_dsp_populate_wseq(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq)
|
||||
{
|
||||
struct cs_dsp_wseq_op *op = NULL;
|
||||
struct cs_dsp_chunk chunk;
|
||||
u8 *words;
|
||||
int ret;
|
||||
|
||||
if (!wseq->ctl) {
|
||||
cs_dsp_err(dsp, "No control for write sequence\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
words = kzalloc(wseq->ctl->len, GFP_KERNEL);
|
||||
if (!words)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = cs_dsp_coeff_read_ctrl(wseq->ctl, 0, words, wseq->ctl->len);
|
||||
if (ret) {
|
||||
cs_dsp_err(dsp, "Failed to read %s: %d\n", wseq->ctl->subname, ret);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&wseq->ops);
|
||||
|
||||
chunk = cs_dsp_chunk(words, wseq->ctl->len);
|
||||
|
||||
while (!cs_dsp_chunk_end(&chunk)) {
|
||||
op = devm_kzalloc(dsp->dev, sizeof(*op), GFP_KERNEL);
|
||||
if (!op) {
|
||||
ret = -ENOMEM;
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
op->offset = cs_dsp_chunk_bytes(&chunk);
|
||||
op->operation = cs_dsp_chunk_read(&chunk, 8);
|
||||
|
||||
switch (op->operation) {
|
||||
case CS_DSP_WSEQ_END:
|
||||
op->data = WSEQ_END_OF_SCRIPT;
|
||||
break;
|
||||
case CS_DSP_WSEQ_UNLOCK:
|
||||
op->data = cs_dsp_chunk_read(&chunk, 16);
|
||||
break;
|
||||
case CS_DSP_WSEQ_ADDR8:
|
||||
op->address = cs_dsp_chunk_read(&chunk, 8);
|
||||
op->data = cs_dsp_chunk_read(&chunk, 32);
|
||||
break;
|
||||
case CS_DSP_WSEQ_H16:
|
||||
case CS_DSP_WSEQ_L16:
|
||||
op->address = cs_dsp_chunk_read(&chunk, 24);
|
||||
op->data = cs_dsp_chunk_read(&chunk, 16);
|
||||
break;
|
||||
case CS_DSP_WSEQ_FULL:
|
||||
op->address = cs_dsp_chunk_read(&chunk, 32);
|
||||
op->data = cs_dsp_chunk_read(&chunk, 32);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
cs_dsp_err(dsp, "Unsupported op: %X\n", op->operation);
|
||||
devm_kfree(dsp->dev, op);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
list_add_tail(&op->list, &wseq->ops);
|
||||
|
||||
if (op->operation == CS_DSP_WSEQ_END)
|
||||
break;
|
||||
}
|
||||
|
||||
if (op && op->operation != CS_DSP_WSEQ_END) {
|
||||
cs_dsp_err(dsp, "%s missing end terminator\n", wseq->ctl->subname);
|
||||
ret = -ENOENT;
|
||||
}
|
||||
|
||||
err_free:
|
||||
kfree(words);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_wseq_init() - Initialize write sequences contained within the loaded DSP firmware
|
||||
* @dsp: Pointer to DSP structure
|
||||
* @wseqs: List of write sequences to initialize
|
||||
* @num_wseqs: Number of write sequences to initialize
|
||||
*
|
||||
* Return: Zero for success, a negative number on error.
|
||||
*/
|
||||
int cs_dsp_wseq_init(struct cs_dsp *dsp, struct cs_dsp_wseq *wseqs, unsigned int num_wseqs)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
lockdep_assert_held(&dsp->pwr_lock);
|
||||
|
||||
for (i = 0; i < num_wseqs; i++) {
|
||||
ret = cs_dsp_populate_wseq(dsp, &wseqs[i]);
|
||||
if (ret) {
|
||||
cs_dsp_wseq_clear(dsp, &wseqs[i]);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_init, FW_CS_DSP);
|
||||
|
||||
static struct cs_dsp_wseq_op *cs_dsp_wseq_find_op(u32 addr, u8 op_code,
|
||||
struct list_head *wseq_ops)
|
||||
{
|
||||
struct cs_dsp_wseq_op *op;
|
||||
|
||||
list_for_each_entry(op, wseq_ops, list) {
|
||||
if (op->operation == op_code && op->address == addr)
|
||||
return op;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* cs_dsp_wseq_write() - Add or update an entry in a write sequence
|
||||
* @dsp: Pointer to a DSP structure
|
||||
* @wseq: Write sequence to write to
|
||||
* @addr: Address of the register to be written to
|
||||
* @data: Data to be written
|
||||
* @op_code: The type of operation of the new entry
|
||||
* @update: If true, searches for the first entry in the write sequence with
|
||||
* the same address and op_code, and replaces it. If false, creates a new entry
|
||||
* at the tail
|
||||
*
|
||||
* This function formats register address and value pairs into the format
|
||||
* required for write sequence entries, and either updates or adds the
|
||||
* new entry into the write sequence.
|
||||
*
|
||||
* If update is set to true and no matching entry is found, it will add a new entry.
|
||||
*
|
||||
* Return: Zero for success, a negative number on error.
|
||||
*/
|
||||
int cs_dsp_wseq_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq,
|
||||
u32 addr, u32 data, u8 op_code, bool update)
|
||||
{
|
||||
struct cs_dsp_wseq_op *op_end, *op_new = NULL;
|
||||
u32 words[WSEQ_OP_MAX_WORDS];
|
||||
struct cs_dsp_chunk chunk;
|
||||
int new_op_size, ret;
|
||||
|
||||
if (update)
|
||||
op_new = cs_dsp_wseq_find_op(addr, op_code, &wseq->ops);
|
||||
|
||||
/* If entry to update is not found, treat it as a new operation */
|
||||
if (!op_new) {
|
||||
op_end = cs_dsp_wseq_find_op(0, CS_DSP_WSEQ_END, &wseq->ops);
|
||||
if (!op_end) {
|
||||
cs_dsp_err(dsp, "Missing terminator for %s\n", wseq->ctl->subname);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
op_new = devm_kzalloc(dsp->dev, sizeof(*op_new), GFP_KERNEL);
|
||||
if (!op_new)
|
||||
return -ENOMEM;
|
||||
|
||||
op_new->operation = op_code;
|
||||
op_new->address = addr;
|
||||
op_new->offset = op_end->offset;
|
||||
update = false;
|
||||
}
|
||||
|
||||
op_new->data = data;
|
||||
|
||||
chunk = cs_dsp_chunk(words, sizeof(words));
|
||||
cs_dsp_chunk_write(&chunk, 8, op_new->operation);
|
||||
|
||||
switch (op_code) {
|
||||
case CS_DSP_WSEQ_FULL:
|
||||
cs_dsp_chunk_write(&chunk, 32, op_new->address);
|
||||
cs_dsp_chunk_write(&chunk, 32, op_new->data);
|
||||
break;
|
||||
case CS_DSP_WSEQ_L16:
|
||||
case CS_DSP_WSEQ_H16:
|
||||
cs_dsp_chunk_write(&chunk, 24, op_new->address);
|
||||
cs_dsp_chunk_write(&chunk, 16, op_new->data);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
cs_dsp_err(dsp, "Operation %X not supported\n", op_code);
|
||||
goto op_new_free;
|
||||
}
|
||||
|
||||
new_op_size = cs_dsp_chunk_bytes(&chunk);
|
||||
|
||||
if (!update) {
|
||||
if (wseq->ctl->len - op_end->offset < new_op_size) {
|
||||
cs_dsp_err(dsp, "Not enough memory in %s for entry\n", wseq->ctl->subname);
|
||||
ret = -E2BIG;
|
||||
goto op_new_free;
|
||||
}
|
||||
|
||||
op_end->offset += new_op_size;
|
||||
|
||||
ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_end->offset / sizeof(u32),
|
||||
&op_end->data, sizeof(u32));
|
||||
if (ret)
|
||||
goto op_new_free;
|
||||
|
||||
list_add_tail(&op_new->list, &op_end->list);
|
||||
}
|
||||
|
||||
ret = cs_dsp_coeff_write_ctrl(wseq->ctl, op_new->offset / sizeof(u32),
|
||||
words, new_op_size);
|
||||
if (ret)
|
||||
goto op_new_free;
|
||||
|
||||
return 0;
|
||||
|
||||
op_new_free:
|
||||
devm_kfree(dsp->dev, op_new);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_write, FW_CS_DSP);
|
||||
|
||||
/**
|
||||
* cs_dsp_wseq_multi_write() - Add or update multiple entries in a write sequence
|
||||
* @dsp: Pointer to a DSP structure
|
||||
* @wseq: Write sequence to write to
|
||||
* @reg_seq: List of address-data pairs
|
||||
* @num_regs: Number of address-data pairs
|
||||
* @op_code: The types of operations of the new entries
|
||||
* @update: If true, searches for the first entry in the write sequence with
|
||||
* the same address and op_code, and replaces it. If false, creates a new entry
|
||||
* at the tail
|
||||
*
|
||||
* This function calls cs_dsp_wseq_write() for multiple address-data pairs.
|
||||
*
|
||||
* Return: Zero for success, a negative number on error.
|
||||
*/
|
||||
int cs_dsp_wseq_multi_write(struct cs_dsp *dsp, struct cs_dsp_wseq *wseq,
|
||||
const struct reg_sequence *reg_seq, int num_regs,
|
||||
u8 op_code, bool update)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
for (i = 0; i < num_regs; i++) {
|
||||
ret = cs_dsp_wseq_write(dsp, wseq, reg_seq[i].reg,
|
||||
reg_seq[i].def, op_code, update);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cs_dsp_wseq_multi_write, FW_CS_DSP);
|
||||
|
||||
MODULE_DESCRIPTION("Cirrus Logic DSP Support");
|
||||
MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
Reference in New Issue
Block a user