Commit 038e0da7 authored by Mark Brown's avatar Mark Brown

mfd: Immutable branch between MFD, Pinctrl and soundwire due for the v6.6 merge window

Merge tag 'ib-mfd-pinctrl-soundwire-v6.6' of https://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd into tmp

Immutable branch between MFD, Pinctrl and soundwire due for the v6.6 merge window
parents 2ccdd1b1 d5282a53
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
%YAML 1.2
---
$id: http://devicetree.org/schemas/sound/cirrus,cs42l43.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Cirrus Logic CS42L43 Audio CODEC
maintainers:
- patches@opensource.cirrus.com
description: |
The CS42L43 is an audio CODEC with integrated MIPI SoundWire interface
(Version 1.2.1 compliant), I2C, SPI, and I2S/TDM interfaces designed
for portable applications. It provides a high dynamic range, stereo
DAC for headphone output, two integrated Class D amplifiers for
loudspeakers, and two ADCs for wired headset microphone input or
stereo line input. PDM inputs are provided for digital microphones.
allOf:
- $ref: dai-common.yaml#
properties:
compatible:
enum:
- cirrus,cs42l43
reg:
maxItems: 1
vdd-p-supply:
description:
Power supply for the high voltage interface.
vdd-a-supply:
description:
Power supply for internal analog circuits.
vdd-d-supply:
description:
Power supply for internal digital circuits. Can be internally supplied.
vdd-io-supply:
description:
Power supply for external interface and internal digital logic.
vdd-cp-supply:
description:
Power supply for the amplifier 3 and 4 charge pump.
vdd-amp-supply:
description:
Power supply for amplifier 1 and 2.
reset-gpios:
maxItems: 1
interrupt-controller: true
"#interrupt-cells":
const: 2
interrupts:
maxItems: 1
"#sound-dai-cells":
const: 1
clocks:
items:
- description: Synchronous audio clock provided on mclk_in.
clock-names:
const: mclk
cirrus,bias-low:
type: boolean
description:
Select a 1.8V headset micbias rather than 2.8V.
cirrus,bias-sense-microamp:
description:
Current at which the headset micbias sense clamp will engage, 0 to
disable.
enum: [ 0, 14, 23, 41, 50, 60, 68, 86, 95 ]
default: 0
cirrus,bias-ramp-ms:
description:
Time in milliseconds the hardware allows for the headset micbias to
ramp up.
enum: [ 10, 40, 90, 170 ]
default: 170
cirrus,detect-us:
description:
Time in microseconds the type detection will run for. Long values will
cause more audible effects, but give more accurate detection.
enum: [ 20, 100, 1000, 10000, 50000, 75000, 100000, 200000 ]
default: 10000
cirrus,button-automute:
type: boolean
description:
Enable the hardware automuting of decimator 1 when a headset button is
pressed.
cirrus,buttons-ohms:
description:
Impedance in Ohms for each headset button, these should be listed in
ascending order.
minItems: 1
maxItems: 6
cirrus,tip-debounce-ms:
description:
Software debounce on tip sense triggering in milliseconds.
default: 0
cirrus,tip-invert:
type: boolean
description:
Indicates tip detect polarity, inverted implies open-circuit whilst the
jack is inserted.
cirrus,tip-disable-pullup:
type: boolean
description:
Indicates if the internal pullup on the tip detect should be disabled.
cirrus,tip-fall-db-ms:
description:
Time in milliseconds a falling edge on the tip detect should be hardware
debounced for. Note the falling edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
cirrus,tip-rise-db-ms:
description:
Time in milliseconds a rising edge on the tip detect should be hardware
debounced for. Note the rising edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
cirrus,use-ring-sense:
type: boolean
description:
Indicates if the ring sense should be used.
cirrus,ring-invert:
type: boolean
description:
Indicates ring detect polarity, inverted implies open-circuit whilst the
jack is inserted.
cirrus,ring-disable-pullup:
type: boolean
description:
Indicates if the internal pullup on the ring detect should be disabled.
cirrus,ring-fall-db-ms:
description:
Time in milliseconds a falling edge on the ring detect should be hardware
debounced for. Note the falling edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
cirrus,ring-rise-db-ms:
description:
Time in milliseconds a rising edge on the ring detect should be hardware
debounced for. Note the rising edge is considered after the invert.
enum: [ 0, 125, 250, 500, 750, 1000, 1250, 1500 ]
default: 500
pinctrl:
type: object
$ref: /schemas/pinctrl/pinctrl.yaml#
additionalProperties: false
properties:
gpio-controller: true
"#gpio-cells":
const: 2
gpio-ranges:
items:
- description: A phandle to the CODEC pinctrl node
minimum: 0
- const: 0
- const: 0
- const: 3
patternProperties:
"-state$":
oneOf:
- $ref: "#/$defs/cirrus-cs42l43-state"
- patternProperties:
"-pins$":
$ref: "#/$defs/cirrus-cs42l43-state"
additionalProperties: false
spi:
type: object
$ref: /schemas/spi/spi-controller.yaml#
unevaluatedProperties: false
$defs:
cirrus-cs42l43-state:
type: object
allOf:
- $ref: /schemas/pinctrl/pincfg-node.yaml#
- $ref: /schemas/pinctrl/pinmux-node.yaml#
oneOf:
- required: [ groups ]
- required: [ pins ]
additionalProperties: false
properties:
groups:
enum: [ gpio1, gpio2, gpio3, asp, pdmout2, pdmout1, i2c, spi ]
pins:
enum: [ gpio1, gpio2, gpio3,
asp_dout, asp_fsync, asp_bclk,
pdmout2_clk, pdmout2_data, pdmout1_clk, pdmout1_data,
i2c_sda, i2c_scl,
spi_miso, spi_sck, spi_ssb ]
function:
enum: [ gpio, spdif, irq, mic-shutter, spk-shutter ]
drive-strength:
description: Set drive strength in mA
enum: [ 1, 2, 4, 8, 9, 10, 12, 16 ]
input-debounce:
description: Set input debounce in uS
enum: [ 0, 85 ]
required:
- compatible
- reg
- vdd-p-supply
- vdd-a-supply
- vdd-io-supply
- vdd-cp-supply
additionalProperties: false
examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
#address-cells = <1>;
#size-cells = <0>;
cs42l43: codec@1a {
compatible = "cirrus,cs42l43";
reg = <0x1a>;
vdd-p-supply = <&vdd5v0>;
vdd-a-supply = <&vdd1v8>;
vdd-io-supply = <&vdd1v8>;
vdd-cp-supply = <&vdd1v8>;
vdd-amp-supply = <&vdd5v0>;
reset-gpios = <&gpio 0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gpio>;
interrupts = <56 IRQ_TYPE_LEVEL_LOW>;
#sound-dai-cells = <1>;
clocks = <&clks 0>;
clock-names = "mclk";
cs42l43_pins: pinctrl {
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&cs42l43_pins 0 0 3>;
pinctrl-names = "default";
pinctrl-0 = <&pinsettings>;
pinsettings: default-state {
shutter-pins {
groups = "gpio3";
function = "mic-shutter";
};
};
};
spi {
#address-cells = <1>;
#size-cells = <0>;
cs-gpios = <&cs42l43_pins 1 0>;
sensor@0 {
compatible = "bosch,bme680";
reg = <0>;
spi-max-frequency = <1400000>;
};
};
};
};
...@@ -4886,7 +4886,10 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers) ...@@ -4886,7 +4886,10 @@ L: alsa-devel@alsa-project.org (moderated for non-subscribers)
L: patches@opensource.cirrus.com L: patches@opensource.cirrus.com
S: Maintained S: Maintained
F: Documentation/devicetree/bindings/sound/cirrus,cs* F: Documentation/devicetree/bindings/sound/cirrus,cs*
F: drivers/mfd/cs42l43*
F: drivers/pinctrl/cirrus/pinctrl-cs42l43*
F: include/dt-bindings/sound/cs* F: include/dt-bindings/sound/cs*
F: include/linux/mfd/cs42l43*
F: include/sound/cs* F: include/sound/cs*
F: sound/pci/hda/cs* F: sound/pci/hda/cs*
F: sound/pci/hda/hda_cs_dsp_ctl.* F: sound/pci/hda/hda_cs_dsp_ctl.*
......
...@@ -237,6 +237,29 @@ config MFD_CROS_EC_DEV ...@@ -237,6 +237,29 @@ config MFD_CROS_EC_DEV
To compile this driver as a module, choose M here: the module will be To compile this driver as a module, choose M here: the module will be
called cros-ec-dev. called cros-ec-dev.
config MFD_CS42L43
tristate
select MFD_CORE
select REGMAP
config MFD_CS42L43_I2C
tristate "Cirrus Logic CS42L43 (I2C)"
depends on I2C
select REGMAP_I2C
select MFD_CS42L43
help
Select this to support the Cirrus Logic CS42L43 PC CODEC with
headphone and class D speaker drivers over I2C.
config MFD_CS42L43_SDW
tristate "Cirrus Logic CS42L43 (SoundWire)"
depends on SOUNDWIRE
select REGMAP_SOUNDWIRE
select MFD_CS42L43
help
Select this to support the Cirrus Logic CS42L43 PC CODEC with
headphone and class D speaker drivers over SoundWire.
config MFD_MADERA config MFD_MADERA
tristate "Cirrus Logic Madera codecs" tristate "Cirrus Logic Madera codecs"
select MFD_CORE select MFD_CORE
......
...@@ -13,6 +13,9 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o ...@@ -13,6 +13,9 @@ obj-$(CONFIG_ARCH_BCM2835) += bcm2835-pm.o
obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o obj-$(CONFIG_MFD_BCM590XX) += bcm590xx.o
obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o obj-$(CONFIG_MFD_BD9571MWV) += bd9571mwv.o
obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o obj-$(CONFIG_MFD_CROS_EC_DEV) += cros_ec_dev.o
obj-$(CONFIG_MFD_CS42L43) += cs42l43.o
obj-$(CONFIG_MFD_CS42L43_I2C) += cs42l43-i2c.o
obj-$(CONFIG_MFD_CS42L43_SDW) += cs42l43-sdw.o
obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o obj-$(CONFIG_MFD_ENE_KB3930) += ene-kb3930.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o
......
// SPDX-License-Identifier: GPL-2.0
/*
* CS42L43 I2C driver
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/i2c.h>
#include <linux/mfd/cs42l43-regs.h>
#include <linux/module.h>
#include "cs42l43.h"
static const struct regmap_config cs42l43_i2c_regmap = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
.max_register = CS42L43_MCU_RAM_MAX,
.readable_reg = cs42l43_readable_register,
.volatile_reg = cs42l43_volatile_register,
.precious_reg = cs42l43_precious_register,
.cache_type = REGCACHE_MAPLE,
.reg_defaults = cs42l43_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default),
};
static int cs42l43_i2c_probe(struct i2c_client *i2c)
{
struct cs42l43 *cs42l43;
int ret;
cs42l43 = devm_kzalloc(&i2c->dev, sizeof(*cs42l43), GFP_KERNEL);
if (!cs42l43)
return -ENOMEM;
cs42l43->dev = &i2c->dev;
cs42l43->irq = i2c->irq;
/* A device on an I2C is always attached by definition. */
cs42l43->attached = true;
cs42l43->regmap = devm_regmap_init_i2c(i2c, &cs42l43_i2c_regmap);
if (IS_ERR(cs42l43->regmap)) {
ret = PTR_ERR(cs42l43->regmap);
dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
return cs42l43_dev_probe(cs42l43);
}
static void cs42l43_i2c_remove(struct i2c_client *i2c)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&i2c->dev);
cs42l43_dev_remove(cs42l43);
}
#if IS_ENABLED(CONFIG_OF)
static const struct of_device_id cs42l43_of_match[] = {
{ .compatible = "cirrus,cs42l43", },
{}
};
MODULE_DEVICE_TABLE(of, cs42l43_of_match);
#endif
#if IS_ENABLED(CONFIG_ACPI)
static const struct acpi_device_id cs42l43_acpi_match[] = {
{ "CSC4243", 0 },
{}
};
MODULE_DEVICE_TABLE(acpi, cs42l43_acpi_match);
#endif
static struct i2c_driver cs42l43_i2c_driver = {
.driver = {
.name = "cs42l43",
.pm = pm_ptr(&cs42l43_pm_ops),
.of_match_table = of_match_ptr(cs42l43_of_match),
.acpi_match_table = ACPI_PTR(cs42l43_acpi_match),
},
.probe = cs42l43_i2c_probe,
.remove = cs42l43_i2c_remove,
};
module_i2c_driver(cs42l43_i2c_driver);
MODULE_IMPORT_NS(MFD_CS42L43);
MODULE_DESCRIPTION("CS42L43 I2C Driver");
MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
// SPDX-License-Identifier: GPL-2.0
/*
* CS42L43 SoundWire driver
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/mfd/cs42l43-regs.h>
#include <linux/module.h>
#include <linux/device.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/soundwire/sdw_type.h>
#include "cs42l43.h"
enum cs42l43_sdw_ports {
CS42L43_DMIC_DEC_ASP_PORT = 1,
CS42L43_SPK_TX_PORT,
CS42L43_SPDIF_HP_PORT,
CS42L43_SPK_RX_PORT,
CS42L43_ASP_PORT,
};
static const struct regmap_config cs42l43_sdw_regmap = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_LITTLE,
.max_register = CS42L43_MCU_RAM_MAX,
.readable_reg = cs42l43_readable_register,
.volatile_reg = cs42l43_volatile_register,
.precious_reg = cs42l43_precious_register,
.cache_type = REGCACHE_MAPLE,
.reg_defaults = cs42l43_reg_default,
.num_reg_defaults = ARRAY_SIZE(cs42l43_reg_default),
};
static int cs42l43_read_prop(struct sdw_slave *sdw)
{
struct sdw_slave_prop *prop = &sdw->prop;
struct device *dev = &sdw->dev;
struct sdw_dpn_prop *dpn;
unsigned long addr;
int nval;
int i;
u32 bit;
prop->use_domain_irq = true;
prop->paging_support = true;
prop->wake_capable = true;
prop->source_ports = BIT(CS42L43_DMIC_DEC_ASP_PORT) | BIT(CS42L43_SPK_TX_PORT);
prop->sink_ports = BIT(CS42L43_SPDIF_HP_PORT) |
BIT(CS42L43_SPK_RX_PORT) | BIT(CS42L43_ASP_PORT);
prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
SDW_SCP_INT1_IMPL_DEF;
nval = hweight32(prop->source_ports);
prop->src_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->src_dpn_prop),
GFP_KERNEL);
if (!prop->src_dpn_prop)
return -ENOMEM;
i = 0;
dpn = prop->src_dpn_prop;
addr = prop->source_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].max_ch = 2;
dpn[i].type = SDW_DPN_FULL;
dpn[i].max_word = 24;
i++;
}
/*
* All ports are 2 channels max, except the first one,
* CS42L43_DMIC_DEC_ASP_PORT.
*/
dpn[CS42L43_DMIC_DEC_ASP_PORT].max_ch = 4;
nval = hweight32(prop->sink_ports);
prop->sink_dpn_prop = devm_kcalloc(dev, nval, sizeof(*prop->sink_dpn_prop),
GFP_KERNEL);
if (!prop->sink_dpn_prop)
return -ENOMEM;
i = 0;
dpn = prop->sink_dpn_prop;
addr = prop->sink_ports;
for_each_set_bit(bit, &addr, 32) {
dpn[i].num = bit;
dpn[i].max_ch = 2;
dpn[i].type = SDW_DPN_FULL;
dpn[i].max_word = 24;
i++;
}
return 0;
}
static int cs42l43_sdw_update_status(struct sdw_slave *sdw, enum sdw_slave_status status)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
switch (status) {
case SDW_SLAVE_ATTACHED:
dev_dbg(cs42l43->dev, "Device attach\n");
sdw_write_no_pm(sdw, CS42L43_GEN_INT_MASK_1,
CS42L43_INT_STAT_GEN1_MASK);
cs42l43->attached = true;
complete(&cs42l43->device_attach);
break;
case SDW_SLAVE_UNATTACHED:
dev_dbg(cs42l43->dev, "Device detach\n");
cs42l43->attached = false;
reinit_completion(&cs42l43->device_attach);
complete(&cs42l43->device_detach);
break;
default:
break;
}
return 0;
}
static int cs42l43_sdw_interrupt(struct sdw_slave *sdw,
struct sdw_slave_intr_status *status)
{
/*
* The IRQ itself was handled through the regmap_irq handler, this is
* just clearing up the additional Cirrus SoundWire registers that are
* not covered by the SoundWire framework or the IRQ handler itself.
* There is only a single bit in GEN_INT_STAT_1 and it doesn't clear if
* IRQs are still pending so doing a read/write here after handling the
* IRQ is fine.
*/
sdw_read_no_pm(sdw, CS42L43_GEN_INT_STAT_1);
sdw_write_no_pm(sdw, CS42L43_GEN_INT_STAT_1, CS42L43_INT_STAT_GEN1_MASK);
return 0;
}
static int cs42l43_sdw_bus_config(struct sdw_slave *sdw,
struct sdw_bus_params *params)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
int ret = 0;
mutex_lock(&cs42l43->pll_lock);
if (cs42l43->sdw_freq != params->curr_dr_freq / 2) {
if (cs42l43->sdw_pll_active) {
dev_err(cs42l43->dev,
"PLL active can't change SoundWire bus clock\n");
ret = -EBUSY;
} else {
cs42l43->sdw_freq = params->curr_dr_freq / 2;
}
}
mutex_unlock(&cs42l43->pll_lock);
return ret;
}
static const struct sdw_slave_ops cs42l43_sdw_ops = {
.read_prop = cs42l43_read_prop,
.update_status = cs42l43_sdw_update_status,
.interrupt_callback = cs42l43_sdw_interrupt,
.bus_config = cs42l43_sdw_bus_config,
};
static int cs42l43_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
{
struct cs42l43 *cs42l43;
struct device *dev = &sdw->dev;
int ret;
cs42l43 = devm_kzalloc(dev, sizeof(*cs42l43), GFP_KERNEL);
if (!cs42l43)
return -ENOMEM;
cs42l43->dev = dev;
cs42l43->sdw = sdw;
cs42l43->regmap = devm_regmap_init_sdw(sdw, &cs42l43_sdw_regmap);
if (IS_ERR(cs42l43->regmap)) {
ret = PTR_ERR(cs42l43->regmap);
dev_err(cs42l43->dev, "Failed to allocate regmap: %d\n", ret);
return ret;
}
return cs42l43_dev_probe(cs42l43);
}
static int cs42l43_sdw_remove(struct sdw_slave *sdw)
{
struct cs42l43 *cs42l43 = dev_get_drvdata(&sdw->dev);
cs42l43_dev_remove(cs42l43);
return 0;
}
static const struct sdw_device_id cs42l43_sdw_id[] = {
SDW_SLAVE_ENTRY(0x01FA, 0x4243, 0),
{}
};
MODULE_DEVICE_TABLE(sdw, cs42l43_sdw_id);
static struct sdw_driver cs42l43_sdw_driver = {
.driver = {
.name = "cs42l43",
.pm = pm_ptr(&cs42l43_pm_ops),
},
.probe = cs42l43_sdw_probe,
.remove = cs42l43_sdw_remove,
.id_table = cs42l43_sdw_id,
.ops = &cs42l43_sdw_ops,
};
module_sdw_driver(cs42l43_sdw_driver);
MODULE_IMPORT_NS(MFD_CS42L43);
MODULE_DESCRIPTION("CS42L43 SoundWire Driver");
MODULE_AUTHOR("Lucas Tanure <tanureal@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CS42L43 core driver internal data
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/mfd/cs42l43.h>
#include <linux/pm.h>
#include <linux/regmap.h>
#ifndef CS42L43_CORE_INT_H
#define CS42L43_CORE_INT_H
#define CS42L43_N_DEFAULTS 176
extern const struct dev_pm_ops cs42l43_pm_ops;
extern const struct reg_default cs42l43_reg_default[CS42L43_N_DEFAULTS];
bool cs42l43_readable_register(struct device *dev, unsigned int reg);
bool cs42l43_precious_register(struct device *dev, unsigned int reg);
bool cs42l43_volatile_register(struct device *dev, unsigned int reg);
int cs42l43_dev_probe(struct cs42l43 *cs42l43);
void cs42l43_dev_remove(struct cs42l43 *cs42l43);
#endif /* CS42L43_CORE_INT_H */
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
config PINCTRL_CS42L43
tristate "Cirrus Logic CS42L43 Pinctrl Driver"
depends on MFD_CS42L43
select GPIOLIB
select PINMUX
select PINCONF
select GENERIC_PINCONF
help
Select this to support the GPIO/Pinctrl functions of the Cirrus
Logic CS42L43 PC CODEC.
config PINCTRL_LOCHNAGAR config PINCTRL_LOCHNAGAR
tristate "Cirrus Logic Lochnagar pinctrl driver" tristate "Cirrus Logic Lochnagar pinctrl driver"
depends on MFD_LOCHNAGAR depends on MFD_LOCHNAGAR
......
# SPDX-License-Identifier: GPL-2.0-only # SPDX-License-Identifier: GPL-2.0-only
# Cirrus Logic pinctrl drivers # Cirrus Logic pinctrl drivers
obj-$(CONFIG_PINCTRL_CS42L43) += pinctrl-cs42l43.o
obj-$(CONFIG_PINCTRL_LOCHNAGAR) += pinctrl-lochnagar.o obj-$(CONFIG_PINCTRL_LOCHNAGAR) += pinctrl-lochnagar.o
pinctrl-madera-objs := pinctrl-madera-core.o pinctrl-madera-objs := pinctrl-madera-core.o
......
This diff is collapsed.
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/irq.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h> #include <linux/soundwire/sdw_registers.h>
...@@ -25,6 +26,23 @@ static int sdw_get_id(struct sdw_bus *bus) ...@@ -25,6 +26,23 @@ static int sdw_get_id(struct sdw_bus *bus)
return 0; return 0;
} }
static int sdw_irq_map(struct irq_domain *h, unsigned int virq,
irq_hw_number_t hw)
{
struct sdw_bus *bus = h->host_data;
irq_set_chip_data(virq, bus);
irq_set_chip(virq, &bus->irq_chip);
irq_set_nested_thread(virq, 1);
irq_set_noprobe(virq);
return 0;
}
static const struct irq_domain_ops sdw_domain_ops = {
.map = sdw_irq_map,
};
/** /**
* sdw_bus_master_add() - add a bus Master instance * sdw_bus_master_add() - add a bus Master instance
* @bus: bus instance * @bus: bus instance
...@@ -151,6 +169,14 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent, ...@@ -151,6 +169,14 @@ int sdw_bus_master_add(struct sdw_bus *bus, struct device *parent,
bus->params.curr_bank = SDW_BANK0; bus->params.curr_bank = SDW_BANK0;
bus->params.next_bank = SDW_BANK1; bus->params.next_bank = SDW_BANK1;
bus->irq_chip.name = dev_name(bus->dev);
bus->domain = irq_domain_create_linear(fwnode, SDW_MAX_DEVICES,
&sdw_domain_ops, bus);
if (!bus->domain) {
dev_err(bus->dev, "Failed to add IRQ domain\n");
return -EINVAL;
}
return 0; return 0;
} }
EXPORT_SYMBOL(sdw_bus_master_add); EXPORT_SYMBOL(sdw_bus_master_add);
...@@ -187,6 +213,9 @@ static int sdw_delete_slave(struct device *dev, void *data) ...@@ -187,6 +213,9 @@ static int sdw_delete_slave(struct device *dev, void *data)
void sdw_bus_master_delete(struct sdw_bus *bus) void sdw_bus_master_delete(struct sdw_bus *bus)
{ {
device_for_each_child(bus->dev, NULL, sdw_delete_slave); device_for_each_child(bus->dev, NULL, sdw_delete_slave);
irq_domain_remove(bus->domain);
sdw_master_device_del(bus); sdw_master_device_del(bus);
sdw_bus_debugfs_exit(bus); sdw_bus_debugfs_exit(bus);
...@@ -1725,6 +1754,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave) ...@@ -1725,6 +1754,9 @@ static int sdw_handle_slave_alerts(struct sdw_slave *slave)
struct device *dev = &slave->dev; struct device *dev = &slave->dev;
struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); struct sdw_driver *drv = drv_to_sdw_driver(dev->driver);
if (slave->prop.use_domain_irq && slave->irq)
handle_nested_irq(slave->irq);
if (drv->ops && drv->ops->interrupt_callback) { if (drv->ops && drv->ops->interrupt_callback) {
slave_intr.sdca_cascade = sdca_cascade; slave_intr.sdca_cascade = sdca_cascade;
slave_intr.control_port = clear; slave_intr.control_port = clear;
......
...@@ -122,6 +122,12 @@ static int sdw_drv_probe(struct device *dev) ...@@ -122,6 +122,12 @@ static int sdw_drv_probe(struct device *dev)
if (drv->ops && drv->ops->read_prop) if (drv->ops && drv->ops->read_prop)
drv->ops->read_prop(slave); drv->ops->read_prop(slave);
if (slave->prop.use_domain_irq) {
slave->irq = irq_create_mapping(slave->bus->domain, slave->dev_num);
if (!slave->irq)
dev_warn(dev, "Failed to map IRQ\n");
}
/* init the sysfs as we have properties now */ /* init the sysfs as we have properties now */
ret = sdw_slave_sysfs_init(slave); ret = sdw_slave_sysfs_init(slave);
if (ret < 0) if (ret < 0)
...@@ -166,7 +172,13 @@ static int sdw_drv_remove(struct device *dev) ...@@ -166,7 +172,13 @@ static int sdw_drv_remove(struct device *dev)
int ret = 0; int ret = 0;
mutex_lock(&slave->sdw_dev_lock); mutex_lock(&slave->sdw_dev_lock);
slave->probed = false; slave->probed = false;
if (slave->prop.use_domain_irq)
irq_dispose_mapping(irq_find_mapping(slave->bus->domain,
slave->dev_num));
mutex_unlock(&slave->sdw_dev_lock); mutex_unlock(&slave->sdw_dev_lock);
if (drv->remove) if (drv->remove)
......
This diff is collapsed.
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CS42L43 core driver external data
*
* Copyright (C) 2022-2023 Cirrus Logic, Inc. and
* Cirrus Logic International Semiconductor Ltd.
*/
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/soundwire/sdw.h>
#include <linux/workqueue.h>
#ifndef CS42L43_CORE_EXT_H
#define CS42L43_CORE_EXT_H
#define CS42L43_N_SUPPLIES 3
enum cs42l43_irq_numbers {
CS42L43_PLL_LOST_LOCK,
CS42L43_PLL_READY,
CS42L43_HP_STARTUP_DONE,
CS42L43_HP_SHUTDOWN_DONE,
CS42L43_HSDET_DONE,
CS42L43_TIPSENSE_UNPLUG_DB,
CS42L43_TIPSENSE_PLUG_DB,
CS42L43_RINGSENSE_UNPLUG_DB,
CS42L43_RINGSENSE_PLUG_DB,
CS42L43_TIPSENSE_UNPLUG_PDET,
CS42L43_TIPSENSE_PLUG_PDET,
CS42L43_RINGSENSE_UNPLUG_PDET,
CS42L43_RINGSENSE_PLUG_PDET,
CS42L43_HS2_BIAS_SENSE,
CS42L43_HS1_BIAS_SENSE,
CS42L43_DC_DETECT1_FALSE,
CS42L43_DC_DETECT1_TRUE,
CS42L43_HSBIAS_CLAMPED,
CS42L43_HS3_4_BIAS_SENSE,
CS42L43_AMP2_CLK_STOP_FAULT,
CS42L43_AMP1_CLK_STOP_FAULT,
CS42L43_AMP2_VDDSPK_FAULT,
CS42L43_AMP1_VDDSPK_FAULT,
CS42L43_AMP2_SHUTDOWN_DONE,
CS42L43_AMP1_SHUTDOWN_DONE,
CS42L43_AMP2_STARTUP_DONE,
CS42L43_AMP1_STARTUP_DONE,
CS42L43_AMP2_THERM_SHDN,
CS42L43_AMP1_THERM_SHDN,
CS42L43_AMP2_THERM_WARN,
CS42L43_AMP1_THERM_WARN,
CS42L43_AMP2_SCDET,
CS42L43_AMP1_SCDET,
CS42L43_GPIO3_FALL,
CS42L43_GPIO3_RISE,
CS42L43_GPIO2_FALL,
CS42L43_GPIO2_RISE,
CS42L43_GPIO1_FALL,
CS42L43_GPIO1_RISE,
CS42L43_HP_ILIMIT,
CS42L43_HP_LOADDET_DONE,
};
struct cs42l43 {
struct device *dev;
struct regmap *regmap;
struct sdw_slave *sdw;
struct regulator *vdd_p;
struct regulator *vdd_d;
struct regulator_bulk_data core_supplies[CS42L43_N_SUPPLIES];
struct gpio_desc *reset;
int irq;
struct regmap_irq_chip irq_chip;
struct regmap_irq_chip_data *irq_data;
struct work_struct boot_work;
struct completion device_attach;
struct completion device_detach;
struct completion firmware_download;
int firmware_error;
unsigned int sdw_freq;
/* Lock to gate control of the PLL and its sources. */
struct mutex pll_lock;
bool sdw_pll_active;
bool attached;
bool hw_lock;
};
#endif /* CS42L43_CORE_EXT_H */
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <linux/bug.h> #include <linux/bug.h>
#include <linux/lockdep_types.h> #include <linux/lockdep_types.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
...@@ -370,6 +372,7 @@ struct sdw_dpn_prop { ...@@ -370,6 +372,7 @@ struct sdw_dpn_prop {
* @clock_reg_supported: the Peripheral implements the clock base and scale * @clock_reg_supported: the Peripheral implements the clock base and scale
* registers introduced with the SoundWire 1.2 specification. SDCA devices * registers introduced with the SoundWire 1.2 specification. SDCA devices
* do not need to set this boolean property as the registers are required. * do not need to set this boolean property as the registers are required.
* @use_domain_irq: call actual IRQ handler on slave, as well as callback
*/ */
struct sdw_slave_prop { struct sdw_slave_prop {
u32 mipi_revision; u32 mipi_revision;
...@@ -394,6 +397,7 @@ struct sdw_slave_prop { ...@@ -394,6 +397,7 @@ struct sdw_slave_prop {
u8 scp_int1_mask; u8 scp_int1_mask;
u32 quirks; u32 quirks;
bool clock_reg_supported; bool clock_reg_supported;
bool use_domain_irq;
}; };
#define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY BIT(0) #define SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY BIT(0)
...@@ -641,6 +645,7 @@ struct sdw_slave_ops { ...@@ -641,6 +645,7 @@ struct sdw_slave_ops {
* struct sdw_slave - SoundWire Slave * struct sdw_slave - SoundWire Slave
* @id: MIPI device ID * @id: MIPI device ID
* @dev: Linux device * @dev: Linux device
* @irq: IRQ number
* @status: Status reported by the Slave * @status: Status reported by the Slave
* @bus: Bus handle * @bus: Bus handle
* @prop: Slave properties * @prop: Slave properties
...@@ -670,6 +675,7 @@ struct sdw_slave_ops { ...@@ -670,6 +675,7 @@ struct sdw_slave_ops {
struct sdw_slave { struct sdw_slave {
struct sdw_slave_id id; struct sdw_slave_id id;
struct device dev; struct device dev;
int irq;
enum sdw_slave_status status; enum sdw_slave_status status;
struct sdw_bus *bus; struct sdw_bus *bus;
struct sdw_slave_prop prop; struct sdw_slave_prop prop;
...@@ -885,6 +891,7 @@ struct sdw_master_ops { ...@@ -885,6 +891,7 @@ struct sdw_master_ops {
* is used to compute and program bus bandwidth, clock, frame shape, * is used to compute and program bus bandwidth, clock, frame shape,
* transport and port parameters * transport and port parameters
* @debugfs: Bus debugfs * @debugfs: Bus debugfs
* @domain: IRQ domain
* @defer_msg: Defer message * @defer_msg: Defer message
* @clk_stop_timeout: Clock stop timeout computed * @clk_stop_timeout: Clock stop timeout computed
* @bank_switch_timeout: Bank switch timeout computed * @bank_switch_timeout: Bank switch timeout computed
...@@ -920,6 +927,8 @@ struct sdw_bus { ...@@ -920,6 +927,8 @@ struct sdw_bus {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *debugfs; struct dentry *debugfs;
#endif #endif
struct irq_chip irq_chip;
struct irq_domain *domain;
struct sdw_defer defer_msg; struct sdw_defer defer_msg;
unsigned int clk_stop_timeout; unsigned int clk_stop_timeout;
u32 bank_switch_timeout; u32 bank_switch_timeout;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment