Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board.
Signed-off-by: Charles Keepax <[email protected]>
Acked-by: Rob Herring <[email protected]>
---
Changes since v2:
- Used vendor prefix on aif-master and aif-slave
- Used -gpios instead of -gpio in example DT
- Used - rather than _ on node names in example DT
- Removed some clocks from the clock DT binding doc since they are
now being registered purely through DT as fixed clocks
Thanks,
Charles
.../devicetree/bindings/mfd/cirrus,lochnagar.txt | 230 +++++++++++++++++++++
include/dt-bindings/clk/lochnagar.h | 26 +++
include/dt-bindings/pinctrl/lochnagar.h | 132 ++++++++++++
3 files changed, 388 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt
create mode 100644 include/dt-bindings/clk/lochnagar.h
create mode 100644 include/dt-bindings/pinctrl/lochnagar.h
diff --git a/Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt
new file mode 100644
index 0000000000000..4bed850aa7101
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/cirrus,lochnagar.txt
@@ -0,0 +1,230 @@
+Cirrus Logic Lochnagar Audio Development Board
+
+Lochnagar is an evaluation and development board for Cirrus Logic
+Smart CODEC and Amp devices. It allows the connection of most Cirrus
+Logic devices on mini-cards, as well as allowing connection of
+various application processor systems to provide a full evaluation
+platform. Audio system topology, clocking and power can all be
+controlled through the Lochnagar, allowing the device under test
+to be used in a variety of possible use cases.
+
+Also see these documents for generic binding information:
+ [1] GPIO : ../gpio/gpio.txt
+ [2] Pinctrl: ../pinctrl/pinctrl-bindings.txt
+ [3] Clock : ../clock/clock-bindings.txt
+ [4] Regulator: ../regulator/regulator.txt
+
+And these for relevant defines:
+ [5] include/dt-bindings/pinctrl/lochnagar.h
+ [6] include/dt-bindings/clock/lochnagar.h
+
+Required properties:
+
+ - compatible : One of the following strings:
+ "cirrus,lochnagar1"
+ "cirrus,lochnagar2"
+
+ - reg : I2C slave address
+
+ - gpio-controller : Indicates this device is a GPIO controller.
+ - #gpio-cells : Must be 2. The first cell is the pin number, see
+ [5] for available pins and the second cell is used to specify
+ optional parameters, see [1].
+ - gpio-ranges : Range of pins managed by the GPIO controller, see
+ [1]. Both the GPIO and Pinctrl base should be set to zero and the
+ count to the appropriate of the LOCHNAGARx_PIN_NUM_GPIOS define,
+ see [5].
+
+ - reset-gpios : Reset line to the Lochnagar, see [1].
+
+ - #clock-cells : Must be 1. The first cell indicates the clock
+ number, see [6] for available clocks and [3].
+
+ - pinctrl-names : A pinctrl state named "default" must be defined.
+ - pinctrl-0 : A phandle to the default pinctrl state.
+
+ - SYSVDD-supply: Primary power supply for the Lochnagar.
+
+Optional properties:
+
+ - present-gpios : Host present line, indicating the presence of a
+ host system, see [1]. This can be omitted if the present line is
+ tied in hardware.
+
+ - clocks : Must contain an entry for each clock in clock-names.
+ - clock-names : May contain entries for each of the following
+ clocks:
+ - ln-cdc-clkout : Output clock from CODEC card.
+ - ln-dsp-clkout : Output clock from DSP card.
+ - ln-gf-mclk1,ln-gf-mclk2,ln-gf-mclk3,ln-gf-mclk4 : Optional
+ input audio clocks from host system.
+ - ln-psia1-mclk, ln-psia2-mclk : Optional input audio clocks from
+ external connector.
+ - ln-spdif-clkout : Optional input audio clock from SPDIF.
+ - ln-adat-clkout : Optional input audio clock from ADAT.
+
+ - assigned-clocks : A list of Lochnagar clocks to be reparented, see
+ [6] for available clocks.
+ - assigned-clock-parents : Parents to be assigned to the clocks
+ listed in "assigned-clocks".
+
+ - MICBIAS1-supply, MICBIAS2-supply: Regulator supplies for the
+ MICxVDD outputs, supplying the digital microphones, normally
+ supplied from the attached CODEC.
+
+Required sub-nodes:
+
+The pin configurations are defined as a child of the pinctrl states
+node, see [2]. Each sub-node can have the following properties:
+ - groups : A list of groups to select (either this or "pins" must be
+ specified), available groups:
+ codec-aif1, codec-aif2, codec-aif3, dsp-aif1, dsp-aif2, psia1,
+ psia2, gf-aif1, gf-aif2, gf-aif3, gf-aif4, spdif-aif, usb-aif1,
+ usb-aif2, adat-aif, soundcard-aif
+ - pins : A list of pin names to select (either this or "groups" must
+ be specified), available pins:
+ fgpa-gpio1, fgpa-gpio2, fgpa-gpio3, fgpa-gpio4, fgpa-gpio5,
+ fgpa-gpio6, codec-gpio1, codec-gpio2, codec-gpio3, codec-gpio4,
+ codec-gpio5, codec-gpio6, codec-gpio7, codec-gpio8, dsp-gpio1,
+ dsp-gpio2, dsp-gpio3, dsp-gpio4, dsp-gpio5, dsp-gpio6, gf-gpio2,
+ gf-gpio3, gf-gpio7, codec-aif1-bclk, codec-aif1-rxdat,
+ codec-aif1-lrclk, codec-aif1-txdat, codec-aif2-bclk,
+ codec-aif2-rxdat, codec-aif2-lrclk, codec-aif2-txdat,
+ codec-aif3-bclk, codec-aif3-rxdat, codec-aif3-lrclk,
+ codec-aif3-txdat, dsp-aif1-bclk, dsp-aif1-rxdat, dsp-aif1-lrclk,
+ dsp-aif1-txdat, dsp-aif2-bclk, dsp-aif2-rxdat,
+ dsp-aif2-lrclk, dsp-aif2-txdat, psia1-bclk, psia1-rxdat,
+ psia1-lrclk, psia1-txdat, psia2-bclk, psia2-rxdat, psia2-lrclk,
+ psia2-txdat, gf-aif3-bclk, gf-aif3-rxdat, gf-aif3-lrclk,
+ gf-aif3-txdat, gf-aif4-bclk, gf-aif4-rxdat, gf-aif4-lrclk,
+ gf-aif4-txdat, gf-aif1-bclk, gf-aif1-rxdat, gf-aif1-lrclk,
+ gf-aif1-txdat, gf-aif2-bclk, gf-aif2-rxdat, gf-aif2-lrclk,
+ gf-aif2-txdat, dsp-uart1-rx, dsp-uart1-tx, dsp-uart2-rx,
+ dsp-uart2-tx, gf-uart2-rx, gf-uart2-tx, usb-uart-rx,
+ codec-pdmclk1, codec-pdmdat1, codec-pdmclk2, codec-pdmdat2,
+ codec-dmicclk1, codec-dmicdat1, codec-dmicclk2, codec-dmicdat2,
+ codec-dmicclk3, codec-dmicdat3, codec-dmicclk4, codec-dmicdat4,
+ dsp-dmicclk1, dsp-dmicdat1, dsp-dmicclk2, dsp-dmicdat2, i2c2-scl,
+ i2c2-sda, i2c3-scl, i2c3-sda, i2c4-scl, i2c4-sda, dsp-standby,
+ codec-mclk1, codec-mclk2, dsp-clkin, psia1-mclk, psia2-mclk,
+ gf-gpio1, gf-gpio5, dsp-gpio20, led1, led2
+ - function : The mux function to select, available functions:
+ aif, fgpa-gpio1, fgpa-gpio2, fgpa-gpio3, fgpa-gpio4, fgpa-gpio5,
+ fgpa-gpio6, codec-gpio1, codec-gpio2, codec-gpio3, codec-gpio4,
+ codec-gpio5, codec-gpio6, codec-gpio7, codec-gpio8, dsp-gpio1,
+ dsp-gpio2, dsp-gpio3, dsp-gpio4, dsp-gpio5, dsp-gpio6, gf-gpio2,
+ gf-gpio3, gf-gpio7, gf-gpio1, gf-gpio5, dsp-gpio20, codec-clkout,
+ dsp-clkout, pmic-32k, spdif-clkout, clk-12m288, clk-11m2986,
+ clk-24m576, clk-22m5792, xmos-mclk, gf-clkout1, gf-mclk1,
+ gf-mclk3, gf-mclk2, gf-clkout2, codec-mclk1, codec-mclk2,
+ dsp-clkin, psia1-mclk, psia2-mclk, spdif-mclk, codec-irq,
+ codec-reset, dsp-reset, dsp-irq, dsp-standby, codec-pdmclk1,
+ codec-pdmdat1, codec-pdmclk2, codec-pdmdat2, codec-dmicclk1,
+ codec-dmicdat1, codec-dmicclk2, codec-dmicdat2, codec-dmicclk3,
+ codec-dmicdat3, codec-dmicclk4, codec-dmicdat4, dsp-dmicclk1,
+ dsp-dmicdat1, dsp-dmicclk2, dsp-dmicdat2, dsp-uart1-rx,
+ dsp-uart1-tx, dsp-uart2-rx, dsp-uart2-tx, gf-uart2-rx,
+ gf-uart2-tx, usb-uart-rx, usb-uart-tx, i2c2-scl, i2c2-sda,
+ i2c3-scl, i2c3-sda, i2c4-scl, i2c4-sda, gpio, spdif-aif, psia1,
+ psia1-bclk, psia1-lrclk, psia1-rxdat, psia1-txdat, psia2,
+ psia2-bclk, psia2-lrclk, psia2-rxdat, psia2-txdat, codec-aif1,
+ codec-aif1-bclk, codec-aif1-lrclk, codec-aif1-rxdat,
+ codec-aif1-txdat, codec-aif2, codec-aif2-bclk, codec-aif2-lrclk,
+ codec-aif2-rxdat, codec-aif2-txdat, codec-aif3, codec-aif3-bclk,
+ codec-aif3-lrclk, codec-aif3-rxdat, codec-aif3-txdat, dsp-aif1,
+ dsp-aif1-bclk, dsp-aif1-lrclk, dsp-aif1-rxdat, dsp-aif1-txdat,
+ dsp-aif2, dsp-aif2-bclk, dsp-aif2-lrclk, dsp-aif2-rxdat,
+ dsp-aif2-txdat, gf-aif3, gf-aif3-bclk, gf-aif3-lrclk,
+ gf-aif3-rxdat, gf-aif3-txdat, gf-aif4, gf-aif4-bclk,
+ gf-aif4-lrclk, gf-aif4-rxdat, gf-aif4-txdat, gf-aif1,
+ gf-aif1-bclk, gf-aif1-lrclk, gf-aif1-rxdat, gf-aif1-txdat,
+ gf-aif2, gf-aif2-bclk, gf-aif2-lrclk, gf-aif2-rxdat,
+ gf-aif2-txdat, usb-aif1, usb-aif2, adat-aif, soundcard-aif,
+
+ - aif-master : Specifies that an AIF group will be used as a master
+ interface (either this or aif-slave is required if a group is
+ being muxed to an AIF)
+ - aif-slave : Specifies that an AIF group will be used as a slave
+ interface (either this or aif-master is required if a group is
+ being muxed to an AIF)
+
+Optional sub-nodes:
+
+ - VDDCORE : Initialisation data for the VDDCORE regulator, which
+ supplies the CODECs digital core if it has no build regulator for that
+ purpose.
+
+ - MICVDD : Initialisation data for the MICVDD regulator, which
+ supplies the CODECs MICVDD.
+ - MIC1VDD, MIC2VDD : Initialisation data for the MICxVDD supplies.
+ Optional Properties:
+ - cirrus,micbias-input : A property selecting which of the CODEC
+ minicard micbias outputs should be used, valid values are 1 - 4.
+
+ - VDD1V8 : Recommended fixed regulator for the VDD1V8 regulator, which supplies the
+ CODECs analog and 1.8V digital supplies.
+ Required Properties:
+ - compatible : Should be set to "regulator-fixed"
+ - regulator-min-microvolt : Should be set to 1.8V
+ - regulator-max-microvolt : Should be set to 1.8V
+ - regulator-boot-on
+ - regulator-always-on
+ - vin-supply : Should be set to same supply as SYSVDD
+
+Example:
+lochnagar: lochnagar@22 {
+ compatible = "cirrus,lochnagar2";
+ reg = <0x22>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-ranges = <&lochnagar 0 0 110>;
+
+ reset-gpios = <&gpio0 55 0>;
+ present-gpios = <&gpio0 60 0>;
+
+ #clock-cells = <1>;
+
+ clocks = <&clk-audio>;
+ clock-names = "ln-gf-mclk2";
+
+ assigned-clocks = <&lochnagar LOCHNAGAR_CDC_MCLK1>,
+ <&lochnagar LOCHNAGAR_CDC_MCLK2>;
+ assigned-clock-parents = <&clk-audio>,
+ <&lochnagar LOCHNAGAR_PMIC_32K>;
+
+ SYSVDD-supply = <&wallvdd>;
+
+ pinctrl-names = "default";
+ pinctrl-0 = <&pin-settings>;
+
+ pin-settings: pin-settings {
+ ap-aif {
+ aif-slave;
+ groups = "gf-aif1";
+ function = "codec-aif3";
+ };
+ codec-aif {
+ aif-master;
+ groups = "codec-aif3";
+ function = "gf-aif1";
+ };
+ };
+
+ lochnagar-micvdd: MICVDD {
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ lochnagar-vdd1v8: VDD1V8 {
+ compatible = "regulator-fixed";
+
+ regulator-name = "VDD1V8";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-boot-on;
+ regulator-always-on;
+
+ vin-supply = <&wallvdd>;
+ };
+};
diff --git a/include/dt-bindings/clk/lochnagar.h b/include/dt-bindings/clk/lochnagar.h
new file mode 100644
index 0000000000000..8fa20551ff171
--- /dev/null
+++ b/include/dt-bindings/clk/lochnagar.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Device Tree defines for Lochnagar clocking
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#ifndef DT_BINDINGS_CLK_LOCHNAGAR_H
+#define DT_BINDINGS_CLK_LOCHNAGAR_H
+
+#define LOCHNAGAR_CDC_MCLK1 0
+#define LOCHNAGAR_CDC_MCLK2 1
+#define LOCHNAGAR_DSP_CLKIN 2
+#define LOCHNAGAR_GF_CLKOUT1 3
+#define LOCHNAGAR_GF_CLKOUT2 4
+#define LOCHNAGAR_PSIA1_MCLK 5
+#define LOCHNAGAR_PSIA2_MCLK 6
+#define LOCHNAGAR_SPDIF_MCLK 7
+#define LOCHNAGAR_ADAT_MCLK 8
+#define LOCHNAGAR_SOUNDCARD_MCLK 9
+#define LOCHNAGAR_SPDIF_CLKOUT 10
+
+#endif
diff --git a/include/dt-bindings/pinctrl/lochnagar.h b/include/dt-bindings/pinctrl/lochnagar.h
new file mode 100644
index 0000000000000..644760bf5725b
--- /dev/null
+++ b/include/dt-bindings/pinctrl/lochnagar.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Device Tree defines for Lochnagar pinctrl
+ *
+ * Copyright (c) 2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#ifndef DT_BINDINGS_PINCTRL_LOCHNAGAR_H
+#define DT_BINDINGS_PINCTRL_LOCHNAGAR_H
+
+#define LOCHNAGAR1_PIN_CDC_RESET 0
+#define LOCHNAGAR1_PIN_DSP_RESET 1
+#define LOCHNAGAR1_PIN_CDC_CIF1MODE 2
+#define LOCHNAGAR1_PIN_NUM_GPIOS 3
+
+#define LOCHNAGAR2_PIN_CDC_RESET 0
+#define LOCHNAGAR2_PIN_DSP_RESET 1
+#define LOCHNAGAR2_PIN_CDC_CIF1MODE 2
+#define LOCHNAGAR2_PIN_CDC_LDOENA 3
+#define LOCHNAGAR2_PIN_SPDIF_HWMODE 4
+#define LOCHNAGAR2_PIN_SPDIF_RESET 5
+#define LOCHNAGAR2_PIN_FPGA_GPIO1 6
+#define LOCHNAGAR2_PIN_FPGA_GPIO2 7
+#define LOCHNAGAR2_PIN_FPGA_GPIO3 8
+#define LOCHNAGAR2_PIN_FPGA_GPIO4 9
+#define LOCHNAGAR2_PIN_FPGA_GPIO5 10
+#define LOCHNAGAR2_PIN_FPGA_GPIO6 11
+#define LOCHNAGAR2_PIN_CDC_GPIO1 12
+#define LOCHNAGAR2_PIN_CDC_GPIO2 13
+#define LOCHNAGAR2_PIN_CDC_GPIO3 14
+#define LOCHNAGAR2_PIN_CDC_GPIO4 15
+#define LOCHNAGAR2_PIN_CDC_GPIO5 16
+#define LOCHNAGAR2_PIN_CDC_GPIO6 17
+#define LOCHNAGAR2_PIN_CDC_GPIO7 18
+#define LOCHNAGAR2_PIN_CDC_GPIO8 19
+#define LOCHNAGAR2_PIN_DSP_GPIO1 20
+#define LOCHNAGAR2_PIN_DSP_GPIO2 21
+#define LOCHNAGAR2_PIN_DSP_GPIO3 22
+#define LOCHNAGAR2_PIN_DSP_GPIO4 23
+#define LOCHNAGAR2_PIN_DSP_GPIO5 24
+#define LOCHNAGAR2_PIN_DSP_GPIO6 25
+#define LOCHNAGAR2_PIN_GF_GPIO2 26
+#define LOCHNAGAR2_PIN_GF_GPIO3 27
+#define LOCHNAGAR2_PIN_GF_GPIO7 28
+#define LOCHNAGAR2_PIN_CDC_AIF1_BCLK 29
+#define LOCHNAGAR2_PIN_CDC_AIF1_RXDAT 30
+#define LOCHNAGAR2_PIN_CDC_AIF1_LRCLK 31
+#define LOCHNAGAR2_PIN_CDC_AIF1_TXDAT 32
+#define LOCHNAGAR2_PIN_CDC_AIF2_BCLK 33
+#define LOCHNAGAR2_PIN_CDC_AIF2_RXDAT 34
+#define LOCHNAGAR2_PIN_CDC_AIF2_LRCLK 35
+#define LOCHNAGAR2_PIN_CDC_AIF2_TXDAT 36
+#define LOCHNAGAR2_PIN_CDC_AIF3_BCLK 37
+#define LOCHNAGAR2_PIN_CDC_AIF3_RXDAT 38
+#define LOCHNAGAR2_PIN_CDC_AIF3_LRCLK 39
+#define LOCHNAGAR2_PIN_CDC_AIF3_TXDAT 40
+#define LOCHNAGAR2_PIN_DSP_AIF1_BCLK 41
+#define LOCHNAGAR2_PIN_DSP_AIF1_RXDAT 42
+#define LOCHNAGAR2_PIN_DSP_AIF1_LRCLK 43
+#define LOCHNAGAR2_PIN_DSP_AIF1_TXDAT 44
+#define LOCHNAGAR2_PIN_DSP_AIF2_BCLK 45
+#define LOCHNAGAR2_PIN_DSP_AIF2_RXDAT 46
+#define LOCHNAGAR2_PIN_DSP_AIF2_LRCLK 47
+#define LOCHNAGAR2_PIN_DSP_AIF2_TXDAT 48
+#define LOCHNAGAR2_PIN_PSIA1_BCLK 49
+#define LOCHNAGAR2_PIN_PSIA1_RXDAT 50
+#define LOCHNAGAR2_PIN_PSIA1_LRCLK 51
+#define LOCHNAGAR2_PIN_PSIA1_TXDAT 52
+#define LOCHNAGAR2_PIN_PSIA2_BCLK 53
+#define LOCHNAGAR2_PIN_PSIA2_RXDAT 54
+#define LOCHNAGAR2_PIN_PSIA2_LRCLK 55
+#define LOCHNAGAR2_PIN_PSIA2_TXDAT 56
+#define LOCHNAGAR2_PIN_GF_AIF3_BCLK 57
+#define LOCHNAGAR2_PIN_GF_AIF3_RXDAT 58
+#define LOCHNAGAR2_PIN_GF_AIF3_LRCLK 59
+#define LOCHNAGAR2_PIN_GF_AIF3_TXDAT 60
+#define LOCHNAGAR2_PIN_GF_AIF4_BCLK 61
+#define LOCHNAGAR2_PIN_GF_AIF4_RXDAT 62
+#define LOCHNAGAR2_PIN_GF_AIF4_LRCLK 63
+#define LOCHNAGAR2_PIN_GF_AIF4_TXDAT 64
+#define LOCHNAGAR2_PIN_GF_AIF1_BCLK 65
+#define LOCHNAGAR2_PIN_GF_AIF1_RXDAT 66
+#define LOCHNAGAR2_PIN_GF_AIF1_LRCLK 67
+#define LOCHNAGAR2_PIN_GF_AIF1_TXDAT 68
+#define LOCHNAGAR2_PIN_GF_AIF2_BCLK 69
+#define LOCHNAGAR2_PIN_GF_AIF2_RXDAT 70
+#define LOCHNAGAR2_PIN_GF_AIF2_LRCLK 71
+#define LOCHNAGAR2_PIN_GF_AIF2_TXDAT 72
+#define LOCHNAGAR2_PIN_DSP_UART1_RX 73
+#define LOCHNAGAR2_PIN_DSP_UART1_TX 74
+#define LOCHNAGAR2_PIN_DSP_UART2_RX 75
+#define LOCHNAGAR2_PIN_DSP_UART2_TX 76
+#define LOCHNAGAR2_PIN_GF_UART2_RX 77
+#define LOCHNAGAR2_PIN_GF_UART2_TX 78
+#define LOCHNAGAR2_PIN_USB_UART_RX 79
+#define LOCHNAGAR2_PIN_CDC_PDMCLK1 80
+#define LOCHNAGAR2_PIN_CDC_PDMDAT1 81
+#define LOCHNAGAR2_PIN_CDC_PDMCLK2 82
+#define LOCHNAGAR2_PIN_CDC_PDMDAT2 83
+#define LOCHNAGAR2_PIN_CDC_DMICCLK1 84
+#define LOCHNAGAR2_PIN_CDC_DMICDAT1 85
+#define LOCHNAGAR2_PIN_CDC_DMICCLK2 86
+#define LOCHNAGAR2_PIN_CDC_DMICDAT2 87
+#define LOCHNAGAR2_PIN_CDC_DMICCLK3 88
+#define LOCHNAGAR2_PIN_CDC_DMICDAT3 89
+#define LOCHNAGAR2_PIN_CDC_DMICCLK4 90
+#define LOCHNAGAR2_PIN_CDC_DMICDAT4 91
+#define LOCHNAGAR2_PIN_DSP_DMICCLK1 92
+#define LOCHNAGAR2_PIN_DSP_DMICDAT1 93
+#define LOCHNAGAR2_PIN_DSP_DMICCLK2 94
+#define LOCHNAGAR2_PIN_DSP_DMICDAT2 95
+#define LOCHNAGAR2_PIN_I2C2_SCL 96
+#define LOCHNAGAR2_PIN_I2C2_SDA 97
+#define LOCHNAGAR2_PIN_I2C3_SCL 98
+#define LOCHNAGAR2_PIN_I2C3_SDA 99
+#define LOCHNAGAR2_PIN_I2C4_SCL 100
+#define LOCHNAGAR2_PIN_I2C4_SDA 101
+#define LOCHNAGAR2_PIN_DSP_STANDBY 102
+#define LOCHNAGAR2_PIN_CDC_MCLK1 103
+#define LOCHNAGAR2_PIN_CDC_MCLK2 104
+#define LOCHNAGAR2_PIN_DSP_CLKIN 105
+#define LOCHNAGAR2_PIN_PSIA1_MCLK 106
+#define LOCHNAGAR2_PIN_PSIA2_MCLK 107
+#define LOCHNAGAR2_PIN_GF_GPIO1 108
+#define LOCHNAGAR2_PIN_GF_GPIO5 109
+#define LOCHNAGAR2_PIN_DSP_GPIO20 110
+#define LOCHNAGAR2_PIN_NUM_GPIOS 111
+
+#endif
--
2.11.0
Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board. Audio system topology,
clocking and power can all be controlled through the Lochnagar
controller chip, allowing the device under test to be used in
a variety of possible use cases.
As the Lochnagar is a fairly complex device this MFD driver
allows the drivers for the various features to be bound
in. Initially clocking, regulator and pinctrl will be added as
these are necessary to configure the system. But in time at least
audio and voltage/current monitoring will also be added.
Signed-off-by: Charles Keepax <[email protected]>
---
Changes since v2:
- Minor updates to the device names bound in for the clock driver
Thanks,
Charles
MAINTAINERS | 13 +
drivers/mfd/Kconfig | 8 +
drivers/mfd/Makefile | 2 +
drivers/mfd/lochnagar-i2c.c | 732 ++++++++++++++++++++++++++++++++++++
include/linux/mfd/lochnagar.h | 48 +++
include/linux/mfd/lochnagar1_regs.h | 157 ++++++++
include/linux/mfd/lochnagar2_regs.h | 253 +++++++++++++
7 files changed, 1213 insertions(+)
create mode 100644 drivers/mfd/lochnagar-i2c.c
create mode 100644 include/linux/mfd/lochnagar.h
create mode 100644 include/linux/mfd/lochnagar1_regs.h
create mode 100644 include/linux/mfd/lochnagar2_regs.h
diff --git a/MAINTAINERS b/MAINTAINERS
index d9e6d86488df4..f1f3494ac5cd8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3557,6 +3557,19 @@ L: [email protected]
S: Maintained
F: drivers/net/ethernet/cirrus/ep93xx_eth.c
+CIRRUS LOGIC LOCHNAGAR DRIVER
+M: Charles Keepax <[email protected]>
+M: Richard Fitzgerald <[email protected]>
+L: [email protected]
+S: Supported
+F: drivers/clk/clk-lochnagar.c
+F: drivers/mfd/lochnagar-i2c.c
+F: drivers/pinctrl/cirrus/pinctrl-lochnagar.c
+F: drivers/regulator/lochnagar-regulator.c
+F: include/dt-bindings/clk/lochnagar.h
+F: include/dt-bindings/pinctrl/lochnagar.h
+F: include/linux/mfd/lochnagar*
+
CISCO FCOE HBA DRIVER
M: Satish Kharat <[email protected]>
M: Sesidhar Baddela <[email protected]>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index f3a5f8d027906..dc64151373714 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1676,6 +1676,14 @@ config MFD_VX855
VIA VX855/VX875 south bridge. You will need to enable the vx855_spi
and/or vx855_gpio drivers for this to do anything useful.
+config MFD_LOCHNAGAR
+ bool "Cirrus Logic Lochnagar Audio Development Board"
+ select MFD_CORE
+ select REGMAP_I2C
+ depends on I2C=y && OF
+ help
+ Support for Cirrus Logic Lochnagar audio development board.
+
config MFD_ARIZONA
select REGMAP
select REGMAP_IRQ
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5856a9489cbd8..f16773cb887ff 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -37,6 +37,8 @@ obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o
obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o
+obj-$(CONFIG_MFD_LOCHNAGAR) += lochnagar-i2c.o
+
obj-$(CONFIG_MFD_ARIZONA) += arizona-core.o
obj-$(CONFIG_MFD_ARIZONA) += arizona-irq.o
obj-$(CONFIG_MFD_ARIZONA_I2C) += arizona-i2c.o
diff --git a/drivers/mfd/lochnagar-i2c.c b/drivers/mfd/lochnagar-i2c.c
new file mode 100644
index 0000000000000..b878a79a49066
--- /dev/null
+++ b/drivers/mfd/lochnagar-i2c.c
@@ -0,0 +1,732 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar I2C bus interface
+ *
+ * Copyright (c) 2012-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lochnagar.h>
+
+#define LOCHNAGAR_BOOT_RETRIES 10
+#define LOCHNAGAR_BOOT_DELAY_MS 350
+
+#define LOCHNAGAR_CONFIG_POLL_US 10000
+
+static bool lochnagar1_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LOCHNAGAR_SOFTWARE_RESET:
+ case LOCHNAGAR_FIRMWARE_ID1:
+ case LOCHNAGAR_FIRMWARE_ID2:
+ case LOCHNAGAR1_CDC_AIF1_SEL:
+ case LOCHNAGAR1_CDC_AIF2_SEL:
+ case LOCHNAGAR1_CDC_AIF3_SEL:
+ case LOCHNAGAR1_CDC_MCLK1_SEL:
+ case LOCHNAGAR1_CDC_MCLK2_SEL:
+ case LOCHNAGAR1_CDC_AIF_CTRL1:
+ case LOCHNAGAR1_CDC_AIF_CTRL2:
+ case LOCHNAGAR1_EXT_AIF_CTRL:
+ case LOCHNAGAR1_DSP_AIF1_SEL:
+ case LOCHNAGAR1_DSP_AIF2_SEL:
+ case LOCHNAGAR1_DSP_CLKIN_SEL:
+ case LOCHNAGAR1_DSP_AIF:
+ case LOCHNAGAR1_GF_AIF1:
+ case LOCHNAGAR1_GF_AIF2:
+ case LOCHNAGAR1_PSIA_AIF:
+ case LOCHNAGAR1_PSIA1_SEL:
+ case LOCHNAGAR1_PSIA2_SEL:
+ case LOCHNAGAR1_SPDIF_AIF_SEL:
+ case LOCHNAGAR1_GF_AIF3_SEL:
+ case LOCHNAGAR1_GF_AIF4_SEL:
+ case LOCHNAGAR1_GF_CLKOUT1_SEL:
+ case LOCHNAGAR1_GF_AIF1_SEL:
+ case LOCHNAGAR1_GF_AIF2_SEL:
+ case LOCHNAGAR1_GF_GPIO2:
+ case LOCHNAGAR1_GF_GPIO3:
+ case LOCHNAGAR1_GF_GPIO7:
+ case LOCHNAGAR1_RST:
+ case LOCHNAGAR1_LED1:
+ case LOCHNAGAR1_LED2:
+ case LOCHNAGAR1_I2C_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default lochnagar1_reg_defaults[] = {
+ { LOCHNAGAR1_CDC_AIF1_SEL, 0x00 },
+ { LOCHNAGAR1_CDC_AIF2_SEL, 0x00 },
+ { LOCHNAGAR1_CDC_AIF3_SEL, 0x00 },
+ { LOCHNAGAR1_CDC_MCLK1_SEL, 0x00 },
+ { LOCHNAGAR1_CDC_MCLK2_SEL, 0x00 },
+ { LOCHNAGAR1_CDC_AIF_CTRL1, 0x00 },
+ { LOCHNAGAR1_CDC_AIF_CTRL2, 0x00 },
+ { LOCHNAGAR1_EXT_AIF_CTRL, 0x00 },
+ { LOCHNAGAR1_DSP_AIF1_SEL, 0x00 },
+ { LOCHNAGAR1_DSP_AIF2_SEL, 0x00 },
+ { LOCHNAGAR1_DSP_CLKIN_SEL, 0x01 },
+ { LOCHNAGAR1_DSP_AIF, 0x08 },
+ { LOCHNAGAR1_GF_AIF1, 0x00 },
+ { LOCHNAGAR1_GF_AIF2, 0x00 },
+ { LOCHNAGAR1_PSIA_AIF, 0x00 },
+ { LOCHNAGAR1_PSIA1_SEL, 0x00 },
+ { LOCHNAGAR1_PSIA2_SEL, 0x00 },
+ { LOCHNAGAR1_SPDIF_AIF_SEL, 0x00 },
+ { LOCHNAGAR1_GF_AIF3_SEL, 0x00 },
+ { LOCHNAGAR1_GF_AIF4_SEL, 0x00 },
+ { LOCHNAGAR1_GF_CLKOUT1_SEL, 0x00 },
+ { LOCHNAGAR1_GF_AIF1_SEL, 0x00 },
+ { LOCHNAGAR1_GF_AIF2_SEL, 0x00 },
+ { LOCHNAGAR1_GF_GPIO2, 0x00 },
+ { LOCHNAGAR1_GF_GPIO3, 0x00 },
+ { LOCHNAGAR1_GF_GPIO7, 0x00 },
+ { LOCHNAGAR1_RST, 0x00 },
+ { LOCHNAGAR1_LED1, 0x00 },
+ { LOCHNAGAR1_LED2, 0x00 },
+ { LOCHNAGAR1_I2C_CTRL, 0x01 },
+};
+
+static const struct regmap_config lochnagar1_i2c_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = 0x50,
+ .readable_reg = lochnagar1_readable_register,
+
+ .use_single_rw = true,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .reg_defaults = lochnagar1_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(lochnagar1_reg_defaults),
+};
+
+static const struct reg_sequence lochnagar1_patch[] = {
+ { 0x40, 0x0083 },
+ { 0x46, 0x0001 },
+ { 0x47, 0x0018 },
+ { 0x50, 0x0000 },
+};
+
+static struct mfd_cell lochnagar1_devs[] = {
+ {
+ .name = "lochnagar-pinctrl",
+ },
+ {
+ .name = "lochnagar1-clk",
+ },
+};
+
+static bool lochnagar2_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LOCHNAGAR_SOFTWARE_RESET:
+ case LOCHNAGAR_FIRMWARE_ID1:
+ case LOCHNAGAR_FIRMWARE_ID2:
+ case LOCHNAGAR2_CDC_AIF1_CTRL:
+ case LOCHNAGAR2_CDC_AIF2_CTRL:
+ case LOCHNAGAR2_CDC_AIF3_CTRL:
+ case LOCHNAGAR2_DSP_AIF1_CTRL:
+ case LOCHNAGAR2_DSP_AIF2_CTRL:
+ case LOCHNAGAR2_PSIA1_CTRL:
+ case LOCHNAGAR2_PSIA2_CTRL:
+ case LOCHNAGAR2_GF_AIF3_CTRL:
+ case LOCHNAGAR2_GF_AIF4_CTRL:
+ case LOCHNAGAR2_GF_AIF1_CTRL:
+ case LOCHNAGAR2_GF_AIF2_CTRL:
+ case LOCHNAGAR2_SPDIF_AIF_CTRL:
+ case LOCHNAGAR2_USB_AIF1_CTRL:
+ case LOCHNAGAR2_USB_AIF2_CTRL:
+ case LOCHNAGAR2_ADAT_AIF_CTRL:
+ case LOCHNAGAR2_CDC_MCLK1_CTRL:
+ case LOCHNAGAR2_CDC_MCLK2_CTRL:
+ case LOCHNAGAR2_DSP_CLKIN_CTRL:
+ case LOCHNAGAR2_PSIA1_MCLK_CTRL:
+ case LOCHNAGAR2_PSIA2_MCLK_CTRL:
+ case LOCHNAGAR2_SPDIF_MCLK_CTRL:
+ case LOCHNAGAR2_GF_CLKOUT1_CTRL:
+ case LOCHNAGAR2_GF_CLKOUT2_CTRL:
+ case LOCHNAGAR2_ADAT_MCLK_CTRL:
+ case LOCHNAGAR2_SOUNDCARD_MCLK_CTRL:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO1:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO2:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO3:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO4:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO5:
+ case LOCHNAGAR2_GPIO_FPGA_GPIO6:
+ case LOCHNAGAR2_GPIO_CDC_GPIO1:
+ case LOCHNAGAR2_GPIO_CDC_GPIO2:
+ case LOCHNAGAR2_GPIO_CDC_GPIO3:
+ case LOCHNAGAR2_GPIO_CDC_GPIO4:
+ case LOCHNAGAR2_GPIO_CDC_GPIO5:
+ case LOCHNAGAR2_GPIO_CDC_GPIO6:
+ case LOCHNAGAR2_GPIO_CDC_GPIO7:
+ case LOCHNAGAR2_GPIO_CDC_GPIO8:
+ case LOCHNAGAR2_GPIO_DSP_GPIO1:
+ case LOCHNAGAR2_GPIO_DSP_GPIO2:
+ case LOCHNAGAR2_GPIO_DSP_GPIO3:
+ case LOCHNAGAR2_GPIO_DSP_GPIO4:
+ case LOCHNAGAR2_GPIO_DSP_GPIO5:
+ case LOCHNAGAR2_GPIO_DSP_GPIO6:
+ case LOCHNAGAR2_GPIO_GF_GPIO2:
+ case LOCHNAGAR2_GPIO_GF_GPIO3:
+ case LOCHNAGAR2_GPIO_GF_GPIO7:
+ case LOCHNAGAR2_GPIO_CDC_AIF1_BCLK:
+ case LOCHNAGAR2_GPIO_CDC_AIF1_RXDAT:
+ case LOCHNAGAR2_GPIO_CDC_AIF1_LRCLK:
+ case LOCHNAGAR2_GPIO_CDC_AIF1_TXDAT:
+ case LOCHNAGAR2_GPIO_CDC_AIF2_BCLK:
+ case LOCHNAGAR2_GPIO_CDC_AIF2_RXDAT:
+ case LOCHNAGAR2_GPIO_CDC_AIF2_LRCLK:
+ case LOCHNAGAR2_GPIO_CDC_AIF2_TXDAT:
+ case LOCHNAGAR2_GPIO_CDC_AIF3_BCLK:
+ case LOCHNAGAR2_GPIO_CDC_AIF3_RXDAT:
+ case LOCHNAGAR2_GPIO_CDC_AIF3_LRCLK:
+ case LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT:
+ case LOCHNAGAR2_GPIO_DSP_AIF1_BCLK:
+ case LOCHNAGAR2_GPIO_DSP_AIF1_RXDAT:
+ case LOCHNAGAR2_GPIO_DSP_AIF1_LRCLK:
+ case LOCHNAGAR2_GPIO_DSP_AIF1_TXDAT:
+ case LOCHNAGAR2_GPIO_DSP_AIF2_BCLK:
+ case LOCHNAGAR2_GPIO_DSP_AIF2_RXDAT:
+ case LOCHNAGAR2_GPIO_DSP_AIF2_LRCLK:
+ case LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT:
+ case LOCHNAGAR2_GPIO_PSIA1_BCLK:
+ case LOCHNAGAR2_GPIO_PSIA1_RXDAT:
+ case LOCHNAGAR2_GPIO_PSIA1_LRCLK:
+ case LOCHNAGAR2_GPIO_PSIA1_TXDAT:
+ case LOCHNAGAR2_GPIO_PSIA2_BCLK:
+ case LOCHNAGAR2_GPIO_PSIA2_RXDAT:
+ case LOCHNAGAR2_GPIO_PSIA2_LRCLK:
+ case LOCHNAGAR2_GPIO_PSIA2_TXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF3_BCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF3_RXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF3_LRCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF3_TXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF4_BCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF4_RXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF4_LRCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF4_TXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF1_BCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF1_RXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF1_LRCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF1_TXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF2_BCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF2_RXDAT:
+ case LOCHNAGAR2_GPIO_GF_AIF2_LRCLK:
+ case LOCHNAGAR2_GPIO_GF_AIF2_TXDAT:
+ case LOCHNAGAR2_GPIO_DSP_UART1_RX:
+ case LOCHNAGAR2_GPIO_DSP_UART1_TX:
+ case LOCHNAGAR2_GPIO_DSP_UART2_RX:
+ case LOCHNAGAR2_GPIO_DSP_UART2_TX:
+ case LOCHNAGAR2_GPIO_GF_UART2_RX:
+ case LOCHNAGAR2_GPIO_GF_UART2_TX:
+ case LOCHNAGAR2_GPIO_USB_UART_RX:
+ case LOCHNAGAR2_GPIO_CDC_PDMCLK1:
+ case LOCHNAGAR2_GPIO_CDC_PDMDAT1:
+ case LOCHNAGAR2_GPIO_CDC_PDMCLK2:
+ case LOCHNAGAR2_GPIO_CDC_PDMDAT2:
+ case LOCHNAGAR2_GPIO_CDC_DMICCLK1:
+ case LOCHNAGAR2_GPIO_CDC_DMICDAT1:
+ case LOCHNAGAR2_GPIO_CDC_DMICCLK2:
+ case LOCHNAGAR2_GPIO_CDC_DMICDAT2:
+ case LOCHNAGAR2_GPIO_CDC_DMICCLK3:
+ case LOCHNAGAR2_GPIO_CDC_DMICDAT3:
+ case LOCHNAGAR2_GPIO_CDC_DMICCLK4:
+ case LOCHNAGAR2_GPIO_CDC_DMICDAT4:
+ case LOCHNAGAR2_GPIO_DSP_DMICCLK1:
+ case LOCHNAGAR2_GPIO_DSP_DMICDAT1:
+ case LOCHNAGAR2_GPIO_DSP_DMICCLK2:
+ case LOCHNAGAR2_GPIO_DSP_DMICDAT2:
+ case LOCHNAGAR2_GPIO_I2C2_SCL:
+ case LOCHNAGAR2_GPIO_I2C2_SDA:
+ case LOCHNAGAR2_GPIO_I2C3_SCL:
+ case LOCHNAGAR2_GPIO_I2C3_SDA:
+ case LOCHNAGAR2_GPIO_I2C4_SCL:
+ case LOCHNAGAR2_GPIO_I2C4_SDA:
+ case LOCHNAGAR2_GPIO_DSP_STANDBY:
+ case LOCHNAGAR2_GPIO_CDC_MCLK1:
+ case LOCHNAGAR2_GPIO_CDC_MCLK2:
+ case LOCHNAGAR2_GPIO_DSP_CLKIN:
+ case LOCHNAGAR2_GPIO_PSIA1_MCLK:
+ case LOCHNAGAR2_GPIO_PSIA2_MCLK:
+ case LOCHNAGAR2_GPIO_GF_GPIO1:
+ case LOCHNAGAR2_GPIO_GF_GPIO5:
+ case LOCHNAGAR2_GPIO_DSP_GPIO20:
+ case LOCHNAGAR2_GPIO_CHANNEL1:
+ case LOCHNAGAR2_GPIO_CHANNEL2:
+ case LOCHNAGAR2_GPIO_CHANNEL3:
+ case LOCHNAGAR2_GPIO_CHANNEL4:
+ case LOCHNAGAR2_GPIO_CHANNEL5:
+ case LOCHNAGAR2_GPIO_CHANNEL6:
+ case LOCHNAGAR2_GPIO_CHANNEL7:
+ case LOCHNAGAR2_GPIO_CHANNEL8:
+ case LOCHNAGAR2_GPIO_CHANNEL9:
+ case LOCHNAGAR2_GPIO_CHANNEL10:
+ case LOCHNAGAR2_GPIO_CHANNEL11:
+ case LOCHNAGAR2_GPIO_CHANNEL12:
+ case LOCHNAGAR2_GPIO_CHANNEL13:
+ case LOCHNAGAR2_GPIO_CHANNEL14:
+ case LOCHNAGAR2_GPIO_CHANNEL15:
+ case LOCHNAGAR2_GPIO_CHANNEL16:
+ case LOCHNAGAR2_MINICARD_RESETS:
+ case LOCHNAGAR2_ANALOGUE_PATH_CTRL1:
+ case LOCHNAGAR2_ANALOGUE_PATH_CTRL2:
+ case LOCHNAGAR2_COMMS_CTRL4:
+ case LOCHNAGAR2_SPDIF_CTRL:
+ case LOCHNAGAR2_POWER_CTRL:
+ case LOCHNAGAR2_MICVDD_CTRL1:
+ case LOCHNAGAR2_MICVDD_CTRL2:
+ case LOCHNAGAR2_VDDCORE_CDC_CTRL1:
+ case LOCHNAGAR2_VDDCORE_CDC_CTRL2:
+ case LOCHNAGAR2_SOUNDCARD_AIF_CTRL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool lochnagar2_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LOCHNAGAR2_GPIO_CHANNEL1:
+ case LOCHNAGAR2_GPIO_CHANNEL2:
+ case LOCHNAGAR2_GPIO_CHANNEL3:
+ case LOCHNAGAR2_GPIO_CHANNEL4:
+ case LOCHNAGAR2_GPIO_CHANNEL5:
+ case LOCHNAGAR2_GPIO_CHANNEL6:
+ case LOCHNAGAR2_GPIO_CHANNEL7:
+ case LOCHNAGAR2_GPIO_CHANNEL8:
+ case LOCHNAGAR2_GPIO_CHANNEL9:
+ case LOCHNAGAR2_GPIO_CHANNEL10:
+ case LOCHNAGAR2_GPIO_CHANNEL11:
+ case LOCHNAGAR2_GPIO_CHANNEL12:
+ case LOCHNAGAR2_GPIO_CHANNEL13:
+ case LOCHNAGAR2_GPIO_CHANNEL14:
+ case LOCHNAGAR2_GPIO_CHANNEL15:
+ case LOCHNAGAR2_GPIO_CHANNEL16:
+ case LOCHNAGAR2_ANALOGUE_PATH_CTRL1:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default lochnagar2_reg_defaults[] = {
+ { LOCHNAGAR2_CDC_AIF1_CTRL, 0x0000 },
+ { LOCHNAGAR2_CDC_AIF2_CTRL, 0x0000 },
+ { LOCHNAGAR2_CDC_AIF3_CTRL, 0x0000 },
+ { LOCHNAGAR2_DSP_AIF1_CTRL, 0x0000 },
+ { LOCHNAGAR2_DSP_AIF2_CTRL, 0x0000 },
+ { LOCHNAGAR2_PSIA1_CTRL, 0x0000 },
+ { LOCHNAGAR2_PSIA2_CTRL, 0x0000 },
+ { LOCHNAGAR2_GF_AIF3_CTRL, 0x0000 },
+ { LOCHNAGAR2_GF_AIF4_CTRL, 0x0000 },
+ { LOCHNAGAR2_GF_AIF1_CTRL, 0x0000 },
+ { LOCHNAGAR2_GF_AIF2_CTRL, 0x0000 },
+ { LOCHNAGAR2_SPDIF_AIF_CTRL, 0x0000 },
+ { LOCHNAGAR2_USB_AIF1_CTRL, 0x0000 },
+ { LOCHNAGAR2_USB_AIF2_CTRL, 0x0000 },
+ { LOCHNAGAR2_ADAT_AIF_CTRL, 0x0000 },
+ { LOCHNAGAR2_CDC_MCLK1_CTRL, 0x0000 },
+ { LOCHNAGAR2_CDC_MCLK2_CTRL, 0x0000 },
+ { LOCHNAGAR2_DSP_CLKIN_CTRL, 0x0000 },
+ { LOCHNAGAR2_PSIA1_MCLK_CTRL, 0x0000 },
+ { LOCHNAGAR2_PSIA2_MCLK_CTRL, 0x0000 },
+ { LOCHNAGAR2_SPDIF_MCLK_CTRL, 0x0000 },
+ { LOCHNAGAR2_GF_CLKOUT1_CTRL, 0x0000 },
+ { LOCHNAGAR2_GF_CLKOUT2_CTRL, 0x0000 },
+ { LOCHNAGAR2_ADAT_MCLK_CTRL, 0x0000 },
+ { LOCHNAGAR2_SOUNDCARD_MCLK_CTRL, 0x0000 },
+ { LOCHNAGAR2_GPIO_FPGA_GPIO1, 0x0000 },
+ { LOCHNAGAR2_GPIO_FPGA_GPIO2, 0x0000 },
+ { LOCHNAGAR2_GPIO_FPGA_GPIO3, 0x0000 },
+ { LOCHNAGAR2_GPIO_FPGA_GPIO4, 0x0000 },
+ { LOCHNAGAR2_GPIO_FPGA_GPIO5, 0x0000 },
+ { LOCHNAGAR2_GPIO_FPGA_GPIO6, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO1, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO2, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO3, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO4, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO5, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO6, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO7, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_GPIO8, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO1, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO2, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO3, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO4, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO5, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO6, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_GPIO2, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_GPIO3, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_GPIO7, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF1_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF1_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF1_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF1_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF2_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF2_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF2_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF2_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF3_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF3_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF3_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF1_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF1_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF1_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF1_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF2_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF2_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF2_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA1_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA1_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA1_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA1_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA2_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA2_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA2_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA2_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF3_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF3_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF3_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF3_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF4_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF4_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF4_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF4_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF1_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF1_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF1_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF1_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF2_BCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF2_RXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF2_LRCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_AIF2_TXDAT, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_UART1_RX, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_UART1_TX, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_UART2_RX, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_UART2_TX, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_UART2_RX, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_UART2_TX, 0x0000 },
+ { LOCHNAGAR2_GPIO_USB_UART_RX, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_PDMCLK1, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_PDMDAT1, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_PDMCLK2, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_PDMDAT2, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICCLK1, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICDAT1, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICCLK2, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICDAT2, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICCLK3, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICDAT3, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICCLK4, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_DMICDAT4, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_DMICCLK1, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_DMICDAT1, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_DMICCLK2, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_DMICDAT2, 0x0000 },
+ { LOCHNAGAR2_GPIO_I2C2_SCL, 0x0000 },
+ { LOCHNAGAR2_GPIO_I2C2_SDA, 0x0000 },
+ { LOCHNAGAR2_GPIO_I2C3_SCL, 0x0000 },
+ { LOCHNAGAR2_GPIO_I2C3_SDA, 0x0000 },
+ { LOCHNAGAR2_GPIO_I2C4_SCL, 0x0000 },
+ { LOCHNAGAR2_GPIO_I2C4_SDA, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_STANDBY, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_MCLK1, 0x0000 },
+ { LOCHNAGAR2_GPIO_CDC_MCLK2, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_CLKIN, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA1_MCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_PSIA2_MCLK, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_GPIO1, 0x0000 },
+ { LOCHNAGAR2_GPIO_GF_GPIO5, 0x0000 },
+ { LOCHNAGAR2_GPIO_DSP_GPIO20, 0x0000 },
+ { LOCHNAGAR2_MINICARD_RESETS, 0x0000 },
+ { LOCHNAGAR2_ANALOGUE_PATH_CTRL2, 0x0000 },
+ { LOCHNAGAR2_COMMS_CTRL4, 0x0001 },
+ { LOCHNAGAR2_SPDIF_CTRL, 0x0008 },
+ { LOCHNAGAR2_POWER_CTRL, 0x0001 },
+ { LOCHNAGAR2_SOUNDCARD_AIF_CTRL, 0x0000 },
+};
+
+static const struct regmap_config lochnagar2_i2c_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = 0x1F1F,
+ .readable_reg = lochnagar2_readable_register,
+ .volatile_reg = lochnagar2_volatile_register,
+
+ .cache_type = REGCACHE_RBTREE,
+
+ .reg_defaults = lochnagar2_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(lochnagar2_reg_defaults),
+};
+
+static const struct reg_sequence lochnagar2_patch[] = {
+ { 0x00EE, 0x0000 },
+ { 0x00F0, 0x0001 },
+};
+
+static struct mfd_cell lochnagar2_devs[] = {
+ {
+ .name = "lochnagar-pinctrl",
+ },
+ {
+ .name = "lochnagar2-clk",
+ },
+ {
+ .name = "lochnagar-regulator",
+ },
+ {
+ .name = "lochnagar2-sound-card",
+ },
+};
+
+static struct lochnagar_config {
+ int id;
+ const char * const name;
+ enum lochnagar_type type;
+ const struct regmap_config *regmap;
+ struct mfd_cell *devs;
+ int ndevs;
+ const struct reg_sequence *patch;
+ int npatch;
+} lochnagar_configs[] = {
+ {
+ .id = 0x50,
+ .name = "lochnagar1",
+ .type = LOCHNAGAR1,
+ .regmap = &lochnagar1_i2c_regmap,
+ .devs = lochnagar1_devs,
+ .ndevs = ARRAY_SIZE(lochnagar1_devs),
+ .patch = lochnagar1_patch,
+ .npatch = ARRAY_SIZE(lochnagar1_patch),
+ },
+ {
+ .id = 0xCB58,
+ .name = "lochnagar2",
+ .type = LOCHNAGAR2,
+ .regmap = &lochnagar2_i2c_regmap,
+ .devs = lochnagar2_devs,
+ .ndevs = ARRAY_SIZE(lochnagar2_devs),
+ .patch = lochnagar2_patch,
+ .npatch = ARRAY_SIZE(lochnagar2_patch),
+ },
+};
+
+static const struct of_device_id lochnagar_of_match[] = {
+ { .compatible = "cirrus,lochnagar1", .data = &lochnagar_configs[0] },
+ { .compatible = "cirrus,lochnagar2", .data = &lochnagar_configs[1] },
+ {},
+};
+
+static int lochnagar_wait_for_boot(struct regmap *regmap, unsigned int *id)
+{
+ int i, ret;
+
+ for (i = 0; i < LOCHNAGAR_BOOT_RETRIES; ++i) {
+ msleep(LOCHNAGAR_BOOT_DELAY_MS);
+
+ ret = regmap_read(regmap, LOCHNAGAR_SOFTWARE_RESET, id);
+ if (!ret)
+ return ret;
+ }
+
+ return -ETIMEDOUT;
+}
+
+int lochnagar_update_config(struct lochnagar *lochnagar)
+{
+ struct regmap *regmap = lochnagar->regmap;
+ unsigned int done = LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK;
+ int timeout_ms = LOCHNAGAR_BOOT_DELAY_MS * LOCHNAGAR_BOOT_RETRIES;
+ unsigned int val = 0;
+ int ret;
+
+ switch (lochnagar->type) {
+ case LOCHNAGAR1:
+ return 0;
+ case LOCHNAGAR2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(regmap, LOCHNAGAR2_ANALOGUE_PATH_CTRL1,
+ LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read_poll_timeout(regmap,
+ LOCHNAGAR2_ANALOGUE_PATH_CTRL1, val,
+ (val & done), LOCHNAGAR_CONFIG_POLL_US,
+ timeout_ms * 1000);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(lochnagar_update_config);
+
+static int lochnagar_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ const struct lochnagar_config *config = NULL;
+ const struct of_device_id *of_id;
+ struct lochnagar *lochnagar;
+ unsigned int val;
+ struct gpio_desc *reset, *present;
+ unsigned int firmwareid;
+ int devid, rev;
+ int ret;
+
+ lochnagar = devm_kzalloc(dev, sizeof(*lochnagar), GFP_KERNEL);
+ if (!lochnagar)
+ return -ENOMEM;
+
+ of_id = of_match_device(lochnagar_of_match, dev);
+ if (!of_id)
+ return -EINVAL;
+
+ config = of_id->data;
+
+ lochnagar->dev = dev;
+ mutex_init(&lochnagar->analogue_config_lock);
+
+ dev_set_drvdata(dev, lochnagar);
+
+ reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset)) {
+ ret = PTR_ERR(reset);
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ return ret;
+ }
+
+ present = devm_gpiod_get_optional(dev, "present", GPIOD_OUT_HIGH);
+ if (IS_ERR(present)) {
+ ret = PTR_ERR(present);
+ dev_err(dev, "Failed to get present GPIO: %d\n", ret);
+ return ret;
+ }
+
+ msleep(20);
+
+ /* Bring Lochnagar out of reset */
+ gpiod_set_value_cansleep(reset, 1);
+
+ /* Identify Lochnagar */
+ lochnagar->type = config->type;
+ lochnagar->regmap = devm_regmap_init_i2c(i2c, config->regmap);
+ if (IS_ERR(lochnagar->regmap)) {
+ ret = PTR_ERR(lochnagar->regmap);
+ dev_err(dev, "Failed to allocate register map: %d\n", ret);
+ return ret;
+ }
+
+ /* Wait for Lochnagar to boot */
+ ret = lochnagar_wait_for_boot(lochnagar->regmap, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device ID: %d\n", ret);
+ return ret;
+ }
+
+ devid = val & LOCHNAGAR_DEVICE_ID_MASK;
+ rev = val & LOCHNAGAR_REV_ID_MASK;
+
+ if (devid != config->id) {
+ dev_err(dev,
+ "ID does not match %s (expected 0x%x got 0x%x)\n",
+ config->name, config->id, devid);
+ return -ENODEV;
+ }
+
+ /* Identify firmware */
+ ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID1, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read firmware id 1: %d\n", ret);
+ return ret;
+ }
+
+ firmwareid = val;
+
+ ret = regmap_read(lochnagar->regmap, LOCHNAGAR_FIRMWARE_ID2, &val);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read firmware id 2: %d\n", ret);
+ return ret;
+ }
+
+ firmwareid |= (val << config->regmap->val_bits);
+
+ dev_info(dev, "Found %s (0x%x) revision %d firmware 0x%.6x\n",
+ config->name, devid, rev + 1, firmwareid);
+
+ ret = regmap_register_patch(lochnagar->regmap, config->patch,
+ config->npatch);
+ if (ret < 0) {
+ dev_err(dev, "Failed to register patch: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_NONE, config->devs,
+ config->ndevs, NULL, 0, NULL);
+ if (ret != 0) {
+ dev_err(dev, "Failed to add subdevices: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_of_platform_populate(dev);
+ if (ret < 0) {
+ dev_err(dev, "Failed to populate child nodes: %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static struct i2c_driver lochnagar_i2c_driver = {
+ .driver = {
+ .name = "lochnagar",
+ .of_match_table = of_match_ptr(lochnagar_of_match),
+ .suppress_bind_attrs = true,
+ },
+
+ .probe_new = lochnagar_i2c_probe,
+};
+
+static int __init lochnagar_i2c_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&lochnagar_i2c_driver);
+ if (ret != 0)
+ pr_err("Failed to register Lochnagar driver: %d\n", ret);
+
+ return ret;
+}
+subsys_initcall(lochnagar_i2c_init);
diff --git a/include/linux/mfd/lochnagar.h b/include/linux/mfd/lochnagar.h
new file mode 100644
index 0000000000000..be485d6714f7c
--- /dev/null
+++ b/include/linux/mfd/lochnagar.h
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Lochnagar internals
+ *
+ * Copyright (c) 2013-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#ifndef CIRRUS_LOCHNAGAR_H
+#define CIRRUS_LOCHNAGAR_H
+
+#include "lochnagar1_regs.h"
+#include "lochnagar2_regs.h"
+
+struct device;
+struct regmap;
+struct mutex;
+
+enum lochnagar_type {
+ LOCHNAGAR1,
+ LOCHNAGAR2,
+};
+
+struct lochnagar {
+ enum lochnagar_type type;
+ struct device *dev;
+ struct regmap *regmap;
+
+ /* Lock to protect updates to the analogue configuration */
+ struct mutex analogue_config_lock;
+};
+
+/* Register Addresses */
+#define LOCHNAGAR_SOFTWARE_RESET 0x00
+#define LOCHNAGAR_FIRMWARE_ID1 0x01
+#define LOCHNAGAR_FIRMWARE_ID2 0x02
+
+/* (0x0000) Software Reset */
+#define LOCHNAGAR_DEVICE_ID_MASK 0xFFFC
+#define LOCHNAGAR_DEVICE_ID_SHIFT 2
+#define LOCHNAGAR_REV_ID_MASK 0x0003
+#define LOCHNAGAR_REV_ID_SHIFT 0
+
+int lochnagar_update_config(struct lochnagar *lochnagar);
+
+#endif
diff --git a/include/linux/mfd/lochnagar1_regs.h b/include/linux/mfd/lochnagar1_regs.h
new file mode 100644
index 0000000000000..114b846245d96
--- /dev/null
+++ b/include/linux/mfd/lochnagar1_regs.h
@@ -0,0 +1,157 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Lochnagar1 register definitions
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#ifndef LOCHNAGAR1_REGISTERS_H
+#define LOCHNAGAR1_REGISTERS_H
+
+/* Register Addresses */
+#define LOCHNAGAR1_CDC_AIF1_SEL 0x0008
+#define LOCHNAGAR1_CDC_AIF2_SEL 0x0009
+#define LOCHNAGAR1_CDC_AIF3_SEL 0x000A
+#define LOCHNAGAR1_CDC_MCLK1_SEL 0x000B
+#define LOCHNAGAR1_CDC_MCLK2_SEL 0x000C
+#define LOCHNAGAR1_CDC_AIF_CTRL1 0x000D
+#define LOCHNAGAR1_CDC_AIF_CTRL2 0x000E
+#define LOCHNAGAR1_EXT_AIF_CTRL 0x000F
+#define LOCHNAGAR1_DSP_AIF1_SEL 0x0010
+#define LOCHNAGAR1_DSP_AIF2_SEL 0x0011
+#define LOCHNAGAR1_DSP_CLKIN_SEL 0x0012
+#define LOCHNAGAR1_DSP_AIF 0x0013
+#define LOCHNAGAR1_GF_AIF1 0x0014
+#define LOCHNAGAR1_GF_AIF2 0x0015
+#define LOCHNAGAR1_PSIA_AIF 0x0016
+#define LOCHNAGAR1_PSIA1_SEL 0x0017
+#define LOCHNAGAR1_PSIA2_SEL 0x0018
+#define LOCHNAGAR1_SPDIF_AIF_SEL 0x0019
+#define LOCHNAGAR1_GF_AIF3_SEL 0x001C
+#define LOCHNAGAR1_GF_AIF4_SEL 0x001D
+#define LOCHNAGAR1_GF_CLKOUT1_SEL 0x001E
+#define LOCHNAGAR1_GF_AIF1_SEL 0x001F
+#define LOCHNAGAR1_GF_AIF2_SEL 0x0020
+#define LOCHNAGAR1_GF_GPIO2 0x0026
+#define LOCHNAGAR1_GF_GPIO3 0x0027
+#define LOCHNAGAR1_GF_GPIO7 0x0028
+#define LOCHNAGAR1_RST 0x0029
+#define LOCHNAGAR1_LED1 0x002A
+#define LOCHNAGAR1_LED2 0x002B
+#define LOCHNAGAR1_I2C_CTRL 0x0046
+
+/*
+ * (0x0008 - 0x000C, 0x0010 - 0x0012, 0x0017 - 0x0020)
+ * CDC_AIF1_SEL - GF_AIF2_SEL
+ */
+#define LOCHNAGAR1_SRC_MASK 0xFF
+#define LOCHNAGAR1_SRC_SHIFT 0
+
+/* (0x000D) CDC_AIF_CTRL1 */
+#define LOCHNAGAR1_CDC_AIF2_LRCLK_DIR_MASK 0x40
+#define LOCHNAGAR1_CDC_AIF2_LRCLK_DIR_SHIFT 6
+#define LOCHNAGAR1_CDC_AIF2_BCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_CDC_AIF2_BCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_CDC_AIF2_ENA_MASK 0x10
+#define LOCHNAGAR1_CDC_AIF2_ENA_SHIFT 4
+#define LOCHNAGAR1_CDC_AIF1_LRCLK_DIR_MASK 0x04
+#define LOCHNAGAR1_CDC_AIF1_LRCLK_DIR_SHIFT 2
+#define LOCHNAGAR1_CDC_AIF1_BCLK_DIR_MASK 0x02
+#define LOCHNAGAR1_CDC_AIF1_BCLK_DIR_SHIFT 1
+#define LOCHNAGAR1_CDC_AIF1_ENA_MASK 0x01
+#define LOCHNAGAR1_CDC_AIF1_ENA_SHIFT 0
+
+/* (0x000E) CDC_AIF_CTRL2 */
+#define LOCHNAGAR1_CDC_AIF3_LRCLK_DIR_MASK 0x40
+#define LOCHNAGAR1_CDC_AIF3_LRCLK_DIR_SHIFT 6
+#define LOCHNAGAR1_CDC_AIF3_BCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_CDC_AIF3_BCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_CDC_AIF3_ENA_MASK 0x10
+#define LOCHNAGAR1_CDC_AIF3_ENA_SHIFT 4
+#define LOCHNAGAR1_CDC_MCLK1_ENA_MASK 0x02
+#define LOCHNAGAR1_CDC_MCLK1_ENA_SHIFT 1
+#define LOCHNAGAR1_CDC_MCLK2_ENA_MASK 0x01
+#define LOCHNAGAR1_CDC_MCLK2_ENA_SHIFT 0
+
+/* (0x000F) EXT_AIF_CTRL */
+#define LOCHNAGAR1_SPDIF_AIF_LRCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_SPDIF_AIF_LRCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_SPDIF_AIF_BCLK_DIR_MASK 0x10
+#define LOCHNAGAR1_SPDIF_AIF_BCLK_DIR_SHIFT 4
+#define LOCHNAGAR1_SPDIF_AIF_ENA_MASK 0x08
+#define LOCHNAGAR1_SPDIF_AIF_ENA_SHIFT 3
+
+/* (0x0013) DSP_AIF */
+#define LOCHNAGAR1_DSP_AIF2_LRCLK_DIR_MASK 0x40
+#define LOCHNAGAR1_DSP_AIF2_LRCLK_DIR_SHIFT 6
+#define LOCHNAGAR1_DSP_AIF2_BCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_DSP_AIF2_BCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_DSP_AIF2_ENA_MASK 0x10
+#define LOCHNAGAR1_DSP_AIF2_ENA_SHIFT 4
+#define LOCHNAGAR1_DSP_CLKIN_ENA_MASK 0x08
+#define LOCHNAGAR1_DSP_CLKIN_ENA_SHIFT 3
+#define LOCHNAGAR1_DSP_AIF1_LRCLK_DIR_MASK 0x04
+#define LOCHNAGAR1_DSP_AIF1_LRCLK_DIR_SHIFT 2
+#define LOCHNAGAR1_DSP_AIF1_BCLK_DIR_MASK 0x02
+#define LOCHNAGAR1_DSP_AIF1_BCLK_DIR_SHIFT 1
+#define LOCHNAGAR1_DSP_AIF1_ENA_MASK 0x01
+#define LOCHNAGAR1_DSP_AIF1_ENA_SHIFT 0
+
+/* (0x0014) GF_AIF1 */
+#define LOCHNAGAR1_GF_CLKOUT1_ENA_MASK 0x40
+#define LOCHNAGAR1_GF_CLKOUT1_ENA_SHIFT 6
+#define LOCHNAGAR1_GF_AIF3_LRCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_GF_AIF3_LRCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_GF_AIF3_BCLK_DIR_MASK 0x10
+#define LOCHNAGAR1_GF_AIF3_BCLK_DIR_SHIFT 4
+#define LOCHNAGAR1_GF_AIF3_ENA_MASK 0x08
+#define LOCHNAGAR1_GF_AIF3_ENA_SHIFT 3
+#define LOCHNAGAR1_GF_AIF1_LRCLK_DIR_MASK 0x04
+#define LOCHNAGAR1_GF_AIF1_LRCLK_DIR_SHIFT 2
+#define LOCHNAGAR1_GF_AIF1_BCLK_DIR_MASK 0x02
+#define LOCHNAGAR1_GF_AIF1_BCLK_DIR_SHIFT 1
+#define LOCHNAGAR1_GF_AIF1_ENA_MASK 0x01
+#define LOCHNAGAR1_GF_AIF1_ENA_SHIFT 0
+
+/* (0x0015) GF_AIF2 */
+#define LOCHNAGAR1_GF_AIF4_LRCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_GF_AIF4_LRCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_GF_AIF4_BCLK_DIR_MASK 0x10
+#define LOCHNAGAR1_GF_AIF4_BCLK_DIR_SHIFT 4
+#define LOCHNAGAR1_GF_AIF4_ENA_MASK 0x08
+#define LOCHNAGAR1_GF_AIF4_ENA_SHIFT 3
+#define LOCHNAGAR1_GF_AIF2_LRCLK_DIR_MASK 0x04
+#define LOCHNAGAR1_GF_AIF2_LRCLK_DIR_SHIFT 2
+#define LOCHNAGAR1_GF_AIF2_BCLK_DIR_MASK 0x02
+#define LOCHNAGAR1_GF_AIF2_BCLK_DIR_SHIFT 1
+#define LOCHNAGAR1_GF_AIF2_ENA_MASK 0x01
+#define LOCHNAGAR1_GF_AIF2_ENA_SHIFT 0
+
+/* (0x0016) PSIA_AIF */
+#define LOCHNAGAR1_PSIA2_LRCLK_DIR_MASK 0x40
+#define LOCHNAGAR1_PSIA2_LRCLK_DIR_SHIFT 6
+#define LOCHNAGAR1_PSIA2_BCLK_DIR_MASK 0x20
+#define LOCHNAGAR1_PSIA2_BCLK_DIR_SHIFT 5
+#define LOCHNAGAR1_PSIA2_ENA_MASK 0x10
+#define LOCHNAGAR1_PSIA2_ENA_SHIFT 4
+#define LOCHNAGAR1_PSIA1_LRCLK_DIR_MASK 0x04
+#define LOCHNAGAR1_PSIA1_LRCLK_DIR_SHIFT 2
+#define LOCHNAGAR1_PSIA1_BCLK_DIR_MASK 0x02
+#define LOCHNAGAR1_PSIA1_BCLK_DIR_SHIFT 1
+#define LOCHNAGAR1_PSIA1_ENA_MASK 0x01
+#define LOCHNAGAR1_PSIA1_ENA_SHIFT 0
+
+/* (0x0029) RST */
+#define LOCHNAGAR1_DSP_RESET_MASK 0x02
+#define LOCHNAGAR1_DSP_RESET_SHIFT 1
+#define LOCHNAGAR1_CDC_RESET_MASK 0x01
+#define LOCHNAGAR1_CDC_RESET_SHIFT 0
+
+/* (0x0046) I2C_CTRL */
+#define LOCHNAGAR1_CDC_CIF_MODE_MASK 0x01
+#define LOCHNAGAR1_CDC_CIF_MODE_SHIFT 0
+
+#endif
diff --git a/include/linux/mfd/lochnagar2_regs.h b/include/linux/mfd/lochnagar2_regs.h
new file mode 100644
index 0000000000000..2c6340c3006b7
--- /dev/null
+++ b/include/linux/mfd/lochnagar2_regs.h
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Lochnagar2 register definitions
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#ifndef LOCHNAGAR2_REGISTERS_H
+#define LOCHNAGAR2_REGISTERS_H
+
+/* Register Addresses */
+#define LOCHNAGAR2_CDC_AIF1_CTRL 0x000D
+#define LOCHNAGAR2_CDC_AIF2_CTRL 0x000E
+#define LOCHNAGAR2_CDC_AIF3_CTRL 0x000F
+#define LOCHNAGAR2_DSP_AIF1_CTRL 0x0010
+#define LOCHNAGAR2_DSP_AIF2_CTRL 0x0011
+#define LOCHNAGAR2_PSIA1_CTRL 0x0012
+#define LOCHNAGAR2_PSIA2_CTRL 0x0013
+#define LOCHNAGAR2_GF_AIF3_CTRL 0x0014
+#define LOCHNAGAR2_GF_AIF4_CTRL 0x0015
+#define LOCHNAGAR2_GF_AIF1_CTRL 0x0016
+#define LOCHNAGAR2_GF_AIF2_CTRL 0x0017
+#define LOCHNAGAR2_SPDIF_AIF_CTRL 0x0018
+#define LOCHNAGAR2_USB_AIF1_CTRL 0x0019
+#define LOCHNAGAR2_USB_AIF2_CTRL 0x001A
+#define LOCHNAGAR2_ADAT_AIF_CTRL 0x001B
+#define LOCHNAGAR2_CDC_MCLK1_CTRL 0x001E
+#define LOCHNAGAR2_CDC_MCLK2_CTRL 0x001F
+#define LOCHNAGAR2_DSP_CLKIN_CTRL 0x0020
+#define LOCHNAGAR2_PSIA1_MCLK_CTRL 0x0021
+#define LOCHNAGAR2_PSIA2_MCLK_CTRL 0x0022
+#define LOCHNAGAR2_SPDIF_MCLK_CTRL 0x0023
+#define LOCHNAGAR2_GF_CLKOUT1_CTRL 0x0024
+#define LOCHNAGAR2_GF_CLKOUT2_CTRL 0x0025
+#define LOCHNAGAR2_ADAT_MCLK_CTRL 0x0026
+#define LOCHNAGAR2_SOUNDCARD_MCLK_CTRL 0x0027
+#define LOCHNAGAR2_GPIO_FPGA_GPIO1 0x0031
+#define LOCHNAGAR2_GPIO_FPGA_GPIO2 0x0032
+#define LOCHNAGAR2_GPIO_FPGA_GPIO3 0x0033
+#define LOCHNAGAR2_GPIO_FPGA_GPIO4 0x0034
+#define LOCHNAGAR2_GPIO_FPGA_GPIO5 0x0035
+#define LOCHNAGAR2_GPIO_FPGA_GPIO6 0x0036
+#define LOCHNAGAR2_GPIO_CDC_GPIO1 0x0037
+#define LOCHNAGAR2_GPIO_CDC_GPIO2 0x0038
+#define LOCHNAGAR2_GPIO_CDC_GPIO3 0x0039
+#define LOCHNAGAR2_GPIO_CDC_GPIO4 0x003A
+#define LOCHNAGAR2_GPIO_CDC_GPIO5 0x003B
+#define LOCHNAGAR2_GPIO_CDC_GPIO6 0x003C
+#define LOCHNAGAR2_GPIO_CDC_GPIO7 0x003D
+#define LOCHNAGAR2_GPIO_CDC_GPIO8 0x003E
+#define LOCHNAGAR2_GPIO_DSP_GPIO1 0x003F
+#define LOCHNAGAR2_GPIO_DSP_GPIO2 0x0040
+#define LOCHNAGAR2_GPIO_DSP_GPIO3 0x0041
+#define LOCHNAGAR2_GPIO_DSP_GPIO4 0x0042
+#define LOCHNAGAR2_GPIO_DSP_GPIO5 0x0043
+#define LOCHNAGAR2_GPIO_DSP_GPIO6 0x0044
+#define LOCHNAGAR2_GPIO_GF_GPIO2 0x0045
+#define LOCHNAGAR2_GPIO_GF_GPIO3 0x0046
+#define LOCHNAGAR2_GPIO_GF_GPIO7 0x0047
+#define LOCHNAGAR2_GPIO_CDC_AIF1_BCLK 0x0048
+#define LOCHNAGAR2_GPIO_CDC_AIF1_RXDAT 0x0049
+#define LOCHNAGAR2_GPIO_CDC_AIF1_LRCLK 0x004A
+#define LOCHNAGAR2_GPIO_CDC_AIF1_TXDAT 0x004B
+#define LOCHNAGAR2_GPIO_CDC_AIF2_BCLK 0x004C
+#define LOCHNAGAR2_GPIO_CDC_AIF2_RXDAT 0x004D
+#define LOCHNAGAR2_GPIO_CDC_AIF2_LRCLK 0x004E
+#define LOCHNAGAR2_GPIO_CDC_AIF2_TXDAT 0x004F
+#define LOCHNAGAR2_GPIO_CDC_AIF3_BCLK 0x0050
+#define LOCHNAGAR2_GPIO_CDC_AIF3_RXDAT 0x0051
+#define LOCHNAGAR2_GPIO_CDC_AIF3_LRCLK 0x0052
+#define LOCHNAGAR2_GPIO_CDC_AIF3_TXDAT 0x0053
+#define LOCHNAGAR2_GPIO_DSP_AIF1_BCLK 0x0054
+#define LOCHNAGAR2_GPIO_DSP_AIF1_RXDAT 0x0055
+#define LOCHNAGAR2_GPIO_DSP_AIF1_LRCLK 0x0056
+#define LOCHNAGAR2_GPIO_DSP_AIF1_TXDAT 0x0057
+#define LOCHNAGAR2_GPIO_DSP_AIF2_BCLK 0x0058
+#define LOCHNAGAR2_GPIO_DSP_AIF2_RXDAT 0x0059
+#define LOCHNAGAR2_GPIO_DSP_AIF2_LRCLK 0x005A
+#define LOCHNAGAR2_GPIO_DSP_AIF2_TXDAT 0x005B
+#define LOCHNAGAR2_GPIO_PSIA1_BCLK 0x005C
+#define LOCHNAGAR2_GPIO_PSIA1_RXDAT 0x005D
+#define LOCHNAGAR2_GPIO_PSIA1_LRCLK 0x005E
+#define LOCHNAGAR2_GPIO_PSIA1_TXDAT 0x005F
+#define LOCHNAGAR2_GPIO_PSIA2_BCLK 0x0060
+#define LOCHNAGAR2_GPIO_PSIA2_RXDAT 0x0061
+#define LOCHNAGAR2_GPIO_PSIA2_LRCLK 0x0062
+#define LOCHNAGAR2_GPIO_PSIA2_TXDAT 0x0063
+#define LOCHNAGAR2_GPIO_GF_AIF3_BCLK 0x0064
+#define LOCHNAGAR2_GPIO_GF_AIF3_RXDAT 0x0065
+#define LOCHNAGAR2_GPIO_GF_AIF3_LRCLK 0x0066
+#define LOCHNAGAR2_GPIO_GF_AIF3_TXDAT 0x0067
+#define LOCHNAGAR2_GPIO_GF_AIF4_BCLK 0x0068
+#define LOCHNAGAR2_GPIO_GF_AIF4_RXDAT 0x0069
+#define LOCHNAGAR2_GPIO_GF_AIF4_LRCLK 0x006A
+#define LOCHNAGAR2_GPIO_GF_AIF4_TXDAT 0x006B
+#define LOCHNAGAR2_GPIO_GF_AIF1_BCLK 0x006C
+#define LOCHNAGAR2_GPIO_GF_AIF1_RXDAT 0x006D
+#define LOCHNAGAR2_GPIO_GF_AIF1_LRCLK 0x006E
+#define LOCHNAGAR2_GPIO_GF_AIF1_TXDAT 0x006F
+#define LOCHNAGAR2_GPIO_GF_AIF2_BCLK 0x0070
+#define LOCHNAGAR2_GPIO_GF_AIF2_RXDAT 0x0071
+#define LOCHNAGAR2_GPIO_GF_AIF2_LRCLK 0x0072
+#define LOCHNAGAR2_GPIO_GF_AIF2_TXDAT 0x0073
+#define LOCHNAGAR2_GPIO_DSP_UART1_RX 0x0074
+#define LOCHNAGAR2_GPIO_DSP_UART1_TX 0x0075
+#define LOCHNAGAR2_GPIO_DSP_UART2_RX 0x0076
+#define LOCHNAGAR2_GPIO_DSP_UART2_TX 0x0077
+#define LOCHNAGAR2_GPIO_GF_UART2_RX 0x0078
+#define LOCHNAGAR2_GPIO_GF_UART2_TX 0x0079
+#define LOCHNAGAR2_GPIO_USB_UART_RX 0x007A
+#define LOCHNAGAR2_GPIO_CDC_PDMCLK1 0x007C
+#define LOCHNAGAR2_GPIO_CDC_PDMDAT1 0x007D
+#define LOCHNAGAR2_GPIO_CDC_PDMCLK2 0x007E
+#define LOCHNAGAR2_GPIO_CDC_PDMDAT2 0x007F
+#define LOCHNAGAR2_GPIO_CDC_DMICCLK1 0x0080
+#define LOCHNAGAR2_GPIO_CDC_DMICDAT1 0x0081
+#define LOCHNAGAR2_GPIO_CDC_DMICCLK2 0x0082
+#define LOCHNAGAR2_GPIO_CDC_DMICDAT2 0x0083
+#define LOCHNAGAR2_GPIO_CDC_DMICCLK3 0x0084
+#define LOCHNAGAR2_GPIO_CDC_DMICDAT3 0x0085
+#define LOCHNAGAR2_GPIO_CDC_DMICCLK4 0x0086
+#define LOCHNAGAR2_GPIO_CDC_DMICDAT4 0x0087
+#define LOCHNAGAR2_GPIO_DSP_DMICCLK1 0x0088
+#define LOCHNAGAR2_GPIO_DSP_DMICDAT1 0x0089
+#define LOCHNAGAR2_GPIO_DSP_DMICCLK2 0x008A
+#define LOCHNAGAR2_GPIO_DSP_DMICDAT2 0x008B
+#define LOCHNAGAR2_GPIO_I2C2_SCL 0x008C
+#define LOCHNAGAR2_GPIO_I2C2_SDA 0x008D
+#define LOCHNAGAR2_GPIO_I2C3_SCL 0x008E
+#define LOCHNAGAR2_GPIO_I2C3_SDA 0x008F
+#define LOCHNAGAR2_GPIO_I2C4_SCL 0x0090
+#define LOCHNAGAR2_GPIO_I2C4_SDA 0x0091
+#define LOCHNAGAR2_GPIO_DSP_STANDBY 0x0092
+#define LOCHNAGAR2_GPIO_CDC_MCLK1 0x0093
+#define LOCHNAGAR2_GPIO_CDC_MCLK2 0x0094
+#define LOCHNAGAR2_GPIO_DSP_CLKIN 0x0095
+#define LOCHNAGAR2_GPIO_PSIA1_MCLK 0x0096
+#define LOCHNAGAR2_GPIO_PSIA2_MCLK 0x0097
+#define LOCHNAGAR2_GPIO_GF_GPIO1 0x0098
+#define LOCHNAGAR2_GPIO_GF_GPIO5 0x0099
+#define LOCHNAGAR2_GPIO_DSP_GPIO20 0x009A
+#define LOCHNAGAR2_GPIO_CHANNEL1 0x00B9
+#define LOCHNAGAR2_GPIO_CHANNEL2 0x00BA
+#define LOCHNAGAR2_GPIO_CHANNEL3 0x00BB
+#define LOCHNAGAR2_GPIO_CHANNEL4 0x00BC
+#define LOCHNAGAR2_GPIO_CHANNEL5 0x00BD
+#define LOCHNAGAR2_GPIO_CHANNEL6 0x00BE
+#define LOCHNAGAR2_GPIO_CHANNEL7 0x00BF
+#define LOCHNAGAR2_GPIO_CHANNEL8 0x00C0
+#define LOCHNAGAR2_GPIO_CHANNEL9 0x00C1
+#define LOCHNAGAR2_GPIO_CHANNEL10 0x00C2
+#define LOCHNAGAR2_GPIO_CHANNEL11 0x00C3
+#define LOCHNAGAR2_GPIO_CHANNEL12 0x00C4
+#define LOCHNAGAR2_GPIO_CHANNEL13 0x00C5
+#define LOCHNAGAR2_GPIO_CHANNEL14 0x00C6
+#define LOCHNAGAR2_GPIO_CHANNEL15 0x00C7
+#define LOCHNAGAR2_GPIO_CHANNEL16 0x00C8
+#define LOCHNAGAR2_MINICARD_RESETS 0x00DF
+#define LOCHNAGAR2_ANALOGUE_PATH_CTRL1 0x00E3
+#define LOCHNAGAR2_ANALOGUE_PATH_CTRL2 0x00E4
+#define LOCHNAGAR2_COMMS_CTRL4 0x00F0
+#define LOCHNAGAR2_SPDIF_CTRL 0x00FE
+#define LOCHNAGAR2_POWER_CTRL 0x0116
+#define LOCHNAGAR2_MICVDD_CTRL1 0x0119
+#define LOCHNAGAR2_MICVDD_CTRL2 0x011B
+#define LOCHNAGAR2_VDDCORE_CDC_CTRL1 0x011E
+#define LOCHNAGAR2_VDDCORE_CDC_CTRL2 0x0120
+#define LOCHNAGAR2_SOUNDCARD_AIF_CTRL 0x0180
+
+/* (0x000D-0x001B, 0x0180) CDC_AIF1_CTRL - SOUNCARD_AIF_CTRL */
+#define LOCHNAGAR2_AIF_ENA_MASK 0x8000
+#define LOCHNAGAR2_AIF_ENA_SHIFT 15
+#define LOCHNAGAR2_AIF_LRCLK_DIR_MASK 0x4000
+#define LOCHNAGAR2_AIF_LRCLK_DIR_SHIFT 14
+#define LOCHNAGAR2_AIF_BCLK_DIR_MASK 0x2000
+#define LOCHNAGAR2_AIF_BCLK_DIR_SHIFT 13
+#define LOCHNAGAR2_AIF_SRC_MASK 0x00FF
+#define LOCHNAGAR2_AIF_SRC_SHIFT 0
+
+/* (0x001E - 0x0027) CDC_MCLK1_CTRL - SOUNDCARD_MCLK_CTRL */
+#define LOCHNAGAR2_CLK_ENA_MASK 0x8000
+#define LOCHNAGAR2_CLK_ENA_SHIFT 15
+#define LOCHNAGAR2_CLK_DIR_MASK 0x4000
+#define LOCHNAGAR2_CLK_DIR_SHIFT 14
+#define LOCHNAGAR2_CLK_SRC_MASK 0x00FF
+#define LOCHNAGAR2_CLK_SRC_SHIFT 0
+
+/* (0x0031 - 0x009A) GPIO_FPGA_GPIO1 - GPIO_DSP_GPIO20 */
+#define LOCHNAGAR2_GPIO_SRC_MASK 0x00FF
+#define LOCHNAGAR2_GPIO_SRC_SHIFT 0
+
+/* (0x00B9 - 0x00C8) GPIO_CHANNEL1 - GPIO_CHANNEL16 */
+#define LOCHNAGAR2_GPIO_CHANNEL_STS_MASK 0x8000
+#define LOCHNAGAR2_GPIO_CHANNEL_STS_SHIFT 15
+#define LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK 0x00FF
+#define LOCHNAGAR2_GPIO_CHANNEL_SRC_SHIFT 0
+
+/* (0x00DF) MINICARD_RESETS */
+#define LOCHNAGAR2_DSP_RESET_MASK 0x0002
+#define LOCHNAGAR2_DSP_RESET_SHIFT 1
+#define LOCHNAGAR2_CDC_RESET_MASK 0x0001
+#define LOCHNAGAR2_CDC_RESET_SHIFT 0
+
+/* (0x00E3) ANALOGUE_PATH_CTRL1 */
+#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_MASK 0x8000
+#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_SHIFT 15
+#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_MASK 0x4000
+#define LOCHNAGAR2_ANALOGUE_PATH_UPDATE_STS_SHIFT 14
+
+/* (0x00E4) ANALOGUE_PATH_CTRL2 */
+#define LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK 0x0080
+#define LOCHNAGAR2_P2_INPUT_BIAS_ENA_SHIFT 7
+#define LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK 0x0040
+#define LOCHNAGAR2_P1_INPUT_BIAS_ENA_SHIFT 6
+#define LOCHNAGAR2_P2_MICBIAS_SRC_MASK 0x0038
+#define LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT 3
+#define LOCHNAGAR2_P1_MICBIAS_SRC_MASK 0x0007
+#define LOCHNAGAR2_P1_MICBIAS_SRC_SHIFT 0
+
+/* (0x00F0) COMMS_CTRL4 */
+#define LOCHNAGAR2_CDC_CIF1MODE_MASK 0x0001
+#define LOCHNAGAR2_CDC_CIF1MODE_SHIFT 0
+
+/* (0x00FE) SPDIF_CTRL */
+#define LOCHNAGAR2_SPDIF_HWMODE_MASK 0x0008
+#define LOCHNAGAR2_SPDIF_HWMODE_SHIFT 3
+#define LOCHNAGAR2_SPDIF_RESET_MASK 0x0001
+#define LOCHNAGAR2_SPDIF_RESET_SHIFT 0
+
+/* (0x0116) POWER_CTRL */
+#define LOCHNAGAR2_PWR_ENA_MASK 0x0001
+#define LOCHNAGAR2_PWR_ENA_SHIFT 0
+
+/* (0x0119) MICVDD_CTRL1 */
+#define LOCHNAGAR2_MICVDD_REG_ENA_MASK 0x8000
+#define LOCHNAGAR2_MICVDD_REG_ENA_SHIFT 15
+
+/* (0x011B) MICVDD_CTRL2 */
+#define LOCHNAGAR2_MICVDD_VSEL_MASK 0x001F
+#define LOCHNAGAR2_MICVDD_VSEL_SHIFT 0
+
+/* (0x011E) VDDCORE_CDC_CTRL1 */
+#define LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK 0x8000
+#define LOCHNAGAR2_VDDCORE_CDC_REG_ENA_SHIFT 15
+
+/* (0x0120) VDDCORE_CDC_CTRL2 */
+#define LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK 0x007F
+#define LOCHNAGAR2_VDDCORE_CDC_VSEL_SHIFT 0
+
+#endif
--
2.11.0
Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board.
Lochnagar provides many pins which can generally be used for an
audio function such as an AIF or a PDM interface, but also as
GPIOs.
Signed-off-by: Charles Keepax <[email protected]>
---
Changes since v2:
- Updated aif-master/aif-slave to use a vendor prefix
- Correctly get and put of_node pointer in probe/remove
Thanks,
Charles
drivers/pinctrl/cirrus/Kconfig | 10 +
drivers/pinctrl/cirrus/Makefile | 2 +
drivers/pinctrl/cirrus/pinctrl-lochnagar.c | 1167 ++++++++++++++++++++++++++++
drivers/pinctrl/cirrus/pinctrl-lochnagar.h | 112 +++
4 files changed, 1291 insertions(+)
create mode 100644 drivers/pinctrl/cirrus/pinctrl-lochnagar.c
create mode 100644 drivers/pinctrl/cirrus/pinctrl-lochnagar.h
diff --git a/drivers/pinctrl/cirrus/Kconfig b/drivers/pinctrl/cirrus/Kconfig
index 27013e5949bc1..74af07e251741 100644
--- a/drivers/pinctrl/cirrus/Kconfig
+++ b/drivers/pinctrl/cirrus/Kconfig
@@ -1,3 +1,13 @@
+config PINCTRL_LOCHNAGAR
+ tristate "Cirrus Logic Lochnagar pinctrl driver"
+ depends on MFD_LOCHNAGAR
+ select PINMUX
+ select PINCONF
+ select GENERIC_PINCONF
+ help
+ This driver supports configuring the GPIO and other pin configuration
+ of the Cirrus Logic Lochnagar audio development board.
+
# This is all selected by the Madera MFD driver Kconfig options
config PINCTRL_MADERA
tristate
diff --git a/drivers/pinctrl/cirrus/Makefile b/drivers/pinctrl/cirrus/Makefile
index 6e4938cde9e30..20baebf438f62 100644
--- a/drivers/pinctrl/cirrus/Makefile
+++ b/drivers/pinctrl/cirrus/Makefile
@@ -1,4 +1,6 @@
# Cirrus Logic pinctrl drivers
+obj-$(CONFIG_PINCTRL_LOCHNAGAR) += pinctrl-lochnagar.o
+
pinctrl-madera-objs := pinctrl-madera-core.o
ifeq ($(CONFIG_PINCTRL_CS47L35),y)
pinctrl-madera-objs += pinctrl-cs47l35.o
diff --git a/drivers/pinctrl/cirrus/pinctrl-lochnagar.c b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c
new file mode 100644
index 0000000000000..d5dc8362cc48d
--- /dev/null
+++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c
@@ -0,0 +1,1167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar pin and GPIO control
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gpio/driver.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+
+#include <linux/mfd/lochnagar.h>
+
+#include "../pinctrl-utils.h"
+#include "pinctrl-lochnagar.h"
+
+#define LN_PIN_GPIO(REV, ID, NAME, REG, SHIFT, INVERT) \
+static const struct lochnagar_pin lochnagar##REV##_##ID##_pin = { \
+ .name = NAME, .type = LN_PTYPE_GPIO, .reg = LOCHNAGAR##REV##_##REG, \
+ .shift = LOCHNAGAR##REV##_##SHIFT##_SHIFT, .invert = INVERT, \
+}
+
+#define LN_PIN_SAIF(REV, ID, NAME) \
+static const struct lochnagar_pin lochnagar##REV##_##ID##_pin = \
+ { .name = NAME, .type = LN_PTYPE_AIF, }
+
+#define LN_PIN_AIF(REV, ID) \
+ LN_PIN_SAIF(REV, ID##_BCLK, LN_##ID##_STR"-bclk"); \
+ LN_PIN_SAIF(REV, ID##_LRCLK, LN_##ID##_STR"-lrclk"); \
+ LN_PIN_SAIF(REV, ID##_RXDAT, LN_##ID##_STR"-rxdat"); \
+ LN_PIN_SAIF(REV, ID##_TXDAT, LN_##ID##_STR"-txdat")
+
+#define LN1_PIN_GPIO(ID, NAME, REG, SHIFT, INVERT) \
+ LN_PIN_GPIO(1, ID, NAME, REG, SHIFT, INVERT)
+
+#define LN1_PIN_MUX(ID, NAME) \
+static const struct lochnagar_pin lochnagar1_##ID##_pin = \
+ { .name = NAME, .type = LN_PTYPE_MUX, .reg = LOCHNAGAR1_##ID, }
+
+#define LN1_PIN_AIF(ID) LN_PIN_AIF(1, ID)
+
+#define LN2_PIN_GPIO(ID, NAME, REG, SHIFT, INVERT) \
+ LN_PIN_GPIO(2, ID, NAME, REG, SHIFT, INVERT)
+
+#define LN2_PIN_MUX(ID, NAME) \
+static const struct lochnagar_pin lochnagar2_##ID##_pin = \
+ { .name = NAME, .type = LN_PTYPE_MUX, .reg = LOCHNAGAR2_GPIO_##ID, }
+
+#define LN2_PIN_AIF(ID) LN_PIN_AIF(2, ID)
+
+#define LN2_PIN_GAI(ID) \
+ LN2_PIN_MUX(ID##_BCLK, LN_##ID##_STR"-bclk"); \
+ LN2_PIN_MUX(ID##_LRCLK, LN_##ID##_STR"-lrclk"); \
+ LN2_PIN_MUX(ID##_RXDAT, LN_##ID##_STR"-rxdat"); \
+ LN2_PIN_MUX(ID##_TXDAT, LN_##ID##_STR"-txdat")
+
+#define LN_PIN(REV, ID) [LOCHNAGAR##REV##_PIN_##ID] = { \
+ .number = LOCHNAGAR##REV##_PIN_##ID, \
+ .name = lochnagar##REV##_##ID##_pin.name, \
+ .drv_data = (void *)&lochnagar##REV##_##ID##_pin, \
+}
+
+#define LN1_PIN(ID) LN_PIN(1, ID)
+#define LN2_PIN(ID) LN_PIN(2, ID)
+
+#define LN_PINS(REV, ID) \
+ LN_PIN(REV, ID##_BCLK), LN_PIN(REV, ID##_LRCLK), \
+ LN_PIN(REV, ID##_RXDAT), LN_PIN(REV, ID##_TXDAT)
+
+#define LN1_PINS(ID) LN_PINS(1, ID)
+#define LN2_PINS(ID) LN_PINS(2, ID)
+
+enum lochnagar_pin_type {
+ LN_PTYPE_GPIO,
+ LN_PTYPE_MUX,
+ LN_PTYPE_AIF,
+ LN_PTYPE_COUNT,
+};
+
+struct lochnagar_pin {
+ const char name[20];
+
+ enum lochnagar_pin_type type;
+
+ unsigned int reg;
+ int shift;
+ bool invert;
+};
+
+LN1_PIN_GPIO(CDC_RESET, "codec-reset", RST, CDC_RESET, 1);
+LN1_PIN_GPIO(DSP_RESET, "dsp-reset", RST, DSP_RESET, 1);
+LN1_PIN_GPIO(CDC_CIF1MODE, "codec-cif1mode", I2C_CTRL, CDC_CIF_MODE, 0);
+LN1_PIN_MUX(GF_GPIO2, "gf-gpio2");
+LN1_PIN_MUX(GF_GPIO3, "gf-gpio3");
+LN1_PIN_MUX(GF_GPIO7, "gf-gpio7");
+LN1_PIN_MUX(LED1, "led1");
+LN1_PIN_MUX(LED2, "led2");
+LN1_PIN_AIF(CDC_AIF1);
+LN1_PIN_AIF(CDC_AIF2);
+LN1_PIN_AIF(CDC_AIF3);
+LN1_PIN_AIF(DSP_AIF1);
+LN1_PIN_AIF(DSP_AIF2);
+LN1_PIN_AIF(PSIA1);
+LN1_PIN_AIF(PSIA2);
+LN1_PIN_AIF(SPDIF_AIF);
+LN1_PIN_AIF(GF_AIF1);
+LN1_PIN_AIF(GF_AIF2);
+LN1_PIN_AIF(GF_AIF3);
+LN1_PIN_AIF(GF_AIF4);
+
+LN2_PIN_GPIO(CDC_RESET, "codec-reset", MINICARD_RESETS, CDC_RESET, 1);
+LN2_PIN_GPIO(DSP_RESET, "dsp-reset", MINICARD_RESETS, DSP_RESET, 1);
+LN2_PIN_GPIO(CDC_CIF1MODE, "codec-cif1mode", COMMS_CTRL4, CDC_CIF1MODE, 0);
+LN2_PIN_GPIO(CDC_LDOENA, "codec-ldoena", POWER_CTRL, PWR_ENA, 0);
+LN2_PIN_GPIO(SPDIF_HWMODE, "spdif-hwmode", SPDIF_CTRL, SPDIF_HWMODE, 0);
+LN2_PIN_GPIO(SPDIF_RESET, "spdif-reset", SPDIF_CTRL, SPDIF_RESET, 1);
+LN2_PIN_MUX(FPGA_GPIO1, "fgpa-gpio1");
+LN2_PIN_MUX(FPGA_GPIO2, "fgpa-gpio2");
+LN2_PIN_MUX(FPGA_GPIO3, "fgpa-gpio3");
+LN2_PIN_MUX(FPGA_GPIO4, "fgpa-gpio4");
+LN2_PIN_MUX(FPGA_GPIO5, "fgpa-gpio5");
+LN2_PIN_MUX(FPGA_GPIO6, "fgpa-gpio6");
+LN2_PIN_MUX(CDC_GPIO1, "codec-gpio1");
+LN2_PIN_MUX(CDC_GPIO2, "codec-gpio2");
+LN2_PIN_MUX(CDC_GPIO3, "codec-gpio3");
+LN2_PIN_MUX(CDC_GPIO4, "codec-gpio4");
+LN2_PIN_MUX(CDC_GPIO5, "codec-gpio5");
+LN2_PIN_MUX(CDC_GPIO6, "codec-gpio6");
+LN2_PIN_MUX(CDC_GPIO7, "codec-gpio7");
+LN2_PIN_MUX(CDC_GPIO8, "codec-gpio8");
+LN2_PIN_MUX(DSP_GPIO1, "dsp-gpio1");
+LN2_PIN_MUX(DSP_GPIO2, "dsp-gpio2");
+LN2_PIN_MUX(DSP_GPIO3, "dsp-gpio3");
+LN2_PIN_MUX(DSP_GPIO4, "dsp-gpio4");
+LN2_PIN_MUX(DSP_GPIO5, "dsp-gpio5");
+LN2_PIN_MUX(DSP_GPIO6, "dsp-gpio6");
+LN2_PIN_MUX(GF_GPIO2, "gf-gpio2");
+LN2_PIN_MUX(GF_GPIO3, "gf-gpio3");
+LN2_PIN_MUX(GF_GPIO7, "gf-gpio7");
+LN2_PIN_MUX(DSP_UART1_RX, "dsp-uart1-rx");
+LN2_PIN_MUX(DSP_UART1_TX, "dsp-uart1-tx");
+LN2_PIN_MUX(DSP_UART2_RX, "dsp-uart2-rx");
+LN2_PIN_MUX(DSP_UART2_TX, "dsp-uart2-tx");
+LN2_PIN_MUX(GF_UART2_RX, "gf-uart2-rx");
+LN2_PIN_MUX(GF_UART2_TX, "gf-uart2-tx");
+LN2_PIN_MUX(USB_UART_RX, "usb-uart-rx");
+LN2_PIN_MUX(CDC_PDMCLK1, "codec-pdmclk1");
+LN2_PIN_MUX(CDC_PDMDAT1, "codec-pdmdat1");
+LN2_PIN_MUX(CDC_PDMCLK2, "codec-pdmclk2");
+LN2_PIN_MUX(CDC_PDMDAT2, "codec-pdmdat2");
+LN2_PIN_MUX(CDC_DMICCLK1, "codec-dmicclk1");
+LN2_PIN_MUX(CDC_DMICDAT1, "codec-dmicdat1");
+LN2_PIN_MUX(CDC_DMICCLK2, "codec-dmicclk2");
+LN2_PIN_MUX(CDC_DMICDAT2, "codec-dmicdat2");
+LN2_PIN_MUX(CDC_DMICCLK3, "codec-dmicclk3");
+LN2_PIN_MUX(CDC_DMICDAT3, "codec-dmicdat3");
+LN2_PIN_MUX(CDC_DMICCLK4, "codec-dmicclk4");
+LN2_PIN_MUX(CDC_DMICDAT4, "codec-dmicdat4");
+LN2_PIN_MUX(DSP_DMICCLK1, "dsp-dmicclk1");
+LN2_PIN_MUX(DSP_DMICDAT1, "dsp-dmicdat1");
+LN2_PIN_MUX(DSP_DMICCLK2, "dsp-dmicclk2");
+LN2_PIN_MUX(DSP_DMICDAT2, "dsp-dmicdat2");
+LN2_PIN_MUX(I2C2_SCL, "i2c2-scl");
+LN2_PIN_MUX(I2C2_SDA, "i2c2-sda");
+LN2_PIN_MUX(I2C3_SCL, "i2c3-scl");
+LN2_PIN_MUX(I2C3_SDA, "i2c3-sda");
+LN2_PIN_MUX(I2C4_SCL, "i2c4-scl");
+LN2_PIN_MUX(I2C4_SDA, "i2c4-sda");
+LN2_PIN_MUX(DSP_STANDBY, "dsp-standby");
+LN2_PIN_MUX(CDC_MCLK1, "codec-mclk1");
+LN2_PIN_MUX(CDC_MCLK2, "codec-mclk2");
+LN2_PIN_MUX(DSP_CLKIN, "dsp-clkin");
+LN2_PIN_MUX(PSIA1_MCLK, "psia1-mclk");
+LN2_PIN_MUX(PSIA2_MCLK, "psia2-mclk");
+LN2_PIN_MUX(GF_GPIO1, "gf-gpio1");
+LN2_PIN_MUX(GF_GPIO5, "gf-gpio5");
+LN2_PIN_MUX(DSP_GPIO20, "dsp-gpio20");
+LN2_PIN_GAI(CDC_AIF1);
+LN2_PIN_GAI(CDC_AIF2);
+LN2_PIN_GAI(CDC_AIF3);
+LN2_PIN_GAI(DSP_AIF1);
+LN2_PIN_GAI(DSP_AIF2);
+LN2_PIN_GAI(PSIA1);
+LN2_PIN_GAI(PSIA2);
+LN2_PIN_GAI(GF_AIF1);
+LN2_PIN_GAI(GF_AIF2);
+LN2_PIN_GAI(GF_AIF3);
+LN2_PIN_GAI(GF_AIF4);
+LN2_PIN_AIF(SPDIF_AIF);
+LN2_PIN_AIF(USB_AIF1);
+LN2_PIN_AIF(USB_AIF2);
+LN2_PIN_AIF(ADAT_AIF);
+LN2_PIN_AIF(SOUNDCARD_AIF);
+
+static const struct pinctrl_pin_desc lochnagar1_pins[] = {
+ LN1_PIN(CDC_RESET), LN1_PIN(DSP_RESET), LN1_PIN(CDC_CIF1MODE),
+ LN1_PIN(GF_GPIO2), LN1_PIN(GF_GPIO3), LN1_PIN(GF_GPIO7),
+ LN1_PIN(LED1), LN1_PIN(LED2),
+ LN1_PINS(CDC_AIF1), LN1_PINS(CDC_AIF2), LN1_PINS(CDC_AIF3),
+ LN1_PINS(DSP_AIF1), LN1_PINS(DSP_AIF2),
+ LN1_PINS(PSIA1), LN1_PINS(PSIA2),
+ LN1_PINS(SPDIF_AIF),
+ LN1_PINS(GF_AIF1), LN1_PINS(GF_AIF2),
+ LN1_PINS(GF_AIF3), LN1_PINS(GF_AIF4),
+};
+
+static const struct pinctrl_pin_desc lochnagar2_pins[] = {
+ LN2_PIN(CDC_RESET), LN2_PIN(DSP_RESET), LN2_PIN(CDC_CIF1MODE),
+ LN2_PIN(CDC_LDOENA),
+ LN2_PIN(SPDIF_HWMODE), LN2_PIN(SPDIF_RESET),
+ LN2_PIN(FPGA_GPIO1), LN2_PIN(FPGA_GPIO2), LN2_PIN(FPGA_GPIO3),
+ LN2_PIN(FPGA_GPIO4), LN2_PIN(FPGA_GPIO5), LN2_PIN(FPGA_GPIO6),
+ LN2_PIN(CDC_GPIO1), LN2_PIN(CDC_GPIO2), LN2_PIN(CDC_GPIO3),
+ LN2_PIN(CDC_GPIO4), LN2_PIN(CDC_GPIO5), LN2_PIN(CDC_GPIO6),
+ LN2_PIN(CDC_GPIO7), LN2_PIN(CDC_GPIO8),
+ LN2_PIN(DSP_GPIO1), LN2_PIN(DSP_GPIO2), LN2_PIN(DSP_GPIO3),
+ LN2_PIN(DSP_GPIO4), LN2_PIN(DSP_GPIO5), LN2_PIN(DSP_GPIO6),
+ LN2_PIN(DSP_GPIO20),
+ LN2_PIN(GF_GPIO1), LN2_PIN(GF_GPIO2), LN2_PIN(GF_GPIO3),
+ LN2_PIN(GF_GPIO5), LN2_PIN(GF_GPIO7),
+ LN2_PINS(CDC_AIF1), LN2_PINS(CDC_AIF2), LN2_PINS(CDC_AIF3),
+ LN2_PINS(DSP_AIF1), LN2_PINS(DSP_AIF2),
+ LN2_PINS(PSIA1), LN2_PINS(PSIA2),
+ LN2_PINS(GF_AIF1), LN2_PINS(GF_AIF2),
+ LN2_PINS(GF_AIF3), LN2_PINS(GF_AIF4),
+ LN2_PIN(DSP_UART1_RX), LN2_PIN(DSP_UART1_TX),
+ LN2_PIN(DSP_UART2_RX), LN2_PIN(DSP_UART2_TX),
+ LN2_PIN(GF_UART2_RX), LN2_PIN(GF_UART2_TX),
+ LN2_PIN(USB_UART_RX),
+ LN2_PIN(CDC_PDMCLK1), LN2_PIN(CDC_PDMDAT1),
+ LN2_PIN(CDC_PDMCLK2), LN2_PIN(CDC_PDMDAT2),
+ LN2_PIN(CDC_DMICCLK1), LN2_PIN(CDC_DMICDAT1),
+ LN2_PIN(CDC_DMICCLK2), LN2_PIN(CDC_DMICDAT2),
+ LN2_PIN(CDC_DMICCLK3), LN2_PIN(CDC_DMICDAT3),
+ LN2_PIN(CDC_DMICCLK4), LN2_PIN(CDC_DMICDAT4),
+ LN2_PIN(DSP_DMICCLK1), LN2_PIN(DSP_DMICDAT1),
+ LN2_PIN(DSP_DMICCLK2), LN2_PIN(DSP_DMICDAT2),
+ LN2_PIN(I2C2_SCL), LN2_PIN(I2C2_SDA),
+ LN2_PIN(I2C3_SCL), LN2_PIN(I2C3_SDA),
+ LN2_PIN(I2C4_SCL), LN2_PIN(I2C4_SDA),
+ LN2_PIN(DSP_STANDBY),
+ LN2_PIN(CDC_MCLK1), LN2_PIN(CDC_MCLK2),
+ LN2_PIN(DSP_CLKIN),
+ LN2_PIN(PSIA1_MCLK), LN2_PIN(PSIA2_MCLK),
+ LN2_PINS(SPDIF_AIF),
+ LN2_PINS(USB_AIF1), LN2_PINS(USB_AIF2),
+ LN2_PINS(ADAT_AIF),
+ LN2_PINS(SOUNDCARD_AIF),
+};
+
+#define LN_AIF_PINS(REV, ID) \
+ LOCHNAGAR##REV##_PIN_##ID##_BCLK, \
+ LOCHNAGAR##REV##_PIN_##ID##_LRCLK, \
+ LOCHNAGAR##REV##_PIN_##ID##_TXDAT, \
+ LOCHNAGAR##REV##_PIN_##ID##_RXDAT,
+
+#define LN1_AIF(ID, CTRL) \
+static const struct lochnagar_aif lochnagar1_##ID##_aif = { \
+ .name = LN_##ID##_STR, \
+ .pins = { LN_AIF_PINS(1, ID) }, \
+ .src_reg = LOCHNAGAR1_##ID##_SEL, \
+ .src_mask = LOCHNAGAR1_SRC_MASK, \
+ .ctrl_reg = LOCHNAGAR1_##CTRL, \
+ .ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
+ .master_mask = LOCHNAGAR1_##ID##_LRCLK_DIR_MASK | \
+ LOCHNAGAR1_##ID##_BCLK_DIR_MASK, \
+}
+
+#define LN2_AIF(ID) \
+static const struct lochnagar_aif lochnagar2_##ID##_aif = { \
+ .name = LN_##ID##_STR, \
+ .pins = { LN_AIF_PINS(2, ID) }, \
+ .src_reg = LOCHNAGAR2_##ID##_CTRL, \
+ .src_mask = LOCHNAGAR2_AIF_SRC_MASK, \
+ .ctrl_reg = LOCHNAGAR2_##ID##_CTRL, \
+ .ena_mask = LOCHNAGAR2_AIF_ENA_MASK, \
+ .master_mask = LOCHNAGAR2_AIF_LRCLK_DIR_MASK | \
+ LOCHNAGAR2_AIF_BCLK_DIR_MASK, \
+}
+
+struct lochnagar_aif {
+ const char name[16];
+
+ unsigned int pins[4];
+
+ unsigned int src_reg;
+ unsigned int src_mask;
+
+ unsigned int ctrl_reg;
+ unsigned int ena_mask;
+ unsigned int master_mask;
+};
+
+LN1_AIF(CDC_AIF1, CDC_AIF_CTRL1);
+LN1_AIF(CDC_AIF2, CDC_AIF_CTRL1);
+LN1_AIF(CDC_AIF3, CDC_AIF_CTRL2);
+LN1_AIF(DSP_AIF1, DSP_AIF);
+LN1_AIF(DSP_AIF2, DSP_AIF);
+LN1_AIF(PSIA1, PSIA_AIF);
+LN1_AIF(PSIA2, PSIA_AIF);
+LN1_AIF(GF_AIF1, GF_AIF1);
+LN1_AIF(GF_AIF2, GF_AIF2);
+LN1_AIF(GF_AIF3, GF_AIF1);
+LN1_AIF(GF_AIF4, GF_AIF2);
+LN1_AIF(SPDIF_AIF, EXT_AIF_CTRL);
+
+LN2_AIF(CDC_AIF1);
+LN2_AIF(CDC_AIF2);
+LN2_AIF(CDC_AIF3);
+LN2_AIF(DSP_AIF1);
+LN2_AIF(DSP_AIF2);
+LN2_AIF(PSIA1);
+LN2_AIF(PSIA2);
+LN2_AIF(GF_AIF1);
+LN2_AIF(GF_AIF2);
+LN2_AIF(GF_AIF3);
+LN2_AIF(GF_AIF4);
+LN2_AIF(SPDIF_AIF);
+LN2_AIF(USB_AIF1);
+LN2_AIF(USB_AIF2);
+LN2_AIF(ADAT_AIF);
+LN2_AIF(SOUNDCARD_AIF);
+
+#define LN2_OP_AIF 0x00
+#define LN2_OP_GPIO 0xFE
+
+#define LN_FUNC(NAME, TYPE, OP) \
+ { .name = NAME, .type = LN_FTYPE_##TYPE, .op = OP }
+
+#define LN_FUNC_PIN(REV, ID, OP) \
+ LN_FUNC(lochnagar##REV##_##ID##_pin.name, PIN, OP)
+
+#define LN1_FUNC_PIN(ID, OP) LN_FUNC_PIN(1, ID, OP)
+#define LN2_FUNC_PIN(ID, OP) LN_FUNC_PIN(2, ID, OP)
+
+#define LN_FUNC_AIF(REV, ID, OP) \
+ LN_FUNC(lochnagar##REV##_##ID##_aif.name, AIF, OP)
+
+#define LN1_FUNC_AIF(ID, OP) LN_FUNC_AIF(1, ID, OP)
+#define LN2_FUNC_AIF(ID, OP) LN_FUNC_AIF(2, ID, OP)
+
+#define LN2_FUNC_GAI(ID, OP, BOP, LROP, RXOP, TXOP) \
+ LN2_FUNC_AIF(ID, OP), \
+ LN_FUNC(lochnagar2_##ID##_BCLK_pin.name, PIN, BOP), \
+ LN_FUNC(lochnagar2_##ID##_LRCLK_pin.name, PIN, LROP), \
+ LN_FUNC(lochnagar2_##ID##_RXDAT_pin.name, PIN, RXOP), \
+ LN_FUNC(lochnagar2_##ID##_TXDAT_pin.name, PIN, TXOP)
+
+enum lochnagar_func_type {
+ LN_FTYPE_PIN,
+ LN_FTYPE_AIF,
+ LN_FTYPE_COUNT,
+};
+
+struct lochnagar_func {
+ const char * const name;
+
+ enum lochnagar_func_type type;
+
+ int op;
+};
+
+static const struct lochnagar_func lochnagar1_funcs[] = {
+ LN_FUNC("dsp-gpio1", PIN, 0x01),
+ LN_FUNC("dsp-gpio2", PIN, 0x02),
+ LN_FUNC("dsp-gpio3", PIN, 0x03),
+ LN_FUNC("codec-gpio1", PIN, 0x04),
+ LN_FUNC("codec-gpio2", PIN, 0x05),
+ LN_FUNC("codec-gpio3", PIN, 0x06),
+ LN_FUNC("codec-gpio4", PIN, 0x07),
+ LN_FUNC("codec-gpio5", PIN, 0x08),
+ LN_FUNC("codec-gpio6", PIN, 0x09),
+ LN_FUNC("codec-gpio7", PIN, 0x0A),
+ LN_FUNC("codec-gpio8", PIN, 0x0B),
+ LN1_FUNC_PIN(GF_GPIO2, 0x0C),
+ LN1_FUNC_PIN(GF_GPIO3, 0x0D),
+ LN1_FUNC_PIN(GF_GPIO7, 0x0E),
+
+ LN1_FUNC_AIF(SPDIF_AIF, 0x01),
+ LN1_FUNC_AIF(PSIA1, 0x02),
+ LN1_FUNC_AIF(PSIA2, 0x03),
+ LN1_FUNC_AIF(CDC_AIF1, 0x04),
+ LN1_FUNC_AIF(CDC_AIF2, 0x05),
+ LN1_FUNC_AIF(CDC_AIF3, 0x06),
+ LN1_FUNC_AIF(DSP_AIF1, 0x07),
+ LN1_FUNC_AIF(DSP_AIF2, 0x08),
+ LN1_FUNC_AIF(GF_AIF3, 0x09),
+ LN1_FUNC_AIF(GF_AIF4, 0x0A),
+ LN1_FUNC_AIF(GF_AIF1, 0x0B),
+ LN1_FUNC_AIF(GF_AIF2, 0x0C),
+};
+
+static const struct lochnagar_func lochnagar2_funcs[] = {
+ LN_FUNC("aif", PIN, LN2_OP_AIF),
+ LN2_FUNC_PIN(FPGA_GPIO1, 0x01),
+ LN2_FUNC_PIN(FPGA_GPIO2, 0x02),
+ LN2_FUNC_PIN(FPGA_GPIO3, 0x03),
+ LN2_FUNC_PIN(FPGA_GPIO4, 0x04),
+ LN2_FUNC_PIN(FPGA_GPIO5, 0x05),
+ LN2_FUNC_PIN(FPGA_GPIO6, 0x06),
+ LN2_FUNC_PIN(CDC_GPIO1, 0x07),
+ LN2_FUNC_PIN(CDC_GPIO2, 0x08),
+ LN2_FUNC_PIN(CDC_GPIO3, 0x09),
+ LN2_FUNC_PIN(CDC_GPIO4, 0x0A),
+ LN2_FUNC_PIN(CDC_GPIO5, 0x0B),
+ LN2_FUNC_PIN(CDC_GPIO6, 0x0C),
+ LN2_FUNC_PIN(CDC_GPIO7, 0x0D),
+ LN2_FUNC_PIN(CDC_GPIO8, 0x0E),
+ LN2_FUNC_PIN(DSP_GPIO1, 0x0F),
+ LN2_FUNC_PIN(DSP_GPIO2, 0x10),
+ LN2_FUNC_PIN(DSP_GPIO3, 0x11),
+ LN2_FUNC_PIN(DSP_GPIO4, 0x12),
+ LN2_FUNC_PIN(DSP_GPIO5, 0x13),
+ LN2_FUNC_PIN(DSP_GPIO6, 0x14),
+ LN2_FUNC_PIN(GF_GPIO2, 0x15),
+ LN2_FUNC_PIN(GF_GPIO3, 0x16),
+ LN2_FUNC_PIN(GF_GPIO7, 0x17),
+ LN2_FUNC_PIN(GF_GPIO1, 0x18),
+ LN2_FUNC_PIN(GF_GPIO5, 0x19),
+ LN2_FUNC_PIN(DSP_GPIO20, 0x1A),
+ LN_FUNC("codec-clkout", PIN, 0x20),
+ LN_FUNC("dsp-clkout", PIN, 0x21),
+ LN_FUNC("pmic-32k", PIN, 0x22),
+ LN_FUNC("spdif-clkout", PIN, 0x23),
+ LN_FUNC("clk-12m288", PIN, 0x24),
+ LN_FUNC("clk-11m2986", PIN, 0x25),
+ LN_FUNC("clk-24m576", PIN, 0x26),
+ LN_FUNC("clk-22m5792", PIN, 0x27),
+ LN_FUNC("xmos-mclk", PIN, 0x29),
+ LN_FUNC("gf-clkout1", PIN, 0x2A),
+ LN_FUNC("gf-mclk1", PIN, 0x2B),
+ LN_FUNC("gf-mclk3", PIN, 0x2C),
+ LN_FUNC("gf-mclk2", PIN, 0x2D),
+ LN_FUNC("gf-clkout2", PIN, 0x2E),
+ LN2_FUNC_PIN(CDC_MCLK1, 0x2F),
+ LN2_FUNC_PIN(CDC_MCLK2, 0x30),
+ LN2_FUNC_PIN(DSP_CLKIN, 0x31),
+ LN2_FUNC_PIN(PSIA1_MCLK, 0x32),
+ LN2_FUNC_PIN(PSIA2_MCLK, 0x33),
+ LN_FUNC("spdif-mclk", PIN, 0x34),
+ LN_FUNC("codec-irq", PIN, 0x42),
+ LN2_FUNC_PIN(CDC_RESET, 0x43),
+ LN2_FUNC_PIN(DSP_RESET, 0x44),
+ LN_FUNC("dsp-irq", PIN, 0x45),
+ LN2_FUNC_PIN(DSP_STANDBY, 0x46),
+ LN2_FUNC_PIN(CDC_PDMCLK1, 0x90),
+ LN2_FUNC_PIN(CDC_PDMDAT1, 0x91),
+ LN2_FUNC_PIN(CDC_PDMCLK2, 0x92),
+ LN2_FUNC_PIN(CDC_PDMDAT2, 0x93),
+ LN2_FUNC_PIN(CDC_DMICCLK1, 0xA0),
+ LN2_FUNC_PIN(CDC_DMICDAT1, 0xA1),
+ LN2_FUNC_PIN(CDC_DMICCLK2, 0xA2),
+ LN2_FUNC_PIN(CDC_DMICDAT2, 0xA3),
+ LN2_FUNC_PIN(CDC_DMICCLK3, 0xA4),
+ LN2_FUNC_PIN(CDC_DMICDAT3, 0xA5),
+ LN2_FUNC_PIN(CDC_DMICCLK4, 0xA6),
+ LN2_FUNC_PIN(CDC_DMICDAT4, 0xA7),
+ LN2_FUNC_PIN(DSP_DMICCLK1, 0xA8),
+ LN2_FUNC_PIN(DSP_DMICDAT1, 0xA9),
+ LN2_FUNC_PIN(DSP_DMICCLK2, 0xAA),
+ LN2_FUNC_PIN(DSP_DMICDAT2, 0xAB),
+ LN2_FUNC_PIN(DSP_UART1_RX, 0xC0),
+ LN2_FUNC_PIN(DSP_UART1_TX, 0xC1),
+ LN2_FUNC_PIN(DSP_UART2_RX, 0xC2),
+ LN2_FUNC_PIN(DSP_UART2_TX, 0xC3),
+ LN2_FUNC_PIN(GF_UART2_RX, 0xC4),
+ LN2_FUNC_PIN(GF_UART2_TX, 0xC5),
+ LN2_FUNC_PIN(USB_UART_RX, 0xC6),
+ LN_FUNC("usb-uart-tx", PIN, 0xC7),
+ LN2_FUNC_PIN(I2C2_SCL, 0xE0),
+ LN2_FUNC_PIN(I2C2_SDA, 0xE1),
+ LN2_FUNC_PIN(I2C3_SCL, 0xE2),
+ LN2_FUNC_PIN(I2C3_SDA, 0xE3),
+ LN2_FUNC_PIN(I2C4_SCL, 0xE4),
+ LN2_FUNC_PIN(I2C4_SDA, 0xE5),
+ LN_FUNC("gpio", PIN, LN2_OP_GPIO),
+
+ LN2_FUNC_AIF(SPDIF_AIF, 0x01),
+ LN2_FUNC_GAI(PSIA1, 0x02, 0x50, 0x51, 0x52, 0x53),
+ LN2_FUNC_GAI(PSIA2, 0x03, 0x54, 0x55, 0x56, 0x57),
+ LN2_FUNC_GAI(CDC_AIF1, 0x04, 0x59, 0x5B, 0x5A, 0x58),
+ LN2_FUNC_GAI(CDC_AIF2, 0x05, 0x5D, 0x5F, 0x5E, 0x5C),
+ LN2_FUNC_GAI(CDC_AIF3, 0x06, 0x61, 0x62, 0x63, 0x60),
+ LN2_FUNC_GAI(DSP_AIF1, 0x07, 0x65, 0x67, 0x66, 0x64),
+ LN2_FUNC_GAI(DSP_AIF2, 0x08, 0x69, 0x6B, 0x6A, 0x68),
+ LN2_FUNC_GAI(GF_AIF3, 0x09, 0x6D, 0x6F, 0x6C, 0x6E),
+ LN2_FUNC_GAI(GF_AIF4, 0x0A, 0x71, 0x73, 0x70, 0x72),
+ LN2_FUNC_GAI(GF_AIF1, 0x0B, 0x75, 0x77, 0x74, 0x76),
+ LN2_FUNC_GAI(GF_AIF2, 0x0C, 0x79, 0x7B, 0x78, 0x7A),
+ LN2_FUNC_AIF(USB_AIF1, 0x0D),
+ LN2_FUNC_AIF(USB_AIF2, 0x0E),
+ LN2_FUNC_AIF(ADAT_AIF, 0x0F),
+ LN2_FUNC_AIF(SOUNDCARD_AIF, 0x10),
+};
+
+#define LN_GROUP_PIN(REV, ID) { \
+ .name = lochnagar##REV##_##ID##_pin.name, \
+ .type = LN_FTYPE_PIN, \
+ .pins = &lochnagar##REV##_pins[LOCHNAGAR##REV##_PIN_##ID].number, \
+ .npins = 1, \
+ .priv = &lochnagar##REV##_pins[LOCHNAGAR##REV##_PIN_##ID], \
+}
+
+#define LN_GROUP_AIF(REV, ID) { \
+ .name = lochnagar##REV##_##ID##_aif.name, \
+ .type = LN_FTYPE_AIF, \
+ .pins = lochnagar##REV##_##ID##_aif.pins, \
+ .npins = ARRAY_SIZE(lochnagar##REV##_##ID##_aif.pins), \
+ .priv = &lochnagar##REV##_##ID##_aif, \
+}
+
+#define LN1_GROUP_PIN(ID) LN_GROUP_PIN(1, ID)
+#define LN2_GROUP_PIN(ID) LN_GROUP_PIN(2, ID)
+
+#define LN1_GROUP_AIF(ID) LN_GROUP_AIF(1, ID)
+#define LN2_GROUP_AIF(ID) LN_GROUP_AIF(2, ID)
+
+#define LN2_GROUP_GAI(ID) \
+ LN2_GROUP_AIF(ID), \
+ LN2_GROUP_PIN(ID##_BCLK), LN2_GROUP_PIN(ID##_LRCLK), \
+ LN2_GROUP_PIN(ID##_RXDAT), LN2_GROUP_PIN(ID##_TXDAT)
+
+struct lochnagar_group {
+ const char * const name;
+
+ enum lochnagar_func_type type;
+
+ const unsigned int *pins;
+ int npins;
+
+ const void *priv;
+};
+
+static const struct lochnagar_group lochnagar1_groups[] = {
+ LN1_GROUP_PIN(GF_GPIO2), LN1_GROUP_PIN(GF_GPIO3),
+ LN1_GROUP_PIN(GF_GPIO7),
+ LN1_GROUP_PIN(LED1), LN1_GROUP_PIN(LED2),
+ LN1_GROUP_AIF(CDC_AIF1), LN1_GROUP_AIF(CDC_AIF2),
+ LN1_GROUP_AIF(CDC_AIF3),
+ LN1_GROUP_AIF(DSP_AIF1), LN1_GROUP_AIF(DSP_AIF2),
+ LN1_GROUP_AIF(PSIA1), LN1_GROUP_AIF(PSIA2),
+ LN1_GROUP_AIF(GF_AIF1), LN1_GROUP_AIF(GF_AIF2),
+ LN1_GROUP_AIF(GF_AIF3), LN1_GROUP_AIF(GF_AIF4),
+ LN1_GROUP_AIF(SPDIF_AIF),
+};
+
+static const struct lochnagar_group lochnagar2_groups[] = {
+ LN2_GROUP_PIN(FPGA_GPIO1), LN2_GROUP_PIN(FPGA_GPIO2),
+ LN2_GROUP_PIN(FPGA_GPIO3), LN2_GROUP_PIN(FPGA_GPIO4),
+ LN2_GROUP_PIN(FPGA_GPIO5), LN2_GROUP_PIN(FPGA_GPIO6),
+ LN2_GROUP_PIN(CDC_GPIO1), LN2_GROUP_PIN(CDC_GPIO2),
+ LN2_GROUP_PIN(CDC_GPIO3), LN2_GROUP_PIN(CDC_GPIO4),
+ LN2_GROUP_PIN(CDC_GPIO5), LN2_GROUP_PIN(CDC_GPIO6),
+ LN2_GROUP_PIN(CDC_GPIO7), LN2_GROUP_PIN(CDC_GPIO8),
+ LN2_GROUP_PIN(DSP_GPIO1), LN2_GROUP_PIN(DSP_GPIO2),
+ LN2_GROUP_PIN(DSP_GPIO3), LN2_GROUP_PIN(DSP_GPIO4),
+ LN2_GROUP_PIN(DSP_GPIO5), LN2_GROUP_PIN(DSP_GPIO6),
+ LN2_GROUP_PIN(DSP_GPIO20),
+ LN2_GROUP_PIN(GF_GPIO1),
+ LN2_GROUP_PIN(GF_GPIO2), LN2_GROUP_PIN(GF_GPIO5),
+ LN2_GROUP_PIN(GF_GPIO3), LN2_GROUP_PIN(GF_GPIO7),
+ LN2_GROUP_PIN(DSP_UART1_RX), LN2_GROUP_PIN(DSP_UART1_TX),
+ LN2_GROUP_PIN(DSP_UART2_RX), LN2_GROUP_PIN(DSP_UART2_TX),
+ LN2_GROUP_PIN(GF_UART2_RX), LN2_GROUP_PIN(GF_UART2_TX),
+ LN2_GROUP_PIN(USB_UART_RX),
+ LN2_GROUP_PIN(CDC_PDMCLK1), LN2_GROUP_PIN(CDC_PDMDAT1),
+ LN2_GROUP_PIN(CDC_PDMCLK2), LN2_GROUP_PIN(CDC_PDMDAT2),
+ LN2_GROUP_PIN(CDC_DMICCLK1), LN2_GROUP_PIN(CDC_DMICDAT1),
+ LN2_GROUP_PIN(CDC_DMICCLK2), LN2_GROUP_PIN(CDC_DMICDAT2),
+ LN2_GROUP_PIN(CDC_DMICCLK3), LN2_GROUP_PIN(CDC_DMICDAT3),
+ LN2_GROUP_PIN(CDC_DMICCLK4), LN2_GROUP_PIN(CDC_DMICDAT4),
+ LN2_GROUP_PIN(DSP_DMICCLK1), LN2_GROUP_PIN(DSP_DMICDAT1),
+ LN2_GROUP_PIN(DSP_DMICCLK2), LN2_GROUP_PIN(DSP_DMICDAT2),
+ LN2_GROUP_PIN(I2C2_SCL), LN2_GROUP_PIN(I2C2_SDA),
+ LN2_GROUP_PIN(I2C3_SCL), LN2_GROUP_PIN(I2C3_SDA),
+ LN2_GROUP_PIN(I2C4_SCL), LN2_GROUP_PIN(I2C4_SDA),
+ LN2_GROUP_PIN(DSP_STANDBY),
+ LN2_GROUP_PIN(CDC_MCLK1), LN2_GROUP_PIN(CDC_MCLK2),
+ LN2_GROUP_PIN(DSP_CLKIN),
+ LN2_GROUP_PIN(PSIA1_MCLK), LN2_GROUP_PIN(PSIA2_MCLK),
+ LN2_GROUP_GAI(CDC_AIF1), LN2_GROUP_GAI(CDC_AIF2),
+ LN2_GROUP_GAI(CDC_AIF3),
+ LN2_GROUP_GAI(DSP_AIF1), LN2_GROUP_GAI(DSP_AIF2),
+ LN2_GROUP_GAI(PSIA1), LN2_GROUP_GAI(PSIA2),
+ LN2_GROUP_GAI(GF_AIF1), LN2_GROUP_GAI(GF_AIF2),
+ LN2_GROUP_GAI(GF_AIF3), LN2_GROUP_GAI(GF_AIF4),
+ LN2_GROUP_AIF(SPDIF_AIF),
+ LN2_GROUP_AIF(USB_AIF1), LN2_GROUP_AIF(USB_AIF2),
+ LN2_GROUP_AIF(ADAT_AIF),
+ LN2_GROUP_AIF(SOUNDCARD_AIF),
+};
+
+struct lochnagar_func_groups {
+ const char **groups;
+ int ngroups;
+};
+
+struct lochnagar_pin_priv {
+ struct lochnagar *lochnagar;
+ struct device *dev;
+
+ const struct lochnagar_func *funcs;
+ int nfuncs;
+
+ const struct pinctrl_pin_desc *pins;
+ int npins;
+
+ const struct lochnagar_group *groups;
+ int ngroups;
+
+ struct lochnagar_func_groups func_groups[LN_FTYPE_COUNT];
+
+ struct gpio_chip gpio_chip;
+};
+
+static int lochnagar_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+ return priv->ngroups;
+}
+
+static const char *lochnagar_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int group_idx)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+ return priv->groups[group_idx].name;
+}
+
+static int lochnagar_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int group_idx,
+ const unsigned int **pins,
+ unsigned int *num_pins)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = priv->groups[group_idx].pins;
+ *num_pins = priv->groups[group_idx].npins;
+
+ return 0;
+}
+
+static const struct pinctrl_ops lochnagar_pin_group_ops = {
+ .get_groups_count = lochnagar_get_groups_count,
+ .get_group_name = lochnagar_get_group_name,
+ .get_group_pins = lochnagar_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_all,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+static int lochnagar_get_funcs_count(struct pinctrl_dev *pctldev)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+ return priv->nfuncs;
+}
+
+static const char *lochnagar_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned int func_idx)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+
+ return priv->funcs[func_idx].name;
+}
+
+static int lochnagar_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned int func_idx,
+ const char * const **groups,
+ unsigned int * const num_groups)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+ int func_type;
+
+ func_type = priv->funcs[func_idx].type;
+
+ *groups = priv->func_groups[func_type].groups;
+ *num_groups = priv->func_groups[func_type].ngroups;
+
+ return 0;
+}
+
+static int lochnagar2_get_gpio_chan(struct lochnagar_pin_priv *priv,
+ unsigned int op)
+{
+ struct regmap *regmap = priv->lochnagar->regmap;
+ unsigned int val;
+ int free = -1;
+ int i, ret;
+
+ for (i = 0; i < LN2_NUM_GPIO_CHANNELS; i++) {
+ ret = regmap_read(regmap, LOCHNAGAR2_GPIO_CHANNEL1 + i, &val);
+ if (ret)
+ return ret;
+
+ val &= LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK;
+
+ if (val == op)
+ return i + 1;
+
+ if (free < 0 && !val)
+ free = i;
+ }
+
+ if (free >= 0) {
+ ret = regmap_update_bits(regmap,
+ LOCHNAGAR2_GPIO_CHANNEL1 + free,
+ LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK, op);
+ if (ret)
+ return ret;
+
+ free++;
+
+ dev_dbg(priv->dev, "Set channel %d to 0x%x\n", free, op);
+
+ return free;
+ }
+
+ return -ENOSPC;
+}
+
+static int lochnagar_pin_set_mux(struct lochnagar_pin_priv *priv,
+ const struct lochnagar_pin *pin,
+ unsigned int op)
+{
+ int ret;
+
+ switch (priv->lochnagar->type) {
+ case LOCHNAGAR1:
+ break;
+ default:
+ ret = lochnagar2_get_gpio_chan(priv, op);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to get channel for %s: %d\n",
+ pin->name, ret);
+ return ret;
+ }
+
+ op = ret;
+ break;
+ }
+
+ dev_dbg(priv->dev, "Set pin %s to 0x%x\n", pin->name, op);
+
+ ret = regmap_write(priv->lochnagar->regmap, pin->reg, op);
+ if (ret)
+ dev_err(priv->dev, "Failed to set %s mux: %d\n",
+ pin->name, ret);
+
+ return 0;
+}
+
+static int lochnagar_aif_set_mux(struct lochnagar_pin_priv *priv,
+ const struct lochnagar_group *group,
+ unsigned int op)
+{
+ struct regmap *regmap = priv->lochnagar->regmap;
+ const struct lochnagar_aif *aif = group->priv;
+ const struct lochnagar_pin *pin;
+ int i, ret;
+
+ ret = regmap_update_bits(regmap, aif->src_reg, aif->src_mask, op);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set %s source: %d\n",
+ group->name, ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(regmap, aif->ctrl_reg,
+ aif->ena_mask, aif->ena_mask);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set %s enable: %d\n",
+ group->name, ret);
+ return ret;
+ }
+
+ for (i = 0; i < group->npins; i++) {
+ pin = priv->pins[group->pins[i]].drv_data;
+
+ if (pin->type != LN_PTYPE_MUX)
+ continue;
+
+ dev_dbg(priv->dev, "Set pin %s to AIF\n", pin->name);
+
+ ret = regmap_update_bits(regmap, pin->reg,
+ LOCHNAGAR2_GPIO_SRC_MASK,
+ LN2_OP_AIF);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set %s to AIF: %d\n",
+ pin->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int lochnagar_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int func_idx, unsigned int group_idx)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+ const struct lochnagar_func *func = &priv->funcs[func_idx];
+ const struct lochnagar_group *group = &priv->groups[group_idx];
+ const struct lochnagar_pin *pin;
+
+ switch (func->type) {
+ case LN_FTYPE_AIF:
+ dev_dbg(priv->dev, "Set group %s to %s\n",
+ group->name, func->name);
+
+ return lochnagar_aif_set_mux(priv, group, func->op);
+ case LN_FTYPE_PIN:
+ pin = priv->pins[*group->pins].drv_data;
+
+ dev_dbg(priv->dev, "Set pin %s to %s\n", pin->name, func->name);
+
+ return lochnagar_pin_set_mux(priv, pin, func->op);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int lochnagar_gpio_request(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+ struct lochnagar *lochnagar = priv->lochnagar;
+ const struct lochnagar_pin *pin = priv->pins[offset].drv_data;
+ int ret;
+
+ dev_dbg(priv->dev, "Requesting GPIO %s\n", pin->name);
+
+ if (lochnagar->type == LOCHNAGAR1 || pin->type != LN_PTYPE_MUX)
+ return 0;
+
+ ret = lochnagar2_get_gpio_chan(priv, LN2_OP_GPIO);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to get low channel: %d\n", ret);
+ return ret;
+ }
+
+ ret = lochnagar2_get_gpio_chan(priv, LN2_OP_GPIO | 0x1);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to get high channel: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lochnagar_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int offset,
+ bool input)
+{
+ /* The GPIOs only support output */
+ if (input)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct pinmux_ops lochnagar_pin_mux_ops = {
+ .get_functions_count = lochnagar_get_funcs_count,
+ .get_function_name = lochnagar_get_func_name,
+ .get_function_groups = lochnagar_get_func_groups,
+ .set_mux = lochnagar_set_mux,
+
+ .gpio_request_enable = lochnagar_gpio_request,
+ .gpio_set_direction = lochnagar_gpio_set_direction,
+
+ .strict = true,
+};
+
+enum lochnagar_pin_config_param {
+ PIN_CONFIG_AIFMASTER = PIN_CONFIG_END + 1,
+ PIN_CONFIG_AIFSLAVE,
+};
+
+static int lochnagar_aif_set_master(struct lochnagar_pin_priv *priv,
+ unsigned int group_idx, bool master)
+{
+ struct regmap *regmap = priv->lochnagar->regmap;
+ const struct lochnagar_group *group = &priv->groups[group_idx];
+ const struct lochnagar_aif *aif = group->priv;
+ unsigned int val = 0;
+ int ret;
+
+ if (group->type != LN_FTYPE_AIF)
+ return -EINVAL;
+
+ if (!master)
+ val = aif->master_mask;
+
+ dev_dbg(priv->dev, "Set AIF %s to %s\n",
+ group->name, master ? "master" : "slave");
+
+ ret = regmap_update_bits(regmap, aif->ctrl_reg, aif->master_mask, val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to set %s mode: %d\n",
+ group->name, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lochnagar_conf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int group_idx,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev);
+ int i, ret;
+
+ for (i = 0; i < num_configs; i++) {
+ unsigned int param = pinconf_to_config_param(*configs);
+
+ switch (param) {
+ case PIN_CONFIG_AIFMASTER:
+ ret = lochnagar_aif_set_master(priv, group_idx, true);
+ if (ret)
+ return ret;
+ break;
+ case PIN_CONFIG_AIFSLAVE:
+ ret = lochnagar_aif_set_master(priv, group_idx, false);
+ if (ret)
+ return ret;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ configs++;
+ }
+
+ return 0;
+}
+
+static const struct pinconf_ops lochnagar_pin_conf_ops = {
+ .pin_config_group_set = lochnagar_conf_group_set,
+};
+
+static const struct pinconf_generic_params lochnagar_dt_params[] = {
+ {"cirrus,aif-master", PIN_CONFIG_AIFMASTER, 0},
+ {"cirrus,aif-slave", PIN_CONFIG_AIFSLAVE, 0},
+};
+
+static const struct pin_config_item lochnagar_conf_items[] = {
+ PCONFDUMP(PIN_CONFIG_AIFMASTER, "cirrus,aif-master", NULL, false),
+ PCONFDUMP(PIN_CONFIG_AIFSLAVE, "cirrus,aif-slave", NULL, false),
+};
+
+static const struct pinctrl_desc lochnagar_pin_desc = {
+ .name = "lochnagar-pinctrl",
+ .owner = THIS_MODULE,
+
+ .pctlops = &lochnagar_pin_group_ops,
+ .pmxops = &lochnagar_pin_mux_ops,
+ .confops = &lochnagar_pin_conf_ops,
+
+ .custom_params = lochnagar_dt_params,
+ .num_custom_params = ARRAY_SIZE(lochnagar_dt_params),
+ .custom_conf_items = lochnagar_conf_items,
+};
+
+static void lochnagar_gpio_set(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ struct lochnagar_pin_priv *priv = gpiochip_get_data(chip);
+ struct lochnagar *lochnagar = priv->lochnagar;
+ const struct lochnagar_pin *pin = priv->pins[offset].drv_data;
+ int ret;
+
+ value = !!value;
+
+ dev_dbg(priv->dev, "Set GPIO %s to %s\n",
+ pin->name, value ? "high" : "low");
+
+ switch (pin->type) {
+ case LN_PTYPE_MUX:
+ value |= LN2_OP_GPIO;
+
+ ret = lochnagar_pin_set_mux(priv, pin, value);
+ break;
+ case LN_PTYPE_GPIO:
+ if (pin->invert)
+ value = !value;
+
+ ret = regmap_update_bits(lochnagar->regmap, pin->reg,
+ 0x1 << pin->shift,
+ value << pin->shift);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (ret < 0)
+ dev_err(chip->parent, "Failed to set %s value: %d\n",
+ pin->name, ret);
+}
+
+static int lochnagar_gpio_direction_out(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ lochnagar_gpio_set(chip, offset, value);
+
+ return pinctrl_gpio_direction_output(chip->base + offset);
+}
+
+static const struct gpio_chip template_chip = {
+ .label = "lochnagar-gpio",
+
+ .request = gpiochip_generic_request,
+ .free = gpiochip_generic_free,
+ .direction_output = lochnagar_gpio_direction_out,
+ .set = lochnagar_gpio_set,
+
+ .can_sleep = true,
+};
+
+static int lochnagar_fill_func_groups(struct lochnagar_pin_priv *priv)
+{
+ struct lochnagar_func_groups *funcs;
+ int i;
+
+ for (i = 0; i < priv->ngroups; i++)
+ priv->func_groups[priv->groups[i].type].ngroups++;
+
+ for (i = 0; i < LN_FTYPE_COUNT; i++) {
+ funcs = &priv->func_groups[i];
+
+ if (!funcs->ngroups)
+ continue;
+
+ funcs->groups = devm_kcalloc(priv->dev, funcs->ngroups,
+ sizeof(*funcs->groups),
+ GFP_KERNEL);
+ if (!funcs->groups)
+ return -ENOMEM;
+
+ funcs->ngroups = 0;
+ }
+
+ for (i = 0; i < priv->ngroups; i++) {
+ funcs = &priv->func_groups[priv->groups[i].type];
+
+ funcs->groups[funcs->ngroups++] = priv->groups[i].name;
+ }
+
+ return 0;
+}
+
+static int lochnagar_pin_probe(struct platform_device *pdev)
+{
+ struct lochnagar *lochnagar = dev_get_drvdata(pdev->dev.parent);
+ struct lochnagar_pin_priv *priv;
+ struct pinctrl_desc *desc;
+ struct pinctrl_dev *pctl;
+ struct device *dev = &pdev->dev;
+ int ret;
+
+ dev->of_node = of_node_get(lochnagar->dev->of_node);
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->lochnagar = lochnagar;
+
+ desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL);
+ if (!desc)
+ return -ENOMEM;
+
+ *desc = lochnagar_pin_desc;
+
+ priv->gpio_chip = template_chip;
+ priv->gpio_chip.parent = dev;
+ priv->gpio_chip.base = -1;
+#ifdef CONFIG_OF_GPIO
+ priv->gpio_chip.of_node = lochnagar->dev->of_node;
+#endif
+
+ switch (lochnagar->type) {
+ case LOCHNAGAR1:
+ priv->funcs = lochnagar1_funcs;
+ priv->nfuncs = ARRAY_SIZE(lochnagar1_funcs);
+ priv->pins = lochnagar1_pins;
+ priv->npins = ARRAY_SIZE(lochnagar1_pins);
+ priv->groups = lochnagar1_groups;
+ priv->ngroups = ARRAY_SIZE(lochnagar1_groups);
+
+ priv->gpio_chip.ngpio = LOCHNAGAR1_PIN_NUM_GPIOS;
+ break;
+ case LOCHNAGAR2:
+ priv->funcs = lochnagar2_funcs;
+ priv->nfuncs = ARRAY_SIZE(lochnagar2_funcs);
+ priv->pins = lochnagar2_pins;
+ priv->npins = ARRAY_SIZE(lochnagar2_pins);
+ priv->groups = lochnagar2_groups;
+ priv->ngroups = ARRAY_SIZE(lochnagar2_groups);
+
+ priv->gpio_chip.ngpio = LOCHNAGAR2_PIN_NUM_GPIOS;
+ break;
+ default:
+ dev_err(dev, "Unknown Lochnagar type: %d\n", lochnagar->type);
+ return -EINVAL;
+ }
+
+ ret = lochnagar_fill_func_groups(priv);
+ if (ret < 0)
+ return ret;
+
+ desc->pins = priv->pins;
+ desc->npins = priv->npins;
+
+ pctl = devm_pinctrl_register(dev, desc, priv);
+ if (IS_ERR(pctl)) {
+ ret = PTR_ERR(pctl);
+ dev_err(priv->dev, "Failed to register pinctrl: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int lochnagar_pin_remove(struct platform_device *pdev)
+{
+ of_node_put(pdev->dev.of_node);
+ pdev->dev.of_node = NULL;
+
+ return 0;
+}
+
+static struct platform_driver lochnagar_pin_driver = {
+ .driver = {
+ .name = "lochnagar-pinctrl",
+ },
+
+ .probe = lochnagar_pin_probe,
+ .remove = lochnagar_pin_remove,
+};
+module_platform_driver(lochnagar_pin_driver);
+
+MODULE_AUTHOR("Charles Keepax <[email protected]>");
+MODULE_DESCRIPTION("Pinctrl driver for Cirrus Logic Lochnagar Board");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-pinctrl");
diff --git a/drivers/pinctrl/cirrus/pinctrl-lochnagar.h b/drivers/pinctrl/cirrus/pinctrl-lochnagar.h
new file mode 100644
index 0000000000000..325ce356a300a
--- /dev/null
+++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Defines for Lochnagar pinctrl
+ *
+ * Copyright (c) 2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#include <dt-bindings/pinctrl/lochnagar.h>
+
+#ifndef PINCTRL_LOCHNAGAR_H
+#define PINCTRL_LOCHNAGAR_H
+
+#define LN2_NUM_GPIO_CHANNELS 16
+
+#define LN_CDC_AIF1_STR "codec-aif1"
+#define LN_CDC_AIF2_STR "codec-aif2"
+#define LN_CDC_AIF3_STR "codec-aif3"
+#define LN_DSP_AIF1_STR "dsp-aif1"
+#define LN_DSP_AIF2_STR "dsp-aif2"
+#define LN_PSIA1_STR "psia1"
+#define LN_PSIA2_STR "psia2"
+#define LN_GF_AIF1_STR "gf-aif1"
+#define LN_GF_AIF2_STR "gf-aif2"
+#define LN_GF_AIF3_STR "gf-aif3"
+#define LN_GF_AIF4_STR "gf-aif4"
+#define LN_SPDIF_AIF_STR "spdif-aif"
+#define LN_USB_AIF1_STR "usb-aif1"
+#define LN_USB_AIF2_STR "usb-aif2"
+#define LN_ADAT_AIF_STR "adat-aif"
+#define LN_SOUNDCARD_AIF_STR "soundcard-aif"
+
+enum {
+ LOCHNAGAR1_PIN_GF_GPIO2 = LOCHNAGAR1_PIN_NUM_GPIOS,
+ LOCHNAGAR1_PIN_GF_GPIO3,
+ LOCHNAGAR1_PIN_GF_GPIO7,
+ LOCHNAGAR1_PIN_LED1,
+ LOCHNAGAR1_PIN_LED2,
+ LOCHNAGAR1_PIN_CDC_AIF1_BCLK,
+ LOCHNAGAR1_PIN_CDC_AIF1_LRCLK,
+ LOCHNAGAR1_PIN_CDC_AIF1_RXDAT,
+ LOCHNAGAR1_PIN_CDC_AIF1_TXDAT,
+ LOCHNAGAR1_PIN_CDC_AIF2_BCLK,
+ LOCHNAGAR1_PIN_CDC_AIF2_LRCLK,
+ LOCHNAGAR1_PIN_CDC_AIF2_RXDAT,
+ LOCHNAGAR1_PIN_CDC_AIF2_TXDAT,
+ LOCHNAGAR1_PIN_CDC_AIF3_BCLK,
+ LOCHNAGAR1_PIN_CDC_AIF3_LRCLK,
+ LOCHNAGAR1_PIN_CDC_AIF3_RXDAT,
+ LOCHNAGAR1_PIN_CDC_AIF3_TXDAT,
+ LOCHNAGAR1_PIN_DSP_AIF1_BCLK,
+ LOCHNAGAR1_PIN_DSP_AIF1_LRCLK,
+ LOCHNAGAR1_PIN_DSP_AIF1_RXDAT,
+ LOCHNAGAR1_PIN_DSP_AIF1_TXDAT,
+ LOCHNAGAR1_PIN_DSP_AIF2_BCLK,
+ LOCHNAGAR1_PIN_DSP_AIF2_LRCLK,
+ LOCHNAGAR1_PIN_DSP_AIF2_RXDAT,
+ LOCHNAGAR1_PIN_DSP_AIF2_TXDAT,
+ LOCHNAGAR1_PIN_PSIA1_BCLK,
+ LOCHNAGAR1_PIN_PSIA1_LRCLK,
+ LOCHNAGAR1_PIN_PSIA1_RXDAT,
+ LOCHNAGAR1_PIN_PSIA1_TXDAT,
+ LOCHNAGAR1_PIN_PSIA2_BCLK,
+ LOCHNAGAR1_PIN_PSIA2_LRCLK,
+ LOCHNAGAR1_PIN_PSIA2_RXDAT,
+ LOCHNAGAR1_PIN_PSIA2_TXDAT,
+ LOCHNAGAR1_PIN_SPDIF_AIF_BCLK,
+ LOCHNAGAR1_PIN_SPDIF_AIF_LRCLK,
+ LOCHNAGAR1_PIN_SPDIF_AIF_RXDAT,
+ LOCHNAGAR1_PIN_SPDIF_AIF_TXDAT,
+ LOCHNAGAR1_PIN_GF_AIF3_BCLK,
+ LOCHNAGAR1_PIN_GF_AIF3_RXDAT,
+ LOCHNAGAR1_PIN_GF_AIF3_LRCLK,
+ LOCHNAGAR1_PIN_GF_AIF3_TXDAT,
+ LOCHNAGAR1_PIN_GF_AIF4_BCLK,
+ LOCHNAGAR1_PIN_GF_AIF4_RXDAT,
+ LOCHNAGAR1_PIN_GF_AIF4_LRCLK,
+ LOCHNAGAR1_PIN_GF_AIF4_TXDAT,
+ LOCHNAGAR1_PIN_GF_AIF1_BCLK,
+ LOCHNAGAR1_PIN_GF_AIF1_RXDAT,
+ LOCHNAGAR1_PIN_GF_AIF1_LRCLK,
+ LOCHNAGAR1_PIN_GF_AIF1_TXDAT,
+ LOCHNAGAR1_PIN_GF_AIF2_BCLK,
+ LOCHNAGAR1_PIN_GF_AIF2_RXDAT,
+ LOCHNAGAR1_PIN_GF_AIF2_LRCLK,
+ LOCHNAGAR1_PIN_GF_AIF2_TXDAT,
+
+ LOCHNAGAR2_PIN_SPDIF_AIF_BCLK = LOCHNAGAR2_PIN_NUM_GPIOS,
+ LOCHNAGAR2_PIN_SPDIF_AIF_LRCLK,
+ LOCHNAGAR2_PIN_SPDIF_AIF_RXDAT,
+ LOCHNAGAR2_PIN_SPDIF_AIF_TXDAT,
+ LOCHNAGAR2_PIN_USB_AIF1_BCLK,
+ LOCHNAGAR2_PIN_USB_AIF1_LRCLK,
+ LOCHNAGAR2_PIN_USB_AIF1_RXDAT,
+ LOCHNAGAR2_PIN_USB_AIF1_TXDAT,
+ LOCHNAGAR2_PIN_USB_AIF2_BCLK,
+ LOCHNAGAR2_PIN_USB_AIF2_LRCLK,
+ LOCHNAGAR2_PIN_USB_AIF2_RXDAT,
+ LOCHNAGAR2_PIN_USB_AIF2_TXDAT,
+ LOCHNAGAR2_PIN_ADAT_AIF_BCLK,
+ LOCHNAGAR2_PIN_ADAT_AIF_LRCLK,
+ LOCHNAGAR2_PIN_ADAT_AIF_RXDAT,
+ LOCHNAGAR2_PIN_ADAT_AIF_TXDAT,
+ LOCHNAGAR2_PIN_SOUNDCARD_AIF_BCLK,
+ LOCHNAGAR2_PIN_SOUNDCARD_AIF_LRCLK,
+ LOCHNAGAR2_PIN_SOUNDCARD_AIF_RXDAT,
+ LOCHNAGAR2_PIN_SOUNDCARD_AIF_TXDAT,
+};
+
+#endif
--
2.11.0
Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board.
The Lochnagar can take several input clocks from the host system,
provides several of its own clock sources, and provides extensive
routing options for those clocks to be supplied to the attached
CODEC/Amp device.
Signed-off-by: Charles Keepax <[email protected]>
---
Changes since v2:
- Remove some unused headers
- Remove an unused check on ena_mask
- Return an out of range value if get_parent fails
- Move to the hw based clock APIs
- Use u16s for register definitions
- Move fixed clocks into DT completely
- Don't use the Lochnagar struct directly
Note that the patch will still need to go through with the MFD stuff
to build correctly.
Thanks,
Charles
drivers/clk/Kconfig | 7 +
drivers/clk/Makefile | 1 +
drivers/clk/clk-lochnagar.c | 368 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 376 insertions(+)
create mode 100644 drivers/clk/clk-lochnagar.c
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 292056bbb30e9..9247c97f85d79 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -219,6 +219,13 @@ config COMMON_CLK_XGENE
---help---
Sypport for the APM X-Gene SoC reference, PLL, and device clocks.
+config COMMON_CLK_LOCHNAGAR
+ tristate "Cirrus Logic Lochnagar clock driver"
+ depends on MFD_LOCHNAGAR
+ help
+ This driver supports the clocking features of the Cirrus Logic
+ Lochnagar audio development board.
+
config COMMON_CLK_NXP
def_bool COMMON_CLK && (ARCH_LPC18XX || ARCH_LPC32XX)
select REGMAP_MMIO if ARCH_LPC32XX
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index a84c5573cabea..0443a7219bdf4 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_COMMON_CLK_GEMINI) += clk-gemini.o
obj-$(CONFIG_COMMON_CLK_ASPEED) += clk-aspeed.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_CLK_HSDK) += clk-hsdk-pll.o
+obj-$(CONFIG_COMMON_CLK_LOCHNAGAR) += clk-lochnagar.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_MAX9485) += clk-max9485.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
diff --git a/drivers/clk/clk-lochnagar.c b/drivers/clk/clk-lochnagar.c
new file mode 100644
index 0000000000000..764ee326957a5
--- /dev/null
+++ b/drivers/clk/clk-lochnagar.c
@@ -0,0 +1,368 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar clock control
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/lochnagar.h>
+#include <dt-bindings/clk/lochnagar.h>
+
+#define LOCHNAGAR_NUM_CLOCKS (LOCHNAGAR_SPDIF_CLKOUT + 1)
+
+struct lochnagar_clk {
+ const char * const name;
+ struct clk_hw hw;
+
+ struct lochnagar_clk_priv *priv;
+
+ u16 cfg_reg;
+ u16 ena_mask;
+ u16 dir_mask; /* Control bit to set clock as a source or sink */
+
+ u16 src_reg;
+ u16 src_mask;
+};
+
+struct lochnagar_clk_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ enum lochnagar_type type;
+
+ const char **parents;
+ unsigned int nparents;
+
+ struct lochnagar_clk lclks[LOCHNAGAR_NUM_CLOCKS];
+
+ struct clk_hw_onecell_data *clk_data;
+};
+
+static const char * const lochnagar1_clk_parents[] = {
+ "ln-none",
+ "ln-spdif-mclk",
+ "ln-psia1-mclk",
+ "ln-psia2-mclk",
+ "ln-cdc-clkout",
+ "ln-dsp-clkout",
+ "ln-pmic-32k",
+ "ln-gf-mclk1",
+ "ln-gf-mclk3",
+ "ln-gf-mclk2",
+ "ln-gf-mclk4",
+};
+
+static const char * const lochnagar2_clk_parents[] = {
+ "ln-none",
+ "ln-cdc-clkout",
+ "ln-dsp-clkout",
+ "ln-pmic-32k",
+ "ln-spdif-mclk",
+ "ln-clk-12m",
+ "ln-clk-11m",
+ "ln-clk-24m",
+ "ln-clk-22m",
+ "ln-reserved",
+ "ln-usb-clk-24m",
+ "ln-gf-mclk1",
+ "ln-gf-mclk3",
+ "ln-gf-mclk2",
+ "ln-psia1-mclk",
+ "ln-psia2-mclk",
+ "ln-spdif-clkout",
+ "ln-adat-clkout",
+ "ln-usb-clk-12m",
+};
+
+#define LN1_CLK(ID, NAME, REG) \
+ [LOCHNAGAR_##ID] = { \
+ .name = NAME, \
+ .cfg_reg = LOCHNAGAR1_##REG, \
+ .ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \
+ .src_reg = LOCHNAGAR1_##ID##_SEL, \
+ .src_mask = LOCHNAGAR1_SRC_MASK, \
+ }
+
+#define LN2_CLK(ID, NAME) \
+ [LOCHNAGAR_##ID] = { \
+ .name = NAME, \
+ .cfg_reg = LOCHNAGAR2_##ID##_CTRL, \
+ .src_reg = LOCHNAGAR2_##ID##_CTRL, \
+ .ena_mask = LOCHNAGAR2_CLK_ENA_MASK, \
+ .dir_mask = LOCHNAGAR2_CLK_DIR_MASK, \
+ .src_mask = LOCHNAGAR2_CLK_SRC_MASK, \
+ }
+
+static const struct lochnagar_clk lochnagar1_clks[LOCHNAGAR_NUM_CLOCKS] = {
+ LN1_CLK(CDC_MCLK1, "ln-cdc-mclk1", CDC_AIF_CTRL2),
+ LN1_CLK(CDC_MCLK2, "ln-cdc-mclk2", CDC_AIF_CTRL2),
+ LN1_CLK(DSP_CLKIN, "ln-dsp-clkin", DSP_AIF),
+ LN1_CLK(GF_CLKOUT1, "ln-gf-clkout1", GF_AIF1),
+};
+
+static const struct lochnagar_clk lochnagar2_clks[LOCHNAGAR_NUM_CLOCKS] = {
+ LN2_CLK(CDC_MCLK1, "ln-cdc-mclk1"),
+ LN2_CLK(CDC_MCLK2, "ln-cdc-mclk2"),
+ LN2_CLK(DSP_CLKIN, "ln-dsp-clkin"),
+ LN2_CLK(GF_CLKOUT1, "ln-gf-clkout1"),
+ LN2_CLK(GF_CLKOUT2, "ln-gf-clkout2"),
+ LN2_CLK(PSIA1_MCLK, "ln-psia1-mclk"),
+ LN2_CLK(PSIA2_MCLK, "ln-psia2-mclk"),
+ LN2_CLK(SPDIF_MCLK, "ln-spdif-mclk"),
+ LN2_CLK(ADAT_MCLK, "ln-adat-mclk"),
+ LN2_CLK(SOUNDCARD_MCLK, "ln-soundcard-mclk"),
+};
+
+static inline struct lochnagar_clk *lochnagar_hw_to_lclk(struct clk_hw *hw)
+{
+ return container_of(hw, struct lochnagar_clk, hw);
+}
+
+static int lochnagar_regmap_prepare(struct clk_hw *hw)
+{
+ struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
+ struct lochnagar_clk_priv *priv = lclk->priv;
+ struct regmap *regmap = priv->regmap;
+ int ret;
+
+ ret = regmap_update_bits(regmap, lclk->cfg_reg,
+ lclk->ena_mask, lclk->ena_mask);
+ if (ret < 0)
+ dev_err(priv->dev, "Failed to prepare %s: %d\n",
+ lclk->name, ret);
+
+ return ret;
+}
+
+static void lochnagar_regmap_unprepare(struct clk_hw *hw)
+{
+ struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
+ struct lochnagar_clk_priv *priv = lclk->priv;
+ struct regmap *regmap = priv->regmap;
+ int ret;
+
+ ret = regmap_update_bits(regmap, lclk->cfg_reg, lclk->ena_mask, 0);
+ if (ret < 0)
+ dev_err(priv->dev, "Failed to unprepare %s: %d\n",
+ lclk->name, ret);
+}
+
+static int lochnagar_regmap_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
+ struct lochnagar_clk_priv *priv = lclk->priv;
+ struct regmap *regmap = priv->regmap;
+ int ret;
+
+ /*
+ * Some clocks on Lochnagar can either generate a clock themselves
+ * or accept an external clock, these default to generating the clock
+ * themselves. If we set a parent however we should update the dir_mask
+ * to indicate to the hardware that this clock will now be receiving an
+ * external clock.
+ */
+ if (lclk->dir_mask) {
+ ret = regmap_update_bits(regmap, lclk->cfg_reg,
+ lclk->dir_mask, lclk->dir_mask);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to set %s direction: %d\n",
+ lclk->name, ret);
+ return ret;
+ }
+ }
+
+ ret = regmap_update_bits(regmap, lclk->src_reg, lclk->src_mask, index);
+ if (ret < 0)
+ dev_err(priv->dev, "Failed to reparent %s: %d\n",
+ lclk->name, ret);
+
+ return ret;
+}
+
+static u8 lochnagar_regmap_get_parent(struct clk_hw *hw)
+{
+ struct lochnagar_clk *lclk = lochnagar_hw_to_lclk(hw);
+ struct lochnagar_clk_priv *priv = lclk->priv;
+ struct regmap *regmap = priv->regmap;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(regmap, lclk->src_reg, &val);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to read parent of %s: %d\n",
+ lclk->name, ret);
+ return priv->nparents;
+ }
+
+ val &= lclk->src_mask;
+
+ return val;
+}
+
+static const struct clk_ops lochnagar_clk_regmap_ops = {
+ .prepare = lochnagar_regmap_prepare,
+ .unprepare = lochnagar_regmap_unprepare,
+ .set_parent = lochnagar_regmap_set_parent,
+ .get_parent = lochnagar_regmap_get_parent,
+};
+
+static int lochnagar_init_parents(struct lochnagar_clk_priv *priv)
+{
+ struct device_node *np = priv->dev->parent->of_node;
+ int i, j;
+
+ switch (priv->type) {
+ case LOCHNAGAR1:
+ memcpy(priv->lclks, lochnagar1_clks, sizeof(lochnagar1_clks));
+
+ priv->nparents = ARRAY_SIZE(lochnagar1_clk_parents);
+ priv->parents = devm_kmemdup(priv->dev, lochnagar1_clk_parents,
+ sizeof(lochnagar1_clk_parents),
+ GFP_KERNEL);
+ break;
+ case LOCHNAGAR2:
+ memcpy(priv->lclks, lochnagar2_clks, sizeof(lochnagar2_clks));
+
+ priv->nparents = ARRAY_SIZE(lochnagar2_clk_parents);
+ priv->parents = devm_kmemdup(priv->dev, lochnagar2_clk_parents,
+ sizeof(lochnagar2_clk_parents),
+ GFP_KERNEL);
+ break;
+ default:
+ dev_err(priv->dev, "Unknown Lochnagar type: %d\n", priv->type);
+ return -EINVAL;
+ }
+
+ if (!priv->parents)
+ return -ENOMEM;
+
+ for (i = 0; i < priv->nparents; i++) {
+ j = of_property_match_string(np, "clock-names",
+ priv->parents[i]);
+ if (j >= 0)
+ priv->parents[i] = of_clk_get_parent_name(np, j);
+ }
+
+ return 0;
+}
+
+static int lochnagar_init_clks(struct lochnagar_clk_priv *priv)
+{
+ struct clk_init_data clk_init = {
+ .ops = &lochnagar_clk_regmap_ops,
+ .parent_names = priv->parents,
+ .num_parents = priv->nparents,
+ };
+ struct lochnagar_clk *lclk;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(priv->lclks); i++) {
+ lclk = &priv->lclks[i];
+
+ if (!lclk->name)
+ continue;
+
+ clk_init.name = lclk->name;
+
+ lclk->priv = priv;
+ lclk->hw.init = &clk_init;
+
+ ret = devm_clk_hw_register(priv->dev, &lclk->hw);
+ if (ret) {
+ dev_err(priv->dev, "Failed to register %s: %d\n",
+ lclk->name, ret);
+ return ret;
+ }
+
+ priv->clk_data->hws[i] = &lclk->hw;
+ }
+
+ priv->clk_data->num = ARRAY_SIZE(priv->lclks);
+
+ /*
+ * Can't use devm here because the of_node is on the parent device
+ * but the lifetime should be bound to this device.
+ */
+ ret = of_clk_add_hw_provider(priv->dev->parent->of_node,
+ of_clk_hw_onecell_get, priv->clk_data);
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to register provider: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int lochnagar_clk_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lochnagar_clk_priv *priv;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->clk_data = devm_kzalloc(dev, struct_size(priv->clk_data, hws,
+ ARRAY_SIZE(priv->lclks)),
+ GFP_KERNEL);
+ if (!priv->clk_data)
+ return -ENOMEM;
+
+ priv->dev = dev;
+ priv->regmap = dev_get_regmap(dev->parent, NULL);
+ priv->type = platform_get_device_id(pdev)->driver_data;
+
+ ret = lochnagar_init_parents(priv);
+ if (ret)
+ return ret;
+
+ ret = lochnagar_init_clks(priv);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, priv);
+
+ return 0;
+}
+
+static int lochnagar_clk_remove(struct platform_device *pdev)
+{
+ struct lochnagar_clk_priv *priv = platform_get_drvdata(pdev);
+
+ of_clk_del_provider(priv->dev->parent->of_node);
+
+ return 0;
+}
+
+static const struct platform_device_id lochnagar_id[] = {
+ { "lochnagar1-clk", LOCHNAGAR1 },
+ { "lochnagar2-clk", LOCHNAGAR2 },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, lochnagar_id);
+
+static struct platform_driver lochnagar_clk_driver = {
+ .driver = {
+ .name = "lochnagar-clk",
+ },
+
+ .id_table = lochnagar_id,
+
+ .probe = lochnagar_clk_probe,
+ .remove = lochnagar_clk_remove,
+};
+module_platform_driver(lochnagar_clk_driver);
+
+MODULE_AUTHOR("Charles Keepax <[email protected]>");
+MODULE_DESCRIPTION("Clock driver for Cirrus Logic Lochnagar Board");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-clk");
--
2.11.0
Lochnagar is an evaluation and development board for Cirrus
Logic Smart CODEC and Amp devices. It allows the connection of
most Cirrus Logic devices on mini-cards, as well as allowing
connection of various application processor systems to provide a
full evaluation platform. This driver supports the board
controller chip on the Lochnagar board.
The Lochnagar board provides power supplies for the attached
CODEC/Amp device. Currently this driver supports the microphone
supplies and the digital core voltage for the attached
device. There are some additional supplies that will be
added in time but these supplies are sufficient for most
systems/use-cases.
Signed-off-by: Charles Keepax <[email protected]>
---
No changes since v2.
Thanks,
Charles
drivers/regulator/Kconfig | 7 +
drivers/regulator/Makefile | 1 +
drivers/regulator/lochnagar-regulator.c | 255 ++++++++++++++++++++++++++++++++
3 files changed, 263 insertions(+)
create mode 100644 drivers/regulator/lochnagar-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 329cdd33ed624..3eda02afdcdeb 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -356,6 +356,13 @@ config REGULATOR_LM363X
One boost output voltage is configurable and always on.
Other LDOs are used for the display module.
+config REGULATOR_LOCHNAGAR
+ tristate "Cirrus Logic Lochnagar regulator driver"
+ depends on MFD_LOCHNAGAR
+ help
+ This enables regulator support on the Cirrus Logic Lochnagar audio
+ development board.
+
config REGULATOR_LP3971
tristate "National Semiconductors LP3971 PMIC regulator driver"
depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 801d9a34a2037..0c715fa77bd62 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -46,6 +46,7 @@ obj-$(CONFIG_REGULATOR_HI655X) += hi655x-regulator.o
obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_ISL9305) += isl9305.o
obj-$(CONFIG_REGULATOR_LM363X) += lm363x-regulator.o
+obj-$(CONFIG_REGULATOR_LOCHNAGAR) += lochnagar-regulator.o
obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o
obj-$(CONFIG_REGULATOR_LP3972) += lp3972.o
obj-$(CONFIG_REGULATOR_LP872X) += lp872x.o
diff --git a/drivers/regulator/lochnagar-regulator.c b/drivers/regulator/lochnagar-regulator.c
new file mode 100644
index 0000000000000..54d791d204080
--- /dev/null
+++ b/drivers/regulator/lochnagar-regulator.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Lochnagar regulator driver
+ *
+ * Copyright (c) 2017-2018 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ *
+ * Author: Charles Keepax <[email protected]>
+ */
+
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+
+#include <linux/mfd/lochnagar.h>
+
+static const struct regulator_ops lochnagar_micvdd_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_linear_range lochnagar_micvdd_ranges[] = {
+ REGULATOR_LINEAR_RANGE(1000000, 0, 0xC, 50000),
+ REGULATOR_LINEAR_RANGE(1700000, 0xD, 0x1F, 100000),
+};
+
+static int lochnagar_micbias_enable(struct regulator_dev *rdev)
+{
+ struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
+ int ret;
+
+ mutex_lock(&lochnagar->analogue_config_lock);
+
+ ret = regulator_enable_regmap(rdev);
+ if (ret < 0)
+ goto err;
+
+ ret = lochnagar_update_config(lochnagar);
+
+err:
+ mutex_unlock(&lochnagar->analogue_config_lock);
+
+ return ret;
+}
+
+static int lochnagar_micbias_disable(struct regulator_dev *rdev)
+{
+ struct lochnagar *lochnagar = rdev_get_drvdata(rdev);
+ int ret;
+
+ mutex_lock(&lochnagar->analogue_config_lock);
+
+ ret = regulator_disable_regmap(rdev);
+ if (ret < 0)
+ goto err;
+
+ ret = lochnagar_update_config(lochnagar);
+
+err:
+ mutex_unlock(&lochnagar->analogue_config_lock);
+
+ return ret;
+}
+
+static const struct regulator_ops lochnagar_micbias_ops = {
+ .enable = lochnagar_micbias_enable,
+ .disable = lochnagar_micbias_disable,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_ops lochnagar_vddcore_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+
+ .list_voltage = regulator_list_voltage_linear_range,
+ .map_voltage = regulator_map_voltage_linear_range,
+
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+};
+
+static const struct regulator_linear_range lochnagar_vddcore_ranges[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x8, 0x41, 12500),
+};
+
+enum lochnagar_regulators {
+ LOCHNAGAR_MICVDD,
+ LOCHNAGAR_MIC1VDD,
+ LOCHNAGAR_MIC2VDD,
+ LOCHNAGAR_VDDCORE,
+};
+
+static int lochnagar_micbias_of_parse(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct lochnagar *lochnagar = config->driver_data;
+ int shift = (desc->id - LOCHNAGAR_MIC1VDD) *
+ LOCHNAGAR2_P2_MICBIAS_SRC_SHIFT;
+ int mask = LOCHNAGAR2_P1_MICBIAS_SRC_MASK << shift;
+ unsigned int val;
+ int ret;
+
+ ret = of_property_read_u32(np, "cirrus,micbias-input", &val);
+ if (ret >= 0) {
+ mutex_lock(&lochnagar->analogue_config_lock);
+ ret = regmap_update_bits(lochnagar->regmap,
+ LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
+ mask, val << shift);
+ mutex_unlock(&lochnagar->analogue_config_lock);
+ if (ret < 0) {
+ dev_err(lochnagar->dev,
+ "Failed to update micbias source: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct regulator_desc lochnagar_regulators[] = {
+ [LOCHNAGAR_MICVDD] = {
+ .name = "MICVDD",
+ .supply_name = "SYSVDD",
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 32,
+ .ops = &lochnagar_micvdd_ops,
+
+ .id = LOCHNAGAR_MICVDD,
+ .of_match = of_match_ptr("MICVDD"),
+
+ .enable_reg = LOCHNAGAR2_MICVDD_CTRL1,
+ .enable_mask = LOCHNAGAR2_MICVDD_REG_ENA_MASK,
+ .vsel_reg = LOCHNAGAR2_MICVDD_CTRL2,
+ .vsel_mask = LOCHNAGAR2_MICVDD_VSEL_MASK,
+
+ .linear_ranges = lochnagar_micvdd_ranges,
+ .n_linear_ranges = ARRAY_SIZE(lochnagar_micvdd_ranges),
+
+ .enable_time = 3000,
+ .ramp_delay = 1000,
+
+ .owner = THIS_MODULE,
+ },
+ [LOCHNAGAR_MIC1VDD] = {
+ .name = "MIC1VDD",
+ .supply_name = "MICBIAS1",
+ .type = REGULATOR_VOLTAGE,
+ .ops = &lochnagar_micbias_ops,
+
+ .id = LOCHNAGAR_MIC1VDD,
+ .of_match = of_match_ptr("MIC1VDD"),
+ .of_parse_cb = lochnagar_micbias_of_parse,
+
+ .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
+ .enable_mask = LOCHNAGAR2_P1_INPUT_BIAS_ENA_MASK,
+
+ .owner = THIS_MODULE,
+ },
+ [LOCHNAGAR_MIC2VDD] = {
+ .name = "MIC2VDD",
+ .supply_name = "MICBIAS2",
+ .type = REGULATOR_VOLTAGE,
+ .ops = &lochnagar_micbias_ops,
+
+ .id = LOCHNAGAR_MIC2VDD,
+ .of_match = of_match_ptr("MIC2VDD"),
+ .of_parse_cb = lochnagar_micbias_of_parse,
+
+ .enable_reg = LOCHNAGAR2_ANALOGUE_PATH_CTRL2,
+ .enable_mask = LOCHNAGAR2_P2_INPUT_BIAS_ENA_MASK,
+
+ .owner = THIS_MODULE,
+ },
+ [LOCHNAGAR_VDDCORE] = {
+ .name = "VDDCORE",
+ .supply_name = "SYSVDD",
+ .type = REGULATOR_VOLTAGE,
+ .n_voltages = 57,
+ .ops = &lochnagar_vddcore_ops,
+
+ .id = LOCHNAGAR_VDDCORE,
+ .of_match = of_match_ptr("VDDCORE"),
+
+ .enable_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL1,
+ .enable_mask = LOCHNAGAR2_VDDCORE_CDC_REG_ENA_MASK,
+ .vsel_reg = LOCHNAGAR2_VDDCORE_CDC_CTRL2,
+ .vsel_mask = LOCHNAGAR2_VDDCORE_CDC_VSEL_MASK,
+
+ .linear_ranges = lochnagar_vddcore_ranges,
+ .n_linear_ranges = ARRAY_SIZE(lochnagar_vddcore_ranges),
+
+ .enable_time = 3000,
+ .ramp_delay = 1000,
+
+ .owner = THIS_MODULE,
+ },
+};
+
+static int lochnagar_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct lochnagar *lochnagar = dev_get_drvdata(dev->parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int ret, i;
+
+ config.dev = lochnagar->dev;
+ config.regmap = lochnagar->regmap;
+ config.driver_data = lochnagar;
+
+ for (i = 0; i < ARRAY_SIZE(lochnagar_regulators); i++) {
+ const struct regulator_desc *desc = &lochnagar_regulators[i];
+
+ rdev = devm_regulator_register(dev, desc, &config);
+ if (IS_ERR(rdev)) {
+ ret = PTR_ERR(rdev);
+ dev_err(dev, "Failed to register %s regulator: %d\n",
+ desc->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver lochnagar_regulator_driver = {
+ .driver = {
+ .name = "lochnagar-regulator",
+ },
+
+ .probe = lochnagar_regulator_probe,
+};
+module_platform_driver(lochnagar_regulator_driver);
+
+MODULE_AUTHOR("Charles Keepax <[email protected]>");
+MODULE_DESCRIPTION("Regulator driver for Cirrus Logic Lochnagar Board");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:lochnagar-regulator");
--
2.11.0
On Fri, Oct 19, 2018 at 10:50:02AM +0100, Charles Keepax wrote:
Please do not submit new versions of already applied patches, please
submit incremental updates to the existing code. Modifying existing
commits creates problems for other users building on top of those
commits so it's best practice to only change pubished git commits if
absolutely essential.
> +++ b/drivers/regulator/lochnagar-regulator.c
> @@ -0,0 +1,255 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Lochnagar regulator driver
Please don't mix C and C++ comments like this in the same block, just
have it be a C++ block so it looks consistent.
On 19/10/18 12:26, Mark Brown wrote:
> On Fri, Oct 19, 2018 at 10:50:02AM +0100, Charles Keepax wrote:
>
> Please do not submit new versions of already applied patches, please
> submit incremental updates to the existing code. Modifying existing
> commits creates problems for other users building on top of those
> commits so it's best practice to only change pubished git commits if
> absolutely essential.
>
>> +++ b/drivers/regulator/lochnagar-regulator.c
>> @@ -0,0 +1,255 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Lochnagar regulator driver
>
> Please don't mix C and C++ comments like this in the same block, just
> have it be a C++ block so it looks consistent.
>
Most SPDX headers on C files that I've looked at have it this way with a
C++ style comment above a C-style comment, though some don't. license-rules.rst
doesn't define how or if a SPDX comment line should be merged with the following
file header comment. I've had a bunch of patches in different subsystems all
accepted with this mixed format (copied from existing files). Doing the same as
existing files sounds reasonable but often isn't in the Linux kernel. It's a
common problem/barrier to kernel programming that existing code isn't a guide
and there isn't a consistent style across the kernel so one never really knows
what the coding style is until you've pushed a patch and annoyed a maintainer.
And then you adopt that style on your next patch and annoy different maintainer.
Maybe someone should update license-rules.rst to make a definite statement of the
style instead of leaving it to become another style that varies across the kernel
and between files.
On Fri, Oct 19, 2018 at 01:02:45PM +0100, Richard Fitzgerald wrote:
> Most SPDX headers on C files that I've looked at have it this way with a
> C++ style comment above a C-style comment, though some don't. license-rules.rst
> doesn't define how or if a SPDX comment line should be merged with the following
> file header comment. I've had a bunch of patches in different subsystems all
> accepted with this mixed format (copied from existing files). Doing the same as
Right, a lot of the conversions seem to have been done very
mechanically.
> Maybe someone should update license-rules.rst to make a definite statement of the
> style instead of leaving it to become another style that varies across the kernel
> and between files.
Well volunteered!
On Fri, Oct 19, 2018 at 12:26:22PM +0100, Mark Brown wrote:
> On Fri, Oct 19, 2018 at 10:50:02AM +0100, Charles Keepax wrote:
>
> Please do not submit new versions of already applied patches, please
> submit incremental updates to the existing code. Modifying existing
> commits creates problems for other users building on top of those
> commits so it's best practice to only change pubished git commits if
> absolutely essential.
>
I don't think any of this has been applied anywhere yet.
> > +++ b/drivers/regulator/lochnagar-regulator.c
> > @@ -0,0 +1,255 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Lochnagar regulator driver
>
> Please don't mix C and C++ comments like this in the same block, just
> have it be a C++ block so it looks consistent.
Can do.
Thanks,
Charles
On Fri, Oct 19, 2018 at 01:19:20PM +0100, Charles Keepax wrote:
> On Fri, Oct 19, 2018 at 12:26:22PM +0100, Mark Brown wrote:
> > Please do not submit new versions of already applied patches, please
> > submit incremental updates to the existing code. Modifying existing
> > commits creates problems for other users building on top of those
> > commits so it's best practice to only change pubished git commits if
> > absolutely essential.
> I don't think any of this has been applied anywhere yet.
It looks a lot like
bef9391cbec547351c6a13e52f3a26bb2d271ec7 regulator: lochnagar: Add support for the Cirrus Logic Lochnagar
in -next.
On Fri, Oct 19, 2018 at 01:21:18PM +0100, Mark Brown wrote:
> On Fri, Oct 19, 2018 at 01:19:20PM +0100, Charles Keepax wrote:
> > On Fri, Oct 19, 2018 at 12:26:22PM +0100, Mark Brown wrote:
>
> > > Please do not submit new versions of already applied patches, please
> > > submit incremental updates to the existing code. Modifying existing
> > > commits creates problems for other users building on top of those
> > > commits so it's best practice to only change pubished git commits if
> > > absolutely essential.
>
> > I don't think any of this has been applied anywhere yet.
>
> It looks a lot like
>
> bef9391cbec547351c6a13e52f3a26bb2d271ec7 regulator: lochnagar: Add support for the Cirrus Logic Lochnagar
>
> in -next.
Well it sure does look a lot like my patch :-). Apologies
something must have gone wrong on one of our ends as I don't
seem to have ever received your usual confirmation email it
was merged. I will try to keep a closer eye on people's trees
in future.
No changes in this version, was just updating other patches in the
chain for some feedback so no harm done. Will send an incremental
patch for comment style.
Thanks,
Charles
On Fri, Oct 19, 2018 at 01:34:42PM +0100, Charles Keepax wrote:
> On Fri, Oct 19, 2018 at 01:21:18PM +0100, Mark Brown wrote:
> > It looks a lot like
> > bef9391cbec547351c6a13e52f3a26bb2d271ec7 regulator: lochnagar: Add support for the Cirrus Logic Lochnagar
> > in -next.
> Well it sure does look a lot like my patch :-). Apologies
> something must have gone wrong on one of our ends as I don't
> seem to have ever received your usual confirmation email it
> was merged. I will try to keep a closer eye on people's trees
> in future.
They do sometimes seem to get flagged as spam, I think due to the amount
of boilerplate and possibly even the fact that they include the patches.
Quoting Charles Keepax (2018-10-19 02:49:59)
> Lochnagar is an evaluation and development board for Cirrus
> Logic Smart CODEC and Amp devices. It allows the connection of
> most Cirrus Logic devices on mini-cards, as well as allowing
> connection of various application processor systems to provide a
> full evaluation platform. This driver supports the board
> controller chip on the Lochnagar board.
>
> Signed-off-by: Charles Keepax <[email protected]>
> Acked-by: Rob Herring <[email protected]>
> ---
Acked-by: Stephen Boyd <[email protected]>
On Fri, Oct 19, 2018 at 11:50 AM Charles Keepax
<[email protected]> wrote:
> Lochnagar is an evaluation and development board for Cirrus
> Logic Smart CODEC and Amp devices. It allows the connection of
> most Cirrus Logic devices on mini-cards, as well as allowing
> connection of various application processor systems to provide a
> full evaluation platform. This driver supports the board
> controller chip on the Lochnagar board.
>
> Lochnagar provides many pins which can generally be used for an
> audio function such as an AIF or a PDM interface, but also as
> GPIOs.
>
> Signed-off-by: Charles Keepax <[email protected]>
> ---
>
> Changes since v2:
> - Updated aif-master/aif-slave to use a vendor prefix
> - Correctly get and put of_node pointer in probe/remove
Looks better and better! Some comments, probably not the last comments:
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinconf-generic.h>
#include <linux/bits.h>
> +struct lochnagar_aif {
> + const char name[16];
> +
> + unsigned int pins[4];
> +
> + unsigned int src_reg;
u32?
> + unsigned int src_mask;
u32?
> +
> + unsigned int ctrl_reg;
u32?
> + unsigned int ena_mask;
u32?
> + unsigned int master_mask;
u32?
> +struct lochnagar_func {
> + const char * const name;
> +
> + enum lochnagar_func_type type;
> +
> + int op;
unsigned int? u32?
Looks like u8 actually.
> +struct lochnagar_group {
> + const char * const name;
> +
> + enum lochnagar_func_type type;
> +
> + const unsigned int *pins;
> + int npins;
unsigned int npins?
> +struct lochnagar_func_groups {
> + const char **groups;
> + int ngroups;
unsigned int ngroups?
> +struct lochnagar_pin_priv {
> + struct lochnagar *lochnagar;
> + struct device *dev;
> +
> + const struct lochnagar_func *funcs;
> + int nfuncs;
> +
> + const struct pinctrl_pin_desc *pins;
> + int npins;
> +
> + const struct lochnagar_group *groups;
> + int ngroups;
> +
> + struct lochnagar_func_groups func_groups[LN_FTYPE_COUNT];
> +
> + struct gpio_chip gpio_chip;
> +};
I bet some of these should be unsigned as well.
> +static const struct pinconf_generic_params lochnagar_dt_params[] = {
> + {"cirrus,aif-master", PIN_CONFIG_AIFMASTER, 0},
> + {"cirrus,aif-slave", PIN_CONFIG_AIFSLAVE, 0},
> +};
I guess these are documented in the device tree bindings.
They look suspciously like muxing but I guess it is explained
there.
> +static void lochnagar_gpio_set(struct gpio_chip *chip,
> + unsigned int offset, int value)
> +{
> + struct lochnagar_pin_priv *priv = gpiochip_get_data(chip);
> + struct lochnagar *lochnagar = priv->lochnagar;
> + const struct lochnagar_pin *pin = priv->pins[offset].drv_data;
> + int ret;
> +
> + value = !!value;
value = value ? BIT(pin->shift) : 0;
> + dev_dbg(priv->dev, "Set GPIO %s to %s\n",
> + pin->name, value ? "high" : "low");
> +
> + switch (pin->type) {
> + case LN_PTYPE_MUX:
> + value |= LN2_OP_GPIO;
> +
> + ret = lochnagar_pin_set_mux(priv, pin, value);
> + break;
> + case LN_PTYPE_GPIO:
> + if (pin->invert)
> + value = !value;
> +
> + ret = regmap_update_bits(lochnagar->regmap, pin->reg,
> + 0x1 << pin->shift,
BIT(pin->shift)
> + value << pin->shift);
Just value provided you used the construction above
to construct it.
Yours,
Linus Walleij
On Fri, Oct 19, 2018 at 11:50 AM Charles Keepax
<[email protected]> wrote:
> + gf-aif2-txdat, usb-aif1, usb-aif2, adat-aif, soundcard-aif,
> +
> + - aif-master : Specifies that an AIF group will be used as a master
> + interface (either this or aif-slave is required if a group is
> + being muxed to an AIF)
> + - aif-slave : Specifies that an AIF group will be used as a slave
> + interface (either this or aif-master is required if a group is
> + being muxed to an AIF)
This is not properly described as pin config parameters, but that is how
they are used in the driver. Please describe that this is supposed to
augment the pin config of the pins.
But I think it looks bogus. If the pins are already mixed to groups
like that:
> + pin-settings: pin-settings {
> + ap-aif {
> + aif-slave;
> + groups = "gf-aif1";
> + function = "codec-aif3";
> + };
> + codec-aif {
> + aif-master;
> + groups = "codec-aif3";
> + function = "gf-aif1";
> + };
> + };
OK so what the "special properties" above indicates is really the
direction of the pins, whether out (master) or in (slave".
Just use the standard bool pin config properties:
input-enable;
output-enable;
Reference
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
For this. If you want users to see that this is master and slave,
just /* Write a comment */ in the device tree.
Yours,
Linus Walleij
On Tue, Oct 30, 2018 at 02:04:20PM +0100, Linus Walleij wrote:
> On Fri, Oct 19, 2018 at 11:50 AM Charles Keepax
> <[email protected]> wrote:
>
> > Lochnagar is an evaluation and development board for Cirrus
> > Logic Smart CODEC and Amp devices. It allows the connection of
> > most Cirrus Logic devices on mini-cards, as well as allowing
> > connection of various application processor systems to provide a
> > full evaluation platform. This driver supports the board
> > controller chip on the Lochnagar board.
> >
> > Lochnagar provides many pins which can generally be used for an
> > audio function such as an AIF or a PDM interface, but also as
> > GPIOs.
> >
> > Signed-off-by: Charles Keepax <[email protected]>
> > ---
> > +static void lochnagar_gpio_set(struct gpio_chip *chip,
> > + unsigned int offset, int value)
> > +{
> > + struct lochnagar_pin_priv *priv = gpiochip_get_data(chip);
> > + struct lochnagar *lochnagar = priv->lochnagar;
> > + const struct lochnagar_pin *pin = priv->pins[offset].drv_data;
> > + int ret;
> > +
> > + value = !!value;
>
> value = value ? BIT(pin->shift) : 0;
>
I think this will end up more complex because...
> > + dev_dbg(priv->dev, "Set GPIO %s to %s\n",
> > + pin->name, value ? "high" : "low");
> > +
> > + switch (pin->type) {
> > + case LN_PTYPE_MUX:
> > + value |= LN2_OP_GPIO;
I want the value to be in the lowest bit for this path.
> > +
> > + ret = lochnagar_pin_set_mux(priv, pin, value);
> > + break;
> > + case LN_PTYPE_GPIO:
> > + if (pin->invert)
> > + value = !value;
And then I need to invert the value here.
> > +
> > + ret = regmap_update_bits(lochnagar->regmap, pin->reg,
> > + 0x1 << pin->shift,
>
> BIT(pin->shift)
>
> > + value << pin->shift);
>
> Just value provided you used the construction above
> to construct it.
>
All the other comments look good will fixup for the next spin.
Thanks,
Charles