From patchwork Mon Apr 17 17:15:55 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214386 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 1D7BFC77B7A for ; Mon, 17 Apr 2023 17:18:00 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 004F2DF8; Mon, 17 Apr 2023 19:17:08 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 004F2DF8 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751878; bh=yjb3bN6PhnuGaUiAnYdcrM+RKuQmHgrePH357KnOgpw=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=Kd2hyQJjjIAZjc0iB4pQhqlqVg49Sn5VwzYPH7Vdf6Kim36ndelj3bN+r832bKQt+ niMsNi+jiKIhFlz09ldOcq9NUc50BUpMs1/7fnrNR/x5RU2bYdEBu0tDOrlJqSFF5z Sl1dntP4W3hkDSiFozzreTbc0tQUTiGbKslhaoog= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id A040BF8052D; Mon, 17 Apr 2023 19:16:23 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 1/7] dt-bindings: mfd: Add the Lantiq PEF2256 E1/T1/J1 framer Date: Mon, 17 Apr 2023 19:15:55 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175178251.26.15661436468102320547@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni , Krzysztof Kozlowski Content-Disposition: inline The Lantiq PEF2256 is a framer and line interface component designed to fulfill all required interfacing between an analog E1/T1/J1 line and the digital PCM system highway/H.100 bus. Signed-off-by: Herve Codina Reviewed-by: Krzysztof Kozlowski --- .../bindings/mfd/lantiq,pef2256.yaml | 267 ++++++++++++++++++ 1 file changed, 267 insertions(+) create mode 100644 Documentation/devicetree/bindings/mfd/lantiq,pef2256.yaml diff --git a/Documentation/devicetree/bindings/mfd/lantiq,pef2256.yaml b/Documentation/devicetree/bindings/mfd/lantiq,pef2256.yaml new file mode 100644 index 000000000000..7bc29e93cbc6 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/lantiq,pef2256.yaml @@ -0,0 +1,267 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/lantiq,pef2256.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Lantiq PEF2256 + +maintainers: + - Herve Codina + +description: + The Lantiq PEF2256, also known as Infineon PEF2256 or FALC56, is a framer and + line interface component designed to fulfill all required interfacing between + an analog E1/T1/J1 line and the digital PCM system highway/H.100 bus. + +properties: + compatible: + items: + - const: lantiq,pef2256 + - const: simple-mfd + + reg: + maxItems: 1 + + clocks: + items: + - description: Master clock + - description: Receive System Clock + - description: Transmit System Clock + + clock-names: + items: + - const: mclk + - const: sclkr + - const: sclkx + + interrupts: + maxItems: 1 + + reset-gpios: + description: + GPIO used to reset the device. + maxItems: 1 + + pinctrl: + $ref: /schemas/pinctrl/pinctrl.yaml# + additionalProperties: false + + patternProperties: + '-pins$': + type: object + $ref: /schemas/pinctrl/pincfg-node.yaml# + additionalProperties: false + + properties: + pins: + enum: [ RPA, RPB, RPC, RPD, XPA, XPB, XPC, XPD ] + + function: + enum: [ SYPR, RFM, RFMB, RSIGM, RSIG, DLR, FREEZE, RFSP, LOS, + SYPX, XFMS, XSIG, TCLK, XMFB, XSIGM, DLX, XCLK, XLT, + GPI, GPOH, GPOL ] + + required: + - pins + - function + + lantiq,line-interface: + $ref: /schemas/types.yaml#/definitions/string + enum: [e1, t1j1] + default: e1 + description: | + The line interface type + - e1: E1 line + - t1j1: T1/J1 line + + lantiq,frame-format: + $ref: /schemas/types.yaml#/definitions/string + description: + The line interface frame format. + + lantiq,data-rate-bps: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [2048000, 4096000, 8192000, 16384000] + default: 2048000 + description: + Data rate (bit per seconds) on the system highway. + + lantiq,clock-falling-edge: + $ref: /schemas/types.yaml#/definitions/flag + description: + Data is sent on falling edge of the clock (and received on the rising + edge). If 'clock-falling-edge' is not present, data is sent on the + rising edge (and received on the falling edge). + + lantiq,channel-phase: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3, 4, 5, 6, 7] + default: 0 + description: + The pef2256 delivers a full frame (32 8bit time-slots in E1 and 24 8bit + time-slots 8 8bit signaling in E1/J1) every 125us. This lead to a data + rate of 2048000 bit/s. When lantiq,data-rate-bps is more than 2048000 + bit/s, the data (all 32 8bit) present in the frame are interleave with + unused time-slots. The lantiq,channel-phase property allows to set the + correct alignment of the interleave mechanism. + For instance, suppose lantiq,data-rate-bps = 8192000 (ie 4*2048000), and + lantiq,channel-phase = 2, the interleave schema with unused time-slots + (nu) and used time-slots (XX) for TSi is + nu nu XX nu nu nu XX nu nu nu XX nu + <-- TSi --> <- TSi+1 -> <- TSi+2 -> + With lantiq,data-rate-bps = 8192000, and lantiq,channel-phase = 1, the + interleave schema is + nu XX nu nu nu XX nu nu nu XX nu nu + <-- TSi --> <- TSi+1 -> <- TSi+2 -> + With lantiq,data-rate-bps = 4096000 (ie 2*2048000), and + lantiq,channel-phase = 1, the interleave schema is + nu XX nu XX nu XX + <-- TSi --> <- TSi+1 -> <- TSi+2 -> + + lantiq,subordinate: + $ref: /schemas/types.yaml#/definitions/flag + description: + If present, the pef2256 works in subordinate mode. In this mode it + synchronizes on line interface clock signals. Otherwise, it synchronizes + on internal clocks. + +patternProperties: + '^codec(-([0-9]|[1-2][0-9]|3[0-1]))?$': + type: object + $ref: /schemas/sound/dai-common.yaml + unevaluatedProperties: false + description: + Codec provided by the pef2256. This codec allows to use some of the PCM + system highway time-slots as audio channels to transport audio data over + the E1/T1/J1 lines. + The time-slots used by the codec must be set and so, the properties + 'dai-tdm-slot-num', 'dai-tdm-slot-width', 'dai-tdm-slot-tx-mask' and + 'dai-tdm-slot-rx-mask' must be present in the sound card node for + sub-nodes that involve the codec. The codec uses 8bit time-slots. + 'dai-tdm-tdm-slot-with' must be set to 8. + The tx and rx masks define the pef2256 time-slots assigned to the codec. + + properties: + compatible: + const: lantiq,pef2256-codec + + '#sound-dai-cells': + const: 0 + + required: + - compatible + - '#sound-dai-cells' + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + +allOf: + - if: + properties: + lantiq,line-interface: + contains: + const: e1 + then: + properties: + lantiq,frame-format: + enum: [doubleframe, crc4-multiframe, auto-multiframe] + default: doubleframe + description: | + The E1 line interface frame format + - doubleframe: Doubleframe format + - crc4-multiframe: CRC4 multiframe format + - auto-multiframe: CRC4 multiframe format with interworking + capabilities (ITU-T G.706 Annex B) + + else: + # T1/J1 line + properties: + lantiq,frame-format: + enum: [4frame, 12frame, 24frame, 72frame] + default: 12frame + description: | + The T1/J1 line interface frame format + - 4frame: 4-frame multiframe format (F4) + - 12frame: 12-frame multiframe format (F12, D3/4) + - 24frame: 24-frame multiframe format (ESF) + - 72frame: 72-frame multiframe format (F72, remote switch mode) + +additionalProperties: false + +examples: + - | + #include + #include + + framer@2000000 { + compatible = "lantiq,pef2256", "simple-mfd"; + reg = <0x2000000 0x100>; + interrupts = <8 IRQ_TYPE_LEVEL_LOW>; + interrupt-parent = <&intc>; + clocks = <&clk_mclk>, <&clk_sclkr>, <&clk_sclkx>; + clock-names = "mclk", "sclkr", "sclkx"; + reset-gpios = <&gpio 11 GPIO_ACTIVE_LOW>; + lantiq,data-rate-bps = <4096000>; + + pinctrl { + pef2256_rpa_sypr: rpa-pins { + pins = "RPA"; + function = "SYPR"; + }; + pef2256_xpa_sypx: xpa-pins { + pins = "XPA"; + function = "SYPX"; + }; + }; + + pef2256_codec0: codec-0 { + compatible = "lantiq,pef2256-codec"; + #sound-dai-cells = <0>; + sound-name-prefix = "PEF2256_0"; + }; + + pef2256_codec1: codec-1 { + compatible = "lantiq,pef2256-codec"; + #sound-dai-cells = <0>; + sound-name-prefix = "PEF2256_1"; + }; + }; + + sound { + compatible = "simple-audio-card"; + #address-cells = <1>; + #size-cells = <0>; + simple-audio-card,dai-link@0 { /* CPU DAI1 - pef2256 codec 1 */ + reg = <0>; + cpu { + sound-dai = <&cpu_dai1>; + }; + codec { + sound-dai = <&pef2256_codec0>; + dai-tdm-slot-num = <4>; + dai-tdm-slot-width = <8>; + /* TS 1, 2, 3, 4 */ + dai-tdm-slot-tx-mask = <0 1 1 1 1>; + dai-tdm-slot-rx-mask = <0 1 1 1 1>; + }; + }; + simple-audio-card,dai-link@1 { /* CPU DAI2 - pef2256 codec 2 */ + reg = <1>; + cpu { + sound-dai = <&cpu_dai2>; + }; + codec { + sound-dai = <&pef2256_codec1>; + dai-tdm-slot-num = <4>; + dai-tdm-slot-width = <8>; + /* TS 5, 6, 7, 8 */ + dai-tdm-slot-tx-mask = <0 0 0 0 0 1 1 1 1>; + dai-tdm-slot-rx-mask = <0 0 0 0 0 1 1 1 1>; + }; + }; + }; From patchwork Mon Apr 17 17:15:56 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214387 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id EBC4EC77B7A for ; Mon, 17 Apr 2023 17:18:07 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 6F15AE0F; Mon, 17 Apr 2023 19:17:15 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 6F15AE0F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751885; bh=THEBJp10W2qpm6iYvWk8seFmU4NYrWoGvHDNxm01FDY=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=PjfjdwIFX+xycbKeE4Cu4GW9ugyePYZ991+XyxQHJ1hT9s9C6AkoK6lpiIOIcmdZW uQUqiN6iz3s3HAx5VCaLeiQdHgdDH0AO1XEXBcrH/M/XgUOYNUHClatC5qtP2cAZqO FYnBtBI5SCRBw8kpCjMUwfpG06AmnCMQWphViLeI= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 36636F8053B; Mon, 17 Apr 2023 19:16:25 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 2/7] mfd: core: Ensure disabled devices are skiped without aborting Date: Mon, 17 Apr 2023 19:15:56 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175178434.26.279057837959556495@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni Content-Disposition: inline The loop searching for a matching device based on its compatible string is aborted when a matching disabled device is found. This abort avoid to add devices as soon as one disabled device is found. Continue searching for an other device instead of aborting on the first disabled one fixes the issue. Fixes: 22380b65dc70 ("mfd: mfd-core: Ensure disabled devices are ignored without error") Signed-off-by: Herve Codina --- drivers/mfd/mfd-core.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 16d1861e9682..7c47b50b358d 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -176,6 +176,7 @@ static int mfd_add_device(struct device *parent, int id, struct platform_device *pdev; struct device_node *np = NULL; struct mfd_of_node_entry *of_entry, *tmp; + bool not_available; int ret = -ENOMEM; int platform_id; int r; @@ -211,13 +212,13 @@ static int mfd_add_device(struct device *parent, int id, goto fail_res; if (IS_ENABLED(CONFIG_OF) && parent->of_node && cell->of_compatible) { + not_available = false; for_each_child_of_node(parent->of_node, np) { if (of_device_is_compatible(np, cell->of_compatible)) { - /* Ignore 'disabled' devices error free */ + /* Skip 'disabled' devices */ if (!of_device_is_available(np)) { - of_node_put(np); - ret = 0; - goto fail_alias; + not_available = true; + continue; } ret = mfd_match_of_node_to_dev(pdev, np, cell); @@ -227,10 +228,17 @@ static int mfd_add_device(struct device *parent, int id, if (ret) goto fail_alias; - break; + goto match; } } + if (not_available) { + /* Ignore 'disabled' devices error free */ + ret = 0; + goto fail_alias; + } + +match: if (!pdev->dev.of_node) pr_warn("%s: Failed to locate of_node [id: %d]\n", cell->name, platform_id); From patchwork Mon Apr 17 17:15:57 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214395 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id B82BBC77B7C for ; Mon, 17 Apr 2023 17:19:37 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id A3503DFA; Mon, 17 Apr 2023 19:18:45 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz A3503DFA DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751975; bh=5rmbwGT1AY7ZEhJR5IvQRsQs+jZJC/ZpNjOp6uhbHmc=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=OAL2psYSAGeMt5WhRwRX44mcB/N2ogCS4N3IKbJZExmOugU6+6QWuigCbX2PdBhQh m1JLdXe7ZjnA1Hmfz0ta+N98VskM2GZaRuQLFLsNzfkDNbKHsf8V5LxMWNE/A+1kUT Jd8XT1KRjQNsm52zZnLFn6BYpGP7LpjwLB3D7Duw= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id AC05BF80579; Mon, 17 Apr 2023 19:16:40 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 3/7] mfd: Add support for the Lantiq PEF2256 framer Date: Mon, 17 Apr 2023 19:15:57 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175179946.26.2336340497826172416@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni Content-Disposition: inline The Lantiq PEF2256 is a framer and line interface component designed to fulfill all required interfacing between an analog E1/T1/J1 line and the digital PCM system highway/H.100 bus. Signed-off-by: Herve Codina --- drivers/mfd/Kconfig | 16 + drivers/mfd/Makefile | 1 + drivers/mfd/pef2256-regs.h | 250 ++++++++++ drivers/mfd/pef2256.c | 950 ++++++++++++++++++++++++++++++++++++ include/linux/mfd/pef2256.h | 52 ++ 5 files changed, 1269 insertions(+) create mode 100644 drivers/mfd/pef2256-regs.h create mode 100644 drivers/mfd/pef2256.c create mode 100644 include/linux/mfd/pef2256.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index fcc141e067b9..d0c5d1e9f950 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1063,6 +1063,22 @@ config PCF50633_GPIO Say yes here if you want to include support GPIO for pins on the PCF50633 chip. +config MFD_PEF2256 + tristate "Lantiq PEF2256 (FALC56) framer" + depends on OF + select MFD_CORE + select REGMAP_MMIO + help + This option enables support for the Lantiq PEF2256 framer, also known + as FALC56. This framer and its line interface component is designed + to fulfill all required interfacing between analog E1/T1/J1 lines and + the digital PCM system highway. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pef2256. + config MFD_PM8XXX tristate "Qualcomm PM8xxx PMIC chips driver" depends on (ARM || HEXAGON || COMPILE_TEST) diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2f6c89d1e277..b2bb6e6a141c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -195,6 +195,7 @@ obj-$(CONFIG_MFD_SI476X_CORE) += si476x-core.o obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o omap-usb-tll.o +obj-$(CONFIG_MFD_PEF2256) += pef2256.o obj-$(CONFIG_MFD_PM8XXX) += qcom-pm8xxx.o ssbi.o obj-$(CONFIG_MFD_QCOM_RPM) += qcom_rpm.o obj-$(CONFIG_MFD_SPMI_PMIC) += qcom-spmi-pmic.o diff --git a/drivers/mfd/pef2256-regs.h b/drivers/mfd/pef2256-regs.h new file mode 100644 index 000000000000..5d3183c91714 --- /dev/null +++ b/drivers/mfd/pef2256-regs.h @@ -0,0 +1,250 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PEF2256 registers definition + * + * Copyright 2023 CS GROUP France + * + * Author: Herve Codina + */ +#ifndef __PEF2256_REGS_H__ +#define __PEF2256_REGS_H__ + +#include "linux/bitfield.h" + +/* Command Register */ +#define PEF2256_CMDR 0x02 +#define PEF2256_CMDR_RRES BIT(6) +#define PEF2256_CMDR_XRES BIT(4) +#define PEF2256_CMDR_SRES BIT(0) + +/* Interrupt Mask Register 0..5 */ +#define PEF2256_IMR0 0x14 +#define PEF2256_IMR1 0x15 +#define PEF2256_IMR2 0x16 +#define PEF2256_IMR3 0x17 +#define PEF2256_IMR4 0x18 +#define PEF2256_IMR5 0x19 + +/* Framer Mode Register 0 */ +#define PEF2256_FMR0 0x1C +#define PEF2256_FMR0_XC_MASK GENMASK(7, 6) +#define PEF2256_FMR0_XC_NRZ FIELD_PREP_CONST(PEF2256_FMR0_XC_MASK, 0x0) +#define PEF2256_FMR0_XC_CMI FIELD_PREP_CONST(PEF2256_FMR0_XC_MASK, 0x1) +#define PEF2256_FMR0_XC_AMI FIELD_PREP_CONST(PEF2256_FMR0_XC_MASK, 0x2) +#define PEF2256_FMR0_XC_HDB3 FIELD_PREP_CONST(PEF2256_FMR0_XC_MASK, 0x3) +#define PEF2256_FMR0_RC_MASK GENMASK(5, 4) +#define PEF2256_FMR0_RC_NRZ FIELD_PREP_CONST(PEF2256_FMR0_RC_MASK, 0x0) +#define PEF2256_FMR0_RC_CMI FIELD_PREP_CONST(PEF2256_FMR0_RC_MASK, 0x1) +#define PEF2256_FMR0_RC_AMI FIELD_PREP_CONST(PEF2256_FMR0_RC_MASK, 0x2) +#define PEF2256_FMR0_RC_HDB3 FIELD_PREP_CONST(PEF2256_FMR0_RC_MASK, 0x3) + +/* Framer Mode Register 1 */ +#define PEF2256_FMR1 0x1D +#define PEF2256_FMR1_XFS BIT(3) +#define PEF2256_FMR1_ECM BIT(2) +/* SSD is defined on 2 bits. The other bit is on SIC1 register */ +#define PEF2256_FMR1_SSD_MASK GENMASK(1, 1) +#define PEF2256_FMR1_SSD_2048 FIELD_PREP_CONST(PEF2256_FMR1_SSD_MASK, 0x0) +#define PEF2256_FMR1_SSD_4096 FIELD_PREP_CONST(PEF2256_FMR1_SSD_MASK, 0x1) +#define PEF2256_FMR1_SSD_8192 FIELD_PREP_CONST(PEF2256_FMR1_SSD_MASK, 0x0) +#define PEF2256_FMR1_SSD_16384 FIELD_PREP_CONST(PEF2256_FMR1_SSD_MASK, 0x1) + +/* Framer Mode Register 2 */ +#define PEF2256_FMR2 0x1E +#define PEF2256_FMR2_RFS_MASK GENMASK(7, 6) +#define PEF2256_FMR2_RFS_DOUBLEFRAME FIELD_PREP_CONST(PEF2256_FMR2_RFS_MASK, 0x0) +#define PEF2256_FMR2_RFS_CRC4_MULTIFRAME FIELD_PREP_CONST(PEF2256_FMR2_RFS_MASK, 0x2) +#define PEF2256_FMR2_RFS_AUTO_MULTIFRAME FIELD_PREP_CONST(PEF2256_FMR2_RFS_MASK, 0x3) +#define PEF2256_FMR2_AXRA BIT(1) + +/* Transmit Service Word */ +#define PEF2256_XSW 0x20 +#define PEF2256_XSW_XSIS BIT(7) +#define PEF2256_XSW_XTM BIT(6) +#define PEF2256_XSW_XY_MASK GENMASK(5, 0) +#define PEF2256_XSW_XY(_v) FIELD_PREP(PEF2256_XSW_XY_MASK, _v) + +/* Transmit Spare Bits */ +#define PEF2256_XSP 0x21 +#define PEF2256_XSP_XSIF BIT(2) + +/* Transmit Control 0..1 */ +#define PEF2256_XC0 0x22 +#define PEF2256_XC1 0x23 + +/* Receive Control 0 */ +#define PEF2256_RC0 0x24 +#define PEF2256_RC0_SWD BIT(7) +#define PEF2256_RC0_ASY4 BIT(6) + +/* Receive Control 1 */ +#define PEF2256_RC1 0x25 + +/* Transmit Pulse Mask 0..1 */ +#define PEF2256_XPM0 0x26 +#define PEF2256_XPM1 0x27 + +/* Transmit Pulse Mask 2 */ +#define PEF2256_XPM2 0x28 +#define PEF2256_XPM2_XLT BIT(6) + +/* Transparent Service Word Mask */ +#define PEF2256_TSWM 0x29 + +/* Line Interface Mode 0 */ +#define PEF2256_LIM0 0x36 +#define PEF2256_2X_LIM0_BIT3 BIT(3) /* v2.x, described as a forced '1' bit */ +#define PEF2256_LIM0_MAS BIT(0) + +/* Line Interface Mode 1 */ +#define PEF2256_LIM1 0x37 +#define PEF2256_12_LIM1_RIL_MASK GENMASK(6, 4) +#define PEF2256_12_LIM1_RIL_910 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x0) +#define PEF2256_12_LIM1_RIL_740 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x1) +#define PEF2256_12_LIM1_RIL_590 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x2) +#define PEF2256_12_LIM1_RIL_420 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x3) +#define PEF2256_12_LIM1_RIL_320 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x4) +#define PEF2256_12_LIM1_RIL_210 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x5) +#define PEF2256_12_LIM1_RIL_160 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x6) +#define PEF2256_12_LIM1_RIL_100 FIELD_PREP_CONST(PEF2256_12_LIM1_RIL_MASK, 0x7) +#define PEF2256_2X_LIM1_RIL_MASK GENMASK(6, 4) +#define PEF2256_2X_LIM1_RIL_2250 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x0) +#define PEF2256_2X_LIM1_RIL_1100 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x1) +#define PEF2256_2X_LIM1_RIL_600 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x2) +#define PEF2256_2X_LIM1_RIL_350 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x3) +#define PEF2256_2X_LIM1_RIL_210 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x4) +#define PEF2256_2X_LIM1_RIL_140 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x5) +#define PEF2256_2X_LIM1_RIL_100 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x6) +#define PEF2256_2X_LIM1_RIL_50 FIELD_PREP_CONST(PEF2256_2X_LIM1_RIL_MASK, 0x7) + +/* Pulse Count Detection */ +#define PEF2256_PCD 0x38 + + /* Pulse Count Recovery */ +#define PEF2256_PCR 0x39 + + /* Line Interface Mode 2 */ +#define PEF2256_LIM2 0x3A +#define PEF2256_LIM2_SLT_MASK GENMASK(5, 4) +#define PEF2256_LIM2_SLT_THR55 FIELD_PREP_CONST(PEF2256_LIM2_SLT_MASK, 0x0) +#define PEF2256_LIM2_SLT_THR67 FIELD_PREP_CONST(PEF2256_LIM2_SLT_MASK, 0x1) +#define PEF2256_LIM2_SLT_THR50 FIELD_PREP_CONST(PEF2256_LIM2_SLT_MASK, 0x2) +#define PEF2256_LIM2_SLT_THR45 FIELD_PREP_CONST(PEF2256_LIM2_SLT_MASK, 0x3) +#define PEF2256_LIM2_ELT BIT(2) + +/* System Interface Control 1 */ +#define PEF2256_SIC1 0x3E +#define PEF2256_SIC1_SSC_MASK (BIT(7) | BIT(3)) +#define PEF2256_SIC1_SSC_2048 (0) +#define PEF2256_SIC1_SSC_4096 BIT(3) +#define PEF2256_SIC1_SSC_8192 BIT(7) +#define PEF2256_SIC1_SSC_16384 (BIT(7) | BIT(3)) +/* SSD is defined on 2 bits. The other bit is on FMR1 register */ +#define PEF2256_SIC1_SSD_MASK GENMASK(6, 6) +#define PEF2256_SIC1_SSD_2048 FIELD_PREP_CONST(PEF2256_SIC1_SSD_MASK, 0x0) +#define PEF2256_SIC1_SSD_4096 FIELD_PREP_CONST(PEF2256_SIC1_SSD_MASK, 0x0) +#define PEF2256_SIC1_SSD_8192 FIELD_PREP_CONST(PEF2256_SIC1_SSD_MASK, 0x1) +#define PEF2256_SIC1_SSD_16384 FIELD_PREP_CONST(PEF2256_SIC1_SSD_MASK, 0x1) +#define PEF2256_SIC1_RBS_MASK GENMASK(5, 4) +#define PEF2256_SIC1_RBS_2FRAMES FIELD_PREP_CONST(PEF2256_SIC1_RBS_MASK, 0x0) +#define PEF2256_SIC1_RBS_1FRAME FIELD_PREP_CONST(PEF2256_SIC1_RBS_MASK, 0x1) +#define PEF2256_SIC1_RBS_96BITS FIELD_PREP_CONST(PEF2256_SIC1_RBS_MASK, 0x2) +#define PEF2256_SIC1_RBS_BYPASS FIELD_PREP_CONST(PEF2256_SIC1_RBS_MASK, 0x3) +#define PEF2256_SIC1_XBS_MASK GENMASK(1, 0) +#define PEF2256_SIC1_XBS_BYPASS FIELD_PREP_CONST(PEF2256_SIC1_XBS_MASK, 0x0) +#define PEF2256_SIC1_XBS_1FRAME FIELD_PREP_CONST(PEF2256_SIC1_XBS_MASK, 0x1) +#define PEF2256_SIC1_XBS_2FRAMES FIELD_PREP_CONST(PEF2256_SIC1_XBS_MASK, 0x2) +#define PEF2256_SIC1_XBS_96BITS FIELD_PREP_CONST(PEF2256_SIC1_XBS_MASK, 0x3) + +/* System Interface Control 2 */ +#define PEF2256_SIC2 0x3F +#define PEF2256_SIC2_SICS_MASK GENMASK(3, 1) +#define PEF2256_SIC2_SICS(_v) FIELD_PREP(PEF2256_SIC2_SICS_MASK, _v) + +/* System Interface Control 3 */ +#define PEF2256_SIC3 0x40 +#define PEF2256_SIC3_RTRI BIT(5) +#define PEF2256_SIC3_RESX BIT(3) +#define PEF2256_SIC3_RESR BIT(2) + +/* Clock Mode Register 1 */ +#define PEF2256_CMR1 0x44 +#define PEF2256_CMR1_RS_MASK GENMASK(5, 4) +#define PEF2256_CMR1_RS_DPLL FIELD_PREP_CONST(PEF2256_CMR1_RS_MASK, 0x0) +#define PEF2256_CMR1_RS_DPLL_LOS_HIGH FIELD_PREP_CONST(PEF2256_CMR1_RS_MASK, 0x1) +#define PEF2256_CMR1_RS_DCOR_2048 FIELD_PREP_CONST(PEF2256_CMR1_RS_MASK, 0x2) +#define PEF2256_CMR1_RS_DCOR_8192 FIELD_PREP_CONST(PEF2256_CMR1_RS_MASK, 0x3) +#define PEF2256_CMR1_DCS BIT(3) + +/* Clock Mode Register 2 */ +#define PEF2256_CMR2 0x45 +#define PEF2256_CMR2_DCOXC BIT(5) + +/* Global Configuration Register */ +#define PEF2256_GCR 0x46 +#define PEF2256_GCR_SCI BIT(6) +#define PEF2256_GCR_ECMC BIT(4) + +/* Port Configuration 5 */ +#define PEF2256_PC5 0x84 +#define PEF2256_PC5_CRP BIT(0) + +/* Global Port Configuration 1 */ +#define PEF2256_GPC1 0x85 +#define PEF2256_GPC1_CSFP_MASK GENMASK(7, 5) +#define PEF2256_GPC1_CSFP_SEC_IN_HIGH FIELD_PREP_CONST(PEF2256_GPC1_CSFP_MASK, 0x0) +#define PEF2256_GPC1_CSFP_SEC_OUT_HIGH FIELD_PREP_CONST(PEF2256_GPC1_CSFP_MASK, 0x1) +#define PEF2256_GPC1_CSFP_FSC_OUT_HIGH FIELD_PREP_CONST(PEF2256_GPC1_CSFP_MASK, 0x2) +#define PEF2256_GPC1_CSFP_FSC_OUT_LOW FIELD_PREP_CONST(PEF2256_GPC1_CSFP_MASK, 0x3) + +/* Port Configuration 6 */ +#define PEF2256_PC6 0x86 + +/* Global Counter Mode n=1..8 */ +#define PEF2256_GCM(_n) (0x92 + (_n) - 1) +#define PEF2256_GCM1 0x92 +#define PEF2256_GCM2 0x93 +#define PEF2256_GCM3 0x94 +#define PEF2256_GCM4 0x95 +#define PEF2256_GCM5 0x96 +#define PEF2256_GCM6 0x97 +#define PEF2256_GCM7 0x98 +#define PEF2256_GCM8 0x99 + +/* Version Status Register */ +#define PEF2256_VSTR 0x4A +#define PEF2256_VSTR_VERSION_12 0x00 +#define PEF2256_VSTR_VERSION_21 0x10 +#define PEF2256_VSTR_VERSION_2x 0x05 + +/* Framer Receive Status 0 */ +#define PEF2256_FRS0 0x4C +#define PEF2256_FRS0_LOS BIT(7) +#define PEF2256_FRS0_AIS BIT(6) + +/* Interrupt Status Register 0..5 */ +#define PEF2256_ISR(_n) (0x68 + (_n)) +#define PEF2256_ISR0 0x68 +#define PEF2256_ISR1 0x69 +#define PEF2256_ISR2 0x6A +#define PEF2256_ISR3 0x6B +#define PEF2256_ISR4 0x6C +#define PEF2256_ISR5 0x6D + +/* Global Interrupt Status */ +#define PEF2256_GIS 0x6E +#define PEF2256_GIS_ISR(_n) BIT(_n) + +/* Wafer Identification Register */ +#define PEF2256_WID 0xEC +#define PEF2256_12_WID_MASK GENMASK(1, 0) +#define PEF2256_12_WID_VERSION_12 FIELD_PREP_CONST(PEF2256_12_WID_MASK, 0x3) +#define PEF2256_2X_WID_MASK GENMASK(7, 6) +#define PEF2256_2X_WID_VERSION_21 FIELD_PREP_CONST(PEF2256_2X_WID_MASK, 0x0) +#define PEF2256_2X_WID_VERSION_22 FIELD_PREP_CONST(PEF2256_2X_WID_MASK, 0x1) + +/* IMR2/ISR2 Interrupts common bits */ +#define PEF2256_INT2_AIS BIT(3) +#define PEF2256_INT2_LOS BIT(2) + +#endif /* __PEF2256_REGS_H__ */ diff --git a/drivers/mfd/pef2256.c b/drivers/mfd/pef2256.c new file mode 100644 index 000000000000..48717ae425b6 --- /dev/null +++ b/drivers/mfd/pef2256.c @@ -0,0 +1,950 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PEF2256 also known as FALC56 driver + * + * Copyright 2023 CS GROUP France + * + * Author: Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pef2256-regs.h" + +enum pef2256_frame_type { + PEF2256_FRAME_E1_DOUBLEFRAME, + PEF2256_FRAME_E1_CRC4_MULTIFRAME, + PEF2256_FRAME_E1_AUTO_MULTIFRAME, + PEF2256_FRAME_T1J1_4FRAME, + PEF2256_FRAME_T1J1_12FRAME, + PEF2256_FRAME_T1J1_24FRAME, + PEF2256_FRAME_T1J1_72FRAME, +}; + +struct pef2256 { + struct device *dev; + struct regmap *regmap; + enum pef2256_version version; + struct clk *mclk; + struct clk *sclkr; + struct clk *sclkx; + struct gpio_desc *reset_gpio; + bool is_e1; + unsigned long sysclk_rate; + u32 data_rate; + bool is_tx_falling_edge; + bool is_subordinate; + enum pef2256_frame_type frame_type; + u8 channel_phase; + atomic_t carrier; + struct atomic_notifier_head event_notifier_list; +}; + +static inline u8 pef2256_read8(struct pef2256 *pef2256, int offset) +{ + int val; + + regmap_read(pef2256->regmap, offset, &val); + return val; +} + +static inline void pef2256_write8(struct pef2256 *pef2256, int offset, u8 val) +{ + regmap_write(pef2256->regmap, offset, val); +} + +static inline void pef2256_clrbits8(struct pef2256 *pef2256, int offset, u8 clr) +{ + regmap_clear_bits(pef2256->regmap, offset, clr); +} + +static inline void pef2256_setbits8(struct pef2256 *pef2256, int offset, u8 set) +{ + regmap_set_bits(pef2256->regmap, offset, set); +} + +static inline void pef2256_clrsetbits8(struct pef2256 *pef2256, int offset, u8 clr, u8 set) +{ + regmap_update_bits(pef2256->regmap, offset, clr | set, set); +} + +enum pef2256_version pef2256_get_version(struct pef2256 *pef2256) +{ + enum pef2256_version version = PEF2256_VERSION_UNKNOWN; + u8 vstr, wid; + + vstr = pef2256_read8(pef2256, PEF2256_VSTR); + wid = pef2256_read8(pef2256, PEF2256_WID); + + switch (vstr) { + case PEF2256_VSTR_VERSION_12: + if ((wid & PEF2256_12_WID_MASK) == PEF2256_12_WID_VERSION_12) + version = PEF2256_VERSION_1_2; + break; + case PEF2256_VSTR_VERSION_2x: + switch (wid & PEF2256_2X_WID_MASK) { + case PEF2256_2X_WID_VERSION_21: + version = PEF2256_VERSION_2_1; + break; + case PEF2256_2X_WID_VERSION_22: + version = PEF2256_VERSION_2_2; + break; + } + break; + case PEF2256_VSTR_VERSION_21: + version = PEF2256_VERSION_2_1; + break; + } + + if (version == PEF2256_VERSION_UNKNOWN) + dev_err(pef2256->dev, "Unknown version (0x%02x, 0x%02x)\n", vstr, wid); + + return version; +} +EXPORT_SYMBOL_GPL(pef2256_get_version); + +enum pef2256_gcm_config_item { + PEF2256_GCM_CONFIG_1544000 = 0, + PEF2256_GCM_CONFIG_2048000, + PEF2256_GCM_CONFIG_8192000, + PEF2256_GCM_CONFIG_10000000, + PEF2256_GCM_CONFIG_12352000, + PEF2256_GCM_CONFIG_16384000, +}; + +struct pef2256_gcm_config { + u8 gcm_12[6]; + u8 gcm_2x[8]; +}; + +static const struct pef2256_gcm_config pef2256_gcm_configs[] = { + [PEF2256_GCM_CONFIG_1544000] = { + .gcm_12 = {0xF0, 0x51, 0x00, 0x80, 0x00, 0x15}, + .gcm_2x = {0x00, 0x15, 0x00, 0x08, 0x00, 0x3F, 0x9C, 0xDF}, + }, + [PEF2256_GCM_CONFIG_2048000] = { + .gcm_12 = {0x00, 0x58, 0xD2, 0xC2, 0x00, 0x10}, + .gcm_2x = {0x00, 0x18, 0xFB, 0x0B, 0x00, 0x2F, 0xDB, 0xDF}, + }, + [PEF2256_GCM_CONFIG_8192000] = { + .gcm_12 = {0x00, 0x58, 0xD2, 0xC2, 0x03, 0x10}, + .gcm_2x = {0x00, 0x18, 0xFB, 0x0B, 0x00, 0x0B, 0xDB, 0xDF}, + }, + [PEF2256_GCM_CONFIG_10000000] = { + .gcm_12 = {0x90, 0x51, 0x81, 0x8F, 0x04, 0x10}, + .gcm_2x = {0x40, 0x1B, 0x3D, 0x0A, 0x00, 0x07, 0xC9, 0xDC}, + }, + [PEF2256_GCM_CONFIG_12352000] = { + .gcm_12 = {0xF0, 0x51, 0x00, 0x80, 0x07, 0x15}, + .gcm_2x = {0x00, 0x19, 0x00, 0x08, 0x01, 0x0A, 0x98, 0xDA}, + }, + [PEF2256_GCM_CONFIG_16384000] = { + .gcm_12 = {0x00, 0x58, 0xD2, 0xC2, 0x07, 0x10}, + .gcm_2x = {0x00, 0x18, 0xFB, 0x0B, 0x01, 0x0B, 0xDB, 0xDF}, + }, +}; + +static int pef2256_setup_gcm(struct pef2256 *pef2256) +{ + enum pef2256_gcm_config_item item; + unsigned long mclk_rate; + const u8 *gcm; + int i, count; + + mclk_rate = clk_get_rate(pef2256->mclk); + switch (mclk_rate) { + case 1544000: + item = PEF2256_GCM_CONFIG_1544000; + break; + case 2048000: + item = PEF2256_GCM_CONFIG_2048000; + break; + case 8192000: + item = PEF2256_GCM_CONFIG_8192000; + break; + case 10000000: + item = PEF2256_GCM_CONFIG_10000000; + break; + case 12352000: + item = PEF2256_GCM_CONFIG_12352000; + break; + case 16384000: + item = PEF2256_GCM_CONFIG_16384000; + break; + default: + dev_err(pef2256->dev, "Unsupported v2.x MCLK rate %lu\n", mclk_rate); + return -EINVAL; + } + + BUILD_BUG_ON(item >= ARRAY_SIZE(pef2256_gcm_configs)); + + if (pef2256->version == PEF2256_VERSION_1_2) { + gcm = pef2256_gcm_configs[item].gcm_12; + count = ARRAY_SIZE(pef2256_gcm_configs[item].gcm_12); + } else { + gcm = pef2256_gcm_configs[item].gcm_2x; + count = ARRAY_SIZE(pef2256_gcm_configs[item].gcm_2x); + } + + for (i = 0; i < count; i++) + pef2256_write8(pef2256, PEF2256_GCM(i + 1), *(gcm + i)); + + return 0; +} + +static int pef2256_setup_e1_line(struct pef2256 *pef2256) +{ + u8 fmr1, fmr2; + + /* RCLK output : DPLL clock, DCO-X enabled, DCO-X internal ref clock */ + pef2256_write8(pef2256, PEF2256_CMR1, 0x00); + + /* + * SCLKR selected, SCLKX selected, + * receive synchro pulse sourced by SYPR, + * transmit synchro pulse sourced by SYPX, + * DCO-X center frequency enabled + */ + pef2256_write8(pef2256, PEF2256_CMR2, PEF2256_CMR2_DCOXC); + + if (pef2256->is_subordinate) { + /* select RCLK source = 2M, disable switching from RCLK to SYNC */ + pef2256_clrsetbits8(pef2256, PEF2256_CMR1, PEF2256_CMR1_RS_MASK, + PEF2256_CMR1_RS_DCOR_2048 | PEF2256_CMR1_DCS); + } + + /* + * slave mode, local loop off, mode short-haul + * In v2.x, bit3 is a forced 1 bit in the datasheet -> Need to be set. + */ + if (pef2256->version == PEF2256_VERSION_1_2) + pef2256_write8(pef2256, PEF2256_LIM0, 0x00); + else + pef2256_write8(pef2256, PEF2256_LIM0, PEF2256_2X_LIM0_BIT3); + + /* "master" mode */ + if (!pef2256->is_subordinate) + pef2256_setbits8(pef2256, PEF2256_LIM0, PEF2256_LIM0_MAS); + + /* analog interface selected, remote loop off */ + pef2256_write8(pef2256, PEF2256_LIM1, 0x00); + + /* receive input threshold = 0,21V */ + if (pef2256->version == PEF2256_VERSION_1_2) + pef2256_clrsetbits8(pef2256, PEF2256_LIM1, PEF2256_12_LIM1_RIL_MASK, + PEF2256_12_LIM1_RIL_210); + else + pef2256_clrsetbits8(pef2256, PEF2256_LIM1, PEF2256_2X_LIM1_RIL_MASK, + PEF2256_2X_LIM1_RIL_210); + + /* + * transmit pulse mask, default value from datasheet + * transmit line in normal operation + */ + if (pef2256->version == PEF2256_VERSION_1_2) + pef2256_write8(pef2256, PEF2256_XPM0, 0x7B); + else + pef2256_write8(pef2256, PEF2256_XPM0, 0x9C); + pef2256_write8(pef2256, PEF2256_XPM1, 0x03); + pef2256_write8(pef2256, PEF2256_XPM2, 0x00); + + /* HDB3 coding, no alarm simulation */ + pef2256_write8(pef2256, PEF2256_FMR0, PEF2256_FMR0_XC_HDB3 | PEF2256_FMR0_RC_HDB3); + + /* + * E1, frame format, 2 Mbit/s system data rate, no AIS + * transmission to remote end or system interface, payload loop + * off, transmit remote alarm on + */ + fmr1 = 0x00; + fmr2 = PEF2256_FMR2_AXRA; + switch (pef2256->frame_type) { + case PEF2256_FRAME_E1_DOUBLEFRAME: + fmr2 |= PEF2256_FMR2_RFS_DOUBLEFRAME; + break; + case PEF2256_FRAME_E1_CRC4_MULTIFRAME: + fmr1 |= PEF2256_FMR1_XFS; + fmr2 |= PEF2256_FMR2_RFS_CRC4_MULTIFRAME; + break; + case PEF2256_FRAME_E1_AUTO_MULTIFRAME: + fmr1 |= PEF2256_FMR1_XFS; + fmr2 |= PEF2256_FMR2_RFS_AUTO_MULTIFRAME; + break; + default: + dev_err(pef2256->dev, "Unsupported frame type %d\n", pef2256->frame_type); + return -EINVAL; + } + pef2256_clrsetbits8(pef2256, PEF2256_FMR1, PEF2256_FMR1_XFS, fmr1); + pef2256_write8(pef2256, PEF2256_FMR2, fmr2); + + if (!pef2256->is_subordinate) { + /* SEC input, active high */ + pef2256_write8(pef2256, PEF2256_GPC1, PEF2256_GPC1_CSFP_SEC_IN_HIGH); + } else { + /* FSC output, active high */ + pef2256_write8(pef2256, PEF2256_GPC1, PEF2256_GPC1_CSFP_FSC_OUT_HIGH); + } + + /* + * SCLKR, SCLKX, RCLK configured to inputs, + * XFMS active low, CLK1 and CLK2 pin configuration + */ + pef2256_write8(pef2256, PEF2256_PC5, 0x00); + pef2256_write8(pef2256, PEF2256_PC6, 0x00); + + /* port RCLK is output */ + pef2256_setbits8(pef2256, PEF2256_PC5, PEF2256_PC5_CRP); + + return 0; +} + +static void pef2256_setup_e1_los(struct pef2256 *pef2256) +{ + /* detection of LOS alarm = 176 pulses (ie (10 + 1) * 16) */ + pef2256_write8(pef2256, PEF2256_PCD, 10); + /* recovery of LOS alarm = 22 pulses (ie 21 + 1) */ + pef2256_write8(pef2256, PEF2256_PCR, 21); + /* E1 default for the receive slicer threshold */ + pef2256_write8(pef2256, PEF2256_LIM2, PEF2256_LIM2_SLT_THR50); + if (pef2256->is_subordinate) { + /* Loop-timed */ + pef2256_setbits8(pef2256, PEF2256_LIM2, PEF2256_LIM2_ELT); + } +} + +static int pef2256_setup_e1_system(struct pef2256 *pef2256) +{ + u8 sic1, fmr1; + + /* + * 2.048 MHz system clocking rate, receive buffer 2 frames, transmit + * buffer bypass, data sampled and transmitted on the falling edge of + * SCLKR/X, automatic freeze signaling, data is active in the first + * channel phase + */ + pef2256_write8(pef2256, PEF2256_SIC1, 0x00); + pef2256_write8(pef2256, PEF2256_SIC2, 0x00); + pef2256_write8(pef2256, PEF2256_SIC3, 0x00); + + if (pef2256->is_subordinate) { + /* transmit buffer size = 2 frames, transparent mode */ + pef2256_clrsetbits8(pef2256, PEF2256_SIC1, PEF2256_SIC1_XBS_MASK, + PEF2256_SIC1_XBS_2FRAMES); + } + + if (pef2256->version != PEF2256_VERSION_1_2) { + /* during inactive channel phase switch RDO/RSIG into tri-state */ + pef2256_setbits8(pef2256, PEF2256_SIC3, PEF2256_SIC3_RTRI); + } + + if (pef2256->is_tx_falling_edge) { + /* falling edge sync pulse transmit, rising edge sync pulse receive */ + pef2256_clrsetbits8(pef2256, PEF2256_SIC3, PEF2256_SIC3_RESX, PEF2256_SIC3_RESR); + } else { + /* rising edge sync pulse transmit, falling edge sync pulse receive */ + pef2256_clrsetbits8(pef2256, PEF2256_SIC3, PEF2256_SIC3_RESR, PEF2256_SIC3_RESX); + } + + /* transmit offset counter (XCO10..0) = 4 */ + pef2256_write8(pef2256, PEF2256_XC0, 0); + pef2256_write8(pef2256, PEF2256_XC1, 4); + /* receive offset counter (RCO10..0) = 4 */ + pef2256_write8(pef2256, PEF2256_RC0, 0); + pef2256_write8(pef2256, PEF2256_RC1, 4); + + /* system clock rate */ + switch (pef2256->sysclk_rate) { + case 2048000: + sic1 = PEF2256_SIC1_SSC_2048; + break; + case 4096000: + sic1 = PEF2256_SIC1_SSC_4096; + break; + case 8192000: + sic1 = PEF2256_SIC1_SSC_8192; + break; + case 16384000: + sic1 = PEF2256_SIC1_SSC_16384; + break; + default: + dev_err(pef2256->dev, "Unsupported sysclk rate %lu\n", pef2256->sysclk_rate); + return -EINVAL; + } + pef2256_clrsetbits8(pef2256, PEF2256_SIC1, PEF2256_SIC1_SSC_MASK, sic1); + + /* data clock rate */ + switch (pef2256->data_rate) { + case 2048000: + fmr1 = PEF2256_FMR1_SSD_2048; + sic1 = PEF2256_SIC1_SSD_2048; + break; + case 4096000: + fmr1 = PEF2256_FMR1_SSD_4096; + sic1 = PEF2256_SIC1_SSD_4096; + break; + case 8192000: + fmr1 = PEF2256_FMR1_SSD_8192; + sic1 = PEF2256_SIC1_SSD_8192; + break; + case 16384000: + fmr1 = PEF2256_FMR1_SSD_16384; + sic1 = PEF2256_SIC1_SSD_16384; + break; + default: + dev_err(pef2256->dev, "Unsupported data rate %u\n", pef2256->data_rate); + return -EINVAL; + } + pef2256_clrsetbits8(pef2256, PEF2256_FMR1, PEF2256_FMR1_SSD_MASK, fmr1); + pef2256_clrsetbits8(pef2256, PEF2256_SIC1, PEF2256_SIC1_SSD_MASK, sic1); + + /* channel phase */ + pef2256_clrsetbits8(pef2256, PEF2256_SIC2, PEF2256_SIC2_SICS_MASK, + PEF2256_SIC2_SICS(pef2256->channel_phase)); + + return 0; +} + +static void pef2256_setup_e1_signaling(struct pef2256 *pef2256) +{ + /* All bits of the transmitted service word are cleared */ + pef2256_write8(pef2256, PEF2256_XSW, PEF2256_XSW_XY(0x1F)); + + /* CAS disabled and clear spare bit values */ + pef2256_write8(pef2256, PEF2256_XSP, 0x00); + + if (pef2256->is_subordinate) { + /* transparent mode */ + pef2256_setbits8(pef2256, PEF2256_XSW, PEF2256_XSW_XTM); + } + + /* Si-Bit, Spare bit For International, FAS word */ + pef2256_setbits8(pef2256, PEF2256_XSW, PEF2256_XSW_XSIS); + pef2256_setbits8(pef2256, PEF2256_XSP, PEF2256_XSP_XSIF); + + /* no transparent mode active */ + pef2256_write8(pef2256, PEF2256_TSWM, 0x00); +} + +static void pef2256_setup_e1_errors(struct pef2256 *pef2256) +{ + /* error counter latched every 1s */ + pef2256_setbits8(pef2256, PEF2256_FMR1, PEF2256_FMR1_ECM); + + /* error counter mode COFA */ + pef2256_setbits8(pef2256, PEF2256_GCR, PEF2256_GCR_ECMC); + + /* errors in service words have no influence */ + pef2256_setbits8(pef2256, PEF2256_RC0, PEF2256_RC0_SWD); + + /* 4 consecutive incorrect FAS causes loss of sync */ + pef2256_setbits8(pef2256, PEF2256_RC0, PEF2256_RC0_ASY4); +} + +static int pef2256_setup_e1(struct pef2256 *pef2256) +{ + int ret; + + /* Setup, Master clocking mode (GCM8..1) */ + ret = pef2256_setup_gcm(pef2256); + if (ret) + return ret; + + /* Select E1 mode */ + pef2256_write8(pef2256, PEF2256_FMR1, 0x00); + + /* internal second timer, power on */ + pef2256_write8(pef2256, PEF2256_GCR, 0x00); + + /* Setup line interface */ + ret = pef2256_setup_e1_line(pef2256); + if (ret) + return ret; + + /* Setup Loss-of-signal detection and recovery */ + pef2256_setup_e1_los(pef2256); + + /* Setup system interface */ + ret = pef2256_setup_e1_system(pef2256); + if (ret) + return ret; + + /* Setup signaling */ + pef2256_setup_e1_signaling(pef2256); + + /* Setup errors counters and condition */ + pef2256_setup_e1_errors(pef2256); + + /* status changed interrupt at both up and down */ + pef2256_setbits8(pef2256, PEF2256_GCR, PEF2256_GCR_SCI); + + /* Clear any ISR2 pending interrupts and unmask needed interrupts */ + pef2256_read8(pef2256, PEF2256_ISR2); + pef2256_clrbits8(pef2256, PEF2256_IMR2, PEF2256_INT2_LOS | PEF2256_INT2_AIS); + + /* reset lines */ + pef2256_write8(pef2256, PEF2256_CMDR, PEF2256_CMDR_RRES | PEF2256_CMDR_XRES); + return 0; +} + +static int pef2256_setup(struct pef2256 *pef2256) +{ + if (pef2256->is_e1) + return pef2256_setup_e1(pef2256); + + dev_err(pef2256->dev, "Only E1 line is currently supported\n"); + return -EOPNOTSUPP; +} + +static void pef2256_isr_default_handler(struct pef2256 *pef2256, u8 nbr, u8 isr) +{ + dev_warn(pef2256->dev, "ISR%u: 0x%02x not handled\n", nbr, isr); +} + +static bool pef2256_is_carrier_on(struct pef2256 *pef2256) +{ + u8 frs0; + + frs0 = pef2256_read8(pef2256, PEF2256_FRS0); + return !(frs0 & (PEF2256_FRS0_LOS | PEF2256_FRS0_AIS)); +} + +static void pef2256_isr2_handler(struct pef2256 *pef2256, u8 nbr, u8 isr) +{ + bool carrier; + + if (isr & (PEF2256_INT2_LOS | PEF2256_INT2_AIS)) { + carrier = pef2256_is_carrier_on(pef2256); + if (atomic_xchg(&pef2256->carrier, carrier) != carrier) + atomic_notifier_call_chain(&pef2256->event_notifier_list, + PEF2256_EVENT_CARRIER, NULL); + } +} + +static irqreturn_t pef2256_irq_handler(int irq, void *priv) +{ + static void (*pef2256_isr_handler[])(struct pef2256 *, u8, u8) = { + [0] = pef2256_isr_default_handler, + [1] = pef2256_isr_default_handler, + [2] = pef2256_isr2_handler, + [3] = pef2256_isr_default_handler, + [4] = pef2256_isr_default_handler, + [5] = pef2256_isr_default_handler + }; + struct pef2256 *pef2256 = (struct pef2256 *)priv; + u8 gis; + u8 isr; + u8 n; + + gis = pef2256_read8(pef2256, PEF2256_GIS); + + for (n = 0; n < ARRAY_SIZE(pef2256_isr_handler); n++) { + if (gis & PEF2256_GIS_ISR(n)) { + isr = pef2256_read8(pef2256, PEF2256_ISR(n)); + pef2256_isr_handler[n](pef2256, n, isr); + } + } + + return IRQ_HANDLED; +} + +static int pef2256_check_rates(struct pef2256 *pef2256, unsigned long sysclk_rate, + unsigned long data_rate) +{ + unsigned long rate; + + switch (sysclk_rate) { + case 2048000: + case 4096000: + case 8192000: + case 16384000: + break; + default: + dev_err(pef2256->dev, "Unsupported system clock rate %lu\n", sysclk_rate); + return -EINVAL; + } + + for (rate = data_rate; rate <= data_rate * 4; rate *= 2) { + if (rate == sysclk_rate) + return 0; + } + dev_err(pef2256->dev, "Unsupported data rate %lu with system clock rate %lu\n", + data_rate, sysclk_rate); + return -EINVAL; +} + +static int pef2556_of_parse(struct pef2256 *pef2256, struct device_node *np) +{ + const char *str; + int ret; + + str = "e1"; + ret = of_property_read_string(np, "lantiq,line-interface", &str); + if (ret && ret != -EINVAL) { + dev_err(pef2256->dev, "%pOF: failed to read lantiq,line-interface\n", + np); + return ret; + } + if (!strcmp(str, "e1")) { + pef2256->is_e1 = true; + } else if (!strcmp(str, "t1j1")) { + pef2256->is_e1 = false; + } else { + dev_err(pef2256->dev, "%pOF: Invalid lantiq,line-interface (%s)\n", + np, str); + return -EINVAL; + } + + pef2256->data_rate = 2048000; + ret = of_property_read_u32(np, "lantiq,data-rate-bps", &pef2256->data_rate); + if (ret && ret != -EINVAL) { + dev_err(pef2256->dev, "%pOF: failed to read lantiq,data-rate-bps\n", np); + return ret; + } + + ret = pef2256_check_rates(pef2256, pef2256->sysclk_rate, pef2256->data_rate); + if (ret) + return ret; + + pef2256->is_tx_falling_edge = of_property_read_bool(np, "lantiq,clock-falling-edge"); + pef2256->is_subordinate = of_property_read_bool(np, "lantiq,subordinate"); + + str = pef2256->is_e1 ? "doubleframe" : "12frame"; + ret = of_property_read_string(np, "lantiq,frame-format", &str); + if (ret && ret != -EINVAL) { + dev_err(pef2256->dev, "%pOF: failed to read lantiq,frame-format\n", + np); + return ret; + } + if (pef2256->is_e1) { + if (!strcmp(str, "doubleframe")) { + pef2256->frame_type = PEF2256_FRAME_E1_DOUBLEFRAME; + } else if (!strcmp(str, "crc4-multiframe")) { + pef2256->frame_type = PEF2256_FRAME_E1_CRC4_MULTIFRAME; + } else if (!strcmp(str, "auto-multiframe")) { + pef2256->frame_type = PEF2256_FRAME_E1_AUTO_MULTIFRAME; + } else { + dev_err(pef2256->dev, "%pOF: Invalid lantiq,frame-format (%s)\n", + np, str); + return -EINVAL; + } + } else { + if (!strcmp(str, "4frame")) { + pef2256->frame_type = PEF2256_FRAME_T1J1_4FRAME; + } else if (!strcmp(str, "12frame")) { + pef2256->frame_type = PEF2256_FRAME_T1J1_12FRAME; + } else if (!strcmp(str, "24frame")) { + pef2256->frame_type = PEF2256_FRAME_T1J1_24FRAME; + } else if (!strcmp(str, "72frame")) { + pef2256->frame_type = PEF2256_FRAME_T1J1_72FRAME; + } else { + dev_err(pef2256->dev, "%pOF: Invalid lantiq,frame-format (%s)\n", + np, str); + return -EINVAL; + } + } + + pef2256->channel_phase = 0; + ret = of_property_read_u8(np, "lantiq,channel-phase", &pef2256->channel_phase); + if (ret && ret != -EINVAL) { + dev_err(pef2256->dev, "%pOF: failed to read lantiq,channel-phase\n", + np); + return ret; + } + if (pef2256->channel_phase >= pef2256->sysclk_rate / pef2256->data_rate) { + dev_err(pef2256->dev, "%pOF: Invalid lantiq,channel-phase %u\n", + np, pef2256->channel_phase); + return -EINVAL; + } + + return 0; +} + +static ssize_t subordinate_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pef2256 *pef2256 = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", pef2256->is_subordinate); +} + +static ssize_t subordinate_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pef2256 *pef2256 = dev_get_drvdata(dev); + int ret; + + if (strtobool(buf, &pef2256->is_subordinate) < 0) + return -EINVAL; + + ret = pef2256_setup(pef2256); + if (ret) + return ret; + + return count; +} + +static DEVICE_ATTR_RW(subordinate); + +static const struct attribute_group pef2256_attribute_group = { + .attrs = (struct attribute *[]) { + &dev_attr_subordinate.attr, + NULL, + }, +}; + +static const struct regmap_config pef2256_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .max_register = 0xff, +}; + +static const struct mfd_cell pef2256_devs[] = { + { .name = "lantiq-pef2256-pinctrl", }, +}; + +static int pef2256_add_audio_devices(struct pef2256 *pef2256) +{ + const char *compatible = "lantiq,pef2256-codec"; + struct mfd_cell *audio_devs; + struct device_node *np; + unsigned int count = 0; + unsigned int i; + int ret; + + for_each_available_child_of_node(pef2256->dev->of_node, np) { + if (of_device_is_compatible(np, compatible)) + count++; + } + + if (!count) + return 0; + + audio_devs = kcalloc(count, sizeof(*audio_devs), GFP_KERNEL); + if (!audio_devs) + return -ENOMEM; + + for (i = 0; i < count; i++) { + (audio_devs + i)->name = "lantiq-pef2256-codec"; + (audio_devs + i)->of_compatible = compatible; + (audio_devs + i)->id = i; + } + + ret = mfd_add_devices(pef2256->dev, 0, audio_devs, count, NULL, 0, NULL); + kfree(audio_devs); + return ret; +} + +static int pef2256_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + unsigned long sclkr_rate, sclkx_rate; + struct pef2256 *pef2256; + const char *version_txt; + void __iomem *iomem; + int ret; + int irq; + + pef2256 = devm_kzalloc(&pdev->dev, sizeof(*pef2256), GFP_KERNEL); + if (!pef2256) + return -ENOMEM; + + pef2256->dev = &pdev->dev; + ATOMIC_INIT_NOTIFIER_HEAD(&pef2256->event_notifier_list); + atomic_set(&pef2256->carrier, 0); + + iomem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + + pef2256->regmap = devm_regmap_init_mmio(&pdev->dev, iomem, + &pef2256_regmap_config); + if (IS_ERR(pef2256->regmap)) { + dev_err(&pdev->dev, "Failed to initialise Regmap (%ld)\n", + PTR_ERR(pef2256->regmap)); + return PTR_ERR(pef2256->regmap); + } + + pef2256->mclk = devm_clk_get_enabled(&pdev->dev, "mclk"); + if (IS_ERR(pef2256->mclk)) + return PTR_ERR(pef2256->mclk); + + pef2256->sclkr = devm_clk_get_enabled(&pdev->dev, "sclkr"); + if (IS_ERR(pef2256->sclkr)) + return PTR_ERR(pef2256->sclkr); + + pef2256->sclkx = devm_clk_get_enabled(&pdev->dev, "sclkx"); + if (IS_ERR(pef2256->sclkx)) + return PTR_ERR(pef2256->sclkx); + + /* + * Both SCLKR (receive) and SCLKX (transmit) must have the same rate, + * stored as sysclk_rate. + * The exact value will be checked at pef2256_check_rates() + */ + sclkr_rate = clk_get_rate(pef2256->sclkr); + sclkx_rate = clk_get_rate(pef2256->sclkx); + if (sclkr_rate != sclkx_rate) { + dev_err(pef2256->dev, "clk rate mismatch. sclkr %lu Hz, sclkx %lu Hz\n", + sclkr_rate, sclkx_rate); + return -EINVAL; + } + pef2256->sysclk_rate = sclkr_rate; + + /* Reset the component. The MCLK clock must be active during reset */ + pef2256->reset_gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(pef2256->reset_gpio)) + return PTR_ERR(pef2256->reset_gpio); + if (pef2256->reset_gpio) { + gpiod_set_value_cansleep(pef2256->reset_gpio, 1); + usleep_range(10, 20); + gpiod_set_value_cansleep(pef2256->reset_gpio, 0); + usleep_range(10, 20); + } + + pef2256->version = pef2256_get_version(pef2256); + switch (pef2256->version) { + case PEF2256_VERSION_1_2: + version_txt = "1.2"; + break; + case PEF2256_VERSION_2_1: + version_txt = "2.1"; + break; + case PEF2256_VERSION_2_2: + version_txt = "2.2"; + break; + default: + return -ENODEV; + } + dev_info(pef2256->dev, "Version %s detected\n", version_txt); + + ret = pef2556_of_parse(pef2256, np); + if (ret) + return ret; + + /* Disable interrupts */ + pef2256_write8(pef2256, PEF2256_IMR0, 0xff); + pef2256_write8(pef2256, PEF2256_IMR1, 0xff); + pef2256_write8(pef2256, PEF2256_IMR2, 0xff); + pef2256_write8(pef2256, PEF2256_IMR3, 0xff); + pef2256_write8(pef2256, PEF2256_IMR4, 0xff); + pef2256_write8(pef2256, PEF2256_IMR5, 0xff); + + /* Clear any pending interrupts */ + pef2256_read8(pef2256, PEF2256_ISR0); + pef2256_read8(pef2256, PEF2256_ISR1); + pef2256_read8(pef2256, PEF2256_ISR2); + pef2256_read8(pef2256, PEF2256_ISR3); + pef2256_read8(pef2256, PEF2256_ISR4); + pef2256_read8(pef2256, PEF2256_ISR5); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + ret = devm_request_irq(pef2256->dev, irq, pef2256_irq_handler, 0, "pef2256", pef2256); + if (ret < 0) + return ret; + + platform_set_drvdata(pdev, pef2256); + + ret = mfd_add_devices(pef2256->dev, 0, pef2256_devs, + ARRAY_SIZE(pef2256_devs), NULL, 0, NULL); + if (ret) { + dev_err(pef2256->dev, "add devices failed (%d)\n", ret); + return ret; + } + + ret = pef2256_setup(pef2256); + if (ret) + return ret; + + ret = sysfs_create_group(&pef2256->dev->kobj, &pef2256_attribute_group); + if (ret < 0) { + dev_err(pef2256->dev, "sysfs registration failed (%d)\n", ret); + return ret; + } + + /* Add audio devices */ + ret = pef2256_add_audio_devices(pef2256); + if (ret < 0) { + dev_err(pef2256->dev, "add audio devices failed (%d)\n", ret); + goto remove_sysfs_group; + } + + return 0; + +remove_sysfs_group: + sysfs_remove_group(&pef2256->dev->kobj, &pef2256_attribute_group); + return ret; +} + +static int pef2256_remove(struct platform_device *pdev) +{ + struct pef2256 *pef2256 = platform_get_drvdata(pdev); + + /* Disable interrupts */ + pef2256_write8(pef2256, PEF2256_IMR0, 0xff); + pef2256_write8(pef2256, PEF2256_IMR1, 0xff); + pef2256_write8(pef2256, PEF2256_IMR2, 0xff); + pef2256_write8(pef2256, PEF2256_IMR3, 0xff); + pef2256_write8(pef2256, PEF2256_IMR4, 0xff); + pef2256_write8(pef2256, PEF2256_IMR5, 0xff); + + sysfs_remove_group(&pef2256->dev->kobj, &pef2256_attribute_group); + + return 0; +} + +static const struct of_device_id pef2256_id_table[] = { + { .compatible = "lantiq,pef2256" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, pef2256_id_table); + +static struct platform_driver pef2256_driver = { + .driver = { + .name = "lantiq-pef2256", + .of_match_table = pef2256_id_table, + }, + .probe = pef2256_probe, + .remove = pef2256_remove, +}; +module_platform_driver(pef2256_driver); + +struct regmap *pef2256_get_regmap(struct pef2256 *pef2256) +{ + return pef2256->regmap; +} +EXPORT_SYMBOL_GPL(pef2256_get_regmap); + +int pef2256_register_event_notifier(struct pef2256 *pef2256, struct notifier_block *nb) +{ + return atomic_notifier_chain_register(&pef2256->event_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(pef2256_register_event_notifier); + +int pef2256_unregister_event_notifier(struct pef2256 *pef2256, struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&pef2256->event_notifier_list, nb); +} +EXPORT_SYMBOL_GPL(pef2256_unregister_event_notifier); + +bool pef2256_get_carrier(struct pef2256 *pef2256) +{ + return !!atomic_read(&pef2256->carrier); +} +EXPORT_SYMBOL_GPL(pef2256_get_carrier); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("PEF2256 driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/pef2256.h b/include/linux/mfd/pef2256.h new file mode 100644 index 000000000000..9931a7116751 --- /dev/null +++ b/include/linux/mfd/pef2256.h @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * PEF2256 consumer API + * + * Copyright 2023 CS GROUP France + * + * Author: Herve Codina + */ +#ifndef __PEF2256_H__ +#define __PEF2256_H__ + +#include + +struct pef2256; +struct notifier_block; +struct regmap; + +/* Retrieve the PEF2256 regmap */ +struct regmap *pef2256_get_regmap(struct pef2256 *pef2256); + +/* PEF2256 hardware versions */ +enum pef2256_version { + PEF2256_VERSION_UNKNOWN, + PEF2256_VERSION_1_2, + PEF2256_VERSION_2_1, + PEF2256_VERSION_2_2, +}; + +/* Get the PEF2256 hardware version */ +enum pef2256_version pef2256_get_version(struct pef2256 *pef2256); + +/* Event signaled by the PEF2256 notifier */ +enum pef2256_event { + PEF2256_EVENT_CARRIER, /* Carrier state changed */ +}; + +/* + * Register/unregister an event notitifier. + * The nb.notifier_call function registered must not sleep. + * The 'action' parameter of the nb.notifier_call function will be set to one + * of the enum pef2256_event values. + */ +int pef2256_register_event_notifier(struct pef2256 *pef2256, struct notifier_block *nb); +int pef2256_unregister_event_notifier(struct pef2256 *pef2256, struct notifier_block *nb); + +/* + * Retrieve the PEF2256 carrier state. + * Returns true: carrier on, false: carrier off + */ +bool pef2256_get_carrier(struct pef2256 *pef2256); + +#endif /* __PEF2256_H__ */ From patchwork Mon Apr 17 17:15:58 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214388 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id BD50EC77B76 for ; Mon, 17 Apr 2023 17:18:26 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 4E932E77; Mon, 17 Apr 2023 19:17:34 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 4E932E77 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751904; bh=6b4aCSDvadrjHs/Pm/9qiGT8xfyuU1ZK4lK0w4SsoMw=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=eIWAh8N76rl1du6lQfXcudtmq/CIcZ5iPztVSER28MgeJ54xaexspZrWy0U1vzB2N vHDBvxRODg7BXK69XiRXgyFcXzEPWboXQ7SeTTS1W9vPTMwFoGb4/cg8AO6T7drZr6 WQIc1zdJHydUAIUYUtMIdlk04mRjDrHb1G+Ykb8A= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 34FE7F80542; Mon, 17 Apr 2023 19:16:27 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 4/7] Documentation: sysfs: Document the Lantiq PEF2256 sysfs entry Date: Mon, 17 Apr 2023 19:15:58 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175178595.26.3254629369916981360@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni Content-Disposition: inline Document the "subordinate" sysfs attribute exposed by the PEF2256 driver. Signed-off-by: Herve Codina --- .../ABI/testing/sysfs-bus-platform-devices-pef2256 | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-platform-devices-pef2256 diff --git a/Documentation/ABI/testing/sysfs-bus-platform-devices-pef2256 b/Documentation/ABI/testing/sysfs-bus-platform-devices-pef2256 new file mode 100644 index 000000000000..95ba1ae55daf --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-platform-devices-pef2256 @@ -0,0 +1,12 @@ +What: /sys/bus/platform/devices/*.pef2256/subordinate +KernelVersion: 6.4 +Contact: Herve Codina +Description: + (RW) Controls whether the PEF2256 works as subordinate or main + device mode. + + - 0: main device mode + - 1: subordinate mode + + In subordinate device mode it synchronizes on line interface + clock signals. Otherwise, it synchronizes on internal clocks. From patchwork Mon Apr 17 17:15:59 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214391 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C151CC77B7A for ; Mon, 17 Apr 2023 17:19:16 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 5E55CE76; Mon, 17 Apr 2023 19:18:24 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 5E55CE76 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751954; bh=jXR+42m9xe/eUhRTi5Sho2KfjSe7enWLaA94YrEQwtg=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=l3n+Fr/UMTve9ovNmHPi6hPqiv46U9ByMV3lEn4xfuBkVyJeJgFGetr4u8sruuF1+ 1/L4H6LMEmUTO0NxxkuzQnHzl31zqe8DjYqj7qL1/8mtnnW8n0JFQMlqgNibtlQrBd EtJ8vYVoW/9yqhd8dHpXiMZvfXfaBLBTKS7LiSCQ= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 1FA45F8056F; Mon, 17 Apr 2023 19:16:38 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 5/7] pinctrl: Add support for the Lantic PEF2256 pinmux Date: Mon, 17 Apr 2023 19:15:59 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175179648.26.5313609199013580875@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni Content-Disposition: inline The Lantiq PEF2256 is a framer and line interface component designed to fulfill all required interfacing between an analog E1/T1/J1 line and the digital PCM system highway/H.100 bus. This pinmux support handles the pin muxing part (pins RP(A..D) and pins XP(A..D)) of the PEF2256. Signed-off-by: Herve Codina --- drivers/pinctrl/Kconfig | 14 ++ drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-pef2256-regs.h | 65 ++++++ drivers/pinctrl/pinctrl-pef2256.c | 310 +++++++++++++++++++++++++ 4 files changed, 390 insertions(+) create mode 100644 drivers/pinctrl/pinctrl-pef2256-regs.h create mode 100644 drivers/pinctrl/pinctrl-pef2256.c diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index dcb53c4a9584..0b4da9c38462 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -377,6 +377,20 @@ config PINCTRL_PALMAS open drain configuration for the Palmas series devices like TPS65913, TPS80036 etc. +config PINCTRL_PEF2256 + tristate "Lantiq PEF2256 (FALC56) pin controller driver" + depends on OF && MFD_PEF2256 + select PINMUX + select GENERIC_PINCONF + help + This option enables the pin controller support for the Lantiq PEF2256 + framer, also known as FALC56. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called pinctrl-pef2256. + config PINCTRL_PIC32 bool "Microchip PIC32 pin controller driver" depends on OF diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index d5939840bb2a..011da5b44d19 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -39,6 +39,7 @@ obj-$(CONFIG_PINCTRL_MICROCHIP_SGPIO) += pinctrl-microchip-sgpio.o obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl-ocelot.o obj-$(CONFIG_PINCTRL_OXNAS) += pinctrl-oxnas.o obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o +obj-$(CONFIG_PINCTRL_PEF2256) += pinctrl-pef2256.o obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o diff --git a/drivers/pinctrl/pinctrl-pef2256-regs.h b/drivers/pinctrl/pinctrl-pef2256-regs.h new file mode 100644 index 000000000000..586d94007e24 --- /dev/null +++ b/drivers/pinctrl/pinctrl-pef2256-regs.h @@ -0,0 +1,65 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PEF2256 pinctrl registers definition + * + * Copyright 2023 CS GROUP France + * + * Author: Herve Codina + */ +#ifndef __PEF2256_PINCTRL_REGS_H__ +#define __PEF2256_PINCTRL_REGS_H__ + +#include "linux/bitfield.h" + +/* Port Configuration 1..4 */ +#define PEF2256_PC1 0x80 +#define PEF2256_PC2 0x81 +#define PEF2256_PC3 0x82 +#define PEF2256_PC4 0x83 +#define PEF2256_12_PC_RPC_MASK GENMASK(6, 4) +#define PEF2256_12_PC_RPC_SYPR FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x0) +#define PEF2256_12_PC_RPC_RFM FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x1) +#define PEF2256_12_PC_RPC_RFMB FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x2) +#define PEF2256_12_PC_RPC_RSIGM FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x3) +#define PEF2256_12_PC_RPC_RSIG FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x4) +#define PEF2256_12_PC_RPC_DLR FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x5) +#define PEF2256_12_PC_RPC_FREEZE FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x6) +#define PEF2256_12_PC_RPC_RFSP FIELD_PREP_CONST(PEF2256_12_PC_RPC_MASK, 0x7) +#define PEF2256_12_PC_XPC_MASK GENMASK(4, 0) +#define PEF2256_12_PC_XPC_SYPX FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x0) +#define PEF2256_12_PC_XPC_XFMS FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x1) +#define PEF2256_12_PC_XPC_XSIG FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x2) +#define PEF2256_12_PC_XPC_TCLK FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x3) +#define PEF2256_12_PC_XPC_XMFB FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x4) +#define PEF2256_12_PC_XPC_XSIGM FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x5) +#define PEF2256_12_PC_XPC_DLX FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x6) +#define PEF2256_12_PC_XPC_XCLK FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x7) +#define PEF2256_12_PC_XPC_XLT FIELD_PREP_CONST(PEF2256_12_PC_XPC_MASK, 0x8) +#define PEF2256_2X_PC_RPC_MASK GENMASK(7, 4) +#define PEF2256_2X_PC_RPC_SYPR FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x0) +#define PEF2256_2X_PC_RPC_RFM FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x1) +#define PEF2256_2X_PC_RPC_RFMB FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x2) +#define PEF2256_2X_PC_RPC_RSIGM FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x3) +#define PEF2256_2X_PC_RPC_RSIG FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x4) +#define PEF2256_2X_PC_RPC_DLR FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x5) +#define PEF2256_2X_PC_RPC_FREEZE FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x6) +#define PEF2256_2X_PC_RPC_RFSP FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x7) +#define PEF2256_2X_PC_RPC_GPI FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0x9) +#define PEF2256_2X_PC_RPC_GPOH FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0xa) +#define PEF2256_2X_PC_RPC_GPOL FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0xb) +#define PEF2256_2X_PC_RPC_LOS FIELD_PREP_CONST(PEF2256_2X_PC_RPC_MASK, 0xc) +#define PEF2256_2X_PC_XPC_MASK GENMASK(3, 0) +#define PEF2256_2X_PC_XPC_SYPX FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x0) +#define PEF2256_2X_PC_XPC_XFMS FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x1) +#define PEF2256_2X_PC_XPC_XSIG FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x2) +#define PEF2256_2X_PC_XPC_TCLK FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x3) +#define PEF2256_2X_PC_XPC_XMFB FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x4) +#define PEF2256_2X_PC_XPC_XSIGM FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x5) +#define PEF2256_2X_PC_XPC_DLX FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x6) +#define PEF2256_2X_PC_XPC_XCLK FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x7) +#define PEF2256_2X_PC_XPC_XLT FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x8) +#define PEF2256_2X_PC_XPC_GPI FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0x9) +#define PEF2256_2X_PC_XPC_GPOH FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0xa) +#define PEF2256_2X_PC_XPC_GPOL FIELD_PREP_CONST(PEF2256_2X_PC_XPC_MASK, 0xb) + +#endif /* __PEF2256_PINCTRL_REGS_H__ */ diff --git a/drivers/pinctrl/pinctrl-pef2256.c b/drivers/pinctrl/pinctrl-pef2256.c new file mode 100644 index 000000000000..65096da13f98 --- /dev/null +++ b/drivers/pinctrl/pinctrl-pef2256.c @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PEF2256 also known as FALC56 driver + * + * Copyright 2023 CS GROUP France + * + * Author: Herve Codina + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pinctrl-pef2256-regs.h" + +struct pef2256_pinreg_desc { + int offset; + u8 mask; +}; + +struct pef2256_function_desc { + const char *name; + const char * const*groups; + unsigned int ngroups; + u8 func_val; +}; + +struct pef2256_pinctrl { + struct device *dev; + struct regmap *regmap; + enum pef2256_version version; + struct { + struct pinctrl_desc pctrl_desc; + const struct pef2256_function_desc *functions; + unsigned int nfunctions; + } pinctrl; +}; + +static int pef2256_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + + /* We map 1 group <-> 1 pin */ + return pef2256->pinctrl.pctrl_desc.npins; +} + +static const char *pef2256_get_group_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + + /* We map 1 group <-> 1 pin */ + return pef2256->pinctrl.pctrl_desc.pins[selector].name; +} + +static int pef2256_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, + const unsigned int **pins, + unsigned int *num_pins) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + + /* We map 1 group <-> 1 pin */ + *pins = &pef2256->pinctrl.pctrl_desc.pins[selector].number; + *num_pins = 1; + + return 0; +} + +static const struct pinctrl_ops pef2256_pctlops = { + .get_groups_count = pef2256_get_groups_count, + .get_group_name = pef2256_get_group_name, + .get_group_pins = pef2256_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static int pef2256_get_functions_count(struct pinctrl_dev *pctldev) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + + return pef2256->pinctrl.nfunctions; +} + +static const char *pef2256_get_function_name(struct pinctrl_dev *pctldev, + unsigned int selector) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + + return pef2256->pinctrl.functions[selector].name; +} + +static int pef2256_get_function_groups(struct pinctrl_dev *pctldev, unsigned int selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + + *groups = pef2256->pinctrl.functions[selector].groups; + *num_groups = pef2256->pinctrl.functions[selector].ngroups; + return 0; +} + +static int pef2256_set_mux(struct pinctrl_dev *pctldev, unsigned int func_selector, + unsigned int group_selector) +{ + struct pef2256_pinctrl *pef2256 = pinctrl_dev_get_drvdata(pctldev); + const struct pef2256_pinreg_desc *pinreg_desc; + u8 func_val; + + /* We map 1 group <-> 1 pin */ + pinreg_desc = pef2256->pinctrl.pctrl_desc.pins[group_selector].drv_data; + func_val = pef2256->pinctrl.functions[func_selector].func_val; + + return regmap_update_bits(pef2256->regmap, pinreg_desc->offset, + pinreg_desc->mask, func_val); +} + +static const struct pinmux_ops pef2256_pmxops = { + .get_functions_count = pef2256_get_functions_count, + .get_function_name = pef2256_get_function_name, + .get_function_groups = pef2256_get_function_groups, + .set_mux = pef2256_set_mux, +}; + +#define PEF2256_PINCTRL_PIN(_number, _name, _offset, _mask) { \ + .number = _number, \ + .name = _name, \ + .drv_data = &(struct pef2256_pinreg_desc) { \ + .offset = _offset, \ + .mask = _mask, \ + }, \ +} + +static const struct pinctrl_pin_desc pef2256_v12_pins[] = { + PEF2256_PINCTRL_PIN(0, "RPA", PEF2256_PC1, PEF2256_12_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(1, "RPB", PEF2256_PC2, PEF2256_12_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(2, "RPC", PEF2256_PC3, PEF2256_12_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(3, "RPD", PEF2256_PC4, PEF2256_12_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(4, "XPA", PEF2256_PC1, PEF2256_12_PC_XPC_MASK), + PEF2256_PINCTRL_PIN(5, "XPB", PEF2256_PC2, PEF2256_12_PC_XPC_MASK), + PEF2256_PINCTRL_PIN(6, "XPC", PEF2256_PC3, PEF2256_12_PC_XPC_MASK), + PEF2256_PINCTRL_PIN(7, "XPD", PEF2256_PC4, PEF2256_12_PC_XPC_MASK), +}; + +static const struct pinctrl_pin_desc pef2256_v2x_pins[] = { + PEF2256_PINCTRL_PIN(0, "RPA", PEF2256_PC1, PEF2256_2X_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(1, "RPB", PEF2256_PC2, PEF2256_2X_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(2, "RPC", PEF2256_PC3, PEF2256_2X_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(3, "RPD", PEF2256_PC4, PEF2256_2X_PC_RPC_MASK), + PEF2256_PINCTRL_PIN(4, "XPA", PEF2256_PC1, PEF2256_2X_PC_XPC_MASK), + PEF2256_PINCTRL_PIN(5, "XPB", PEF2256_PC2, PEF2256_2X_PC_XPC_MASK), + PEF2256_PINCTRL_PIN(6, "XPC", PEF2256_PC3, PEF2256_2X_PC_XPC_MASK), + PEF2256_PINCTRL_PIN(7, "XPD", PEF2256_PC4, PEF2256_2X_PC_XPC_MASK), +}; + +static const char *const pef2256_rp_groups[] = { "RPA", "RPB", "RPC", "RPD" }; +static const char *const pef2256_xp_groups[] = { "XPA", "XPB", "XPC", "XPD" }; +static const char *const pef2256_all_groups[] = { "RPA", "RPB", "RPC", "RPD", + "XPA", "XPB", "XPC", "XPD" }; + +#define PEF2256_FUNCTION(_name, _func_val, _groups) { \ + .name = _name, \ + .groups = _groups, \ + .ngroups = ARRAY_SIZE(_groups), \ + .func_val = _func_val, \ +} + +static const struct pef2256_function_desc pef2256_v2x_functions[] = { + PEF2256_FUNCTION("SYPR", PEF2256_2X_PC_RPC_SYPR, pef2256_rp_groups), + PEF2256_FUNCTION("RFM", PEF2256_2X_PC_RPC_RFM, pef2256_rp_groups), + PEF2256_FUNCTION("RFMB", PEF2256_2X_PC_RPC_RFMB, pef2256_rp_groups), + PEF2256_FUNCTION("RSIGM", PEF2256_2X_PC_RPC_RSIGM, pef2256_rp_groups), + PEF2256_FUNCTION("RSIG", PEF2256_2X_PC_RPC_RSIG, pef2256_rp_groups), + PEF2256_FUNCTION("DLR", PEF2256_2X_PC_RPC_DLR, pef2256_rp_groups), + PEF2256_FUNCTION("FREEZE", PEF2256_2X_PC_RPC_FREEZE, pef2256_rp_groups), + PEF2256_FUNCTION("RFSP", PEF2256_2X_PC_RPC_RFSP, pef2256_rp_groups), + PEF2256_FUNCTION("LOS", PEF2256_2X_PC_RPC_LOS, pef2256_rp_groups), + + PEF2256_FUNCTION("SYPX", PEF2256_2X_PC_XPC_SYPX, pef2256_xp_groups), + PEF2256_FUNCTION("XFMS", PEF2256_2X_PC_XPC_XFMS, pef2256_xp_groups), + PEF2256_FUNCTION("XSIG", PEF2256_2X_PC_XPC_XSIG, pef2256_xp_groups), + PEF2256_FUNCTION("TCLK", PEF2256_2X_PC_XPC_TCLK, pef2256_xp_groups), + PEF2256_FUNCTION("XMFB", PEF2256_2X_PC_XPC_XMFB, pef2256_xp_groups), + PEF2256_FUNCTION("XSIGM", PEF2256_2X_PC_XPC_XSIGM, pef2256_xp_groups), + PEF2256_FUNCTION("DLX", PEF2256_2X_PC_XPC_DLX, pef2256_xp_groups), + PEF2256_FUNCTION("XCLK", PEF2256_2X_PC_XPC_XCLK, pef2256_xp_groups), + PEF2256_FUNCTION("XLT", PEF2256_2X_PC_XPC_XLT, pef2256_xp_groups), + + PEF2256_FUNCTION("GPI", PEF2256_2X_PC_RPC_GPI | PEF2256_2X_PC_XPC_GPI, + pef2256_all_groups), + PEF2256_FUNCTION("GPOH", PEF2256_2X_PC_RPC_GPOH | PEF2256_2X_PC_XPC_GPOH, + pef2256_all_groups), + PEF2256_FUNCTION("GPOL", PEF2256_2X_PC_RPC_GPOL | PEF2256_2X_PC_XPC_GPOL, + pef2256_all_groups), +}; + +static const struct pef2256_function_desc pef2256_v12_functions[] = { + PEF2256_FUNCTION("SYPR", PEF2256_12_PC_RPC_SYPR, pef2256_rp_groups), + PEF2256_FUNCTION("RFM", PEF2256_12_PC_RPC_RFM, pef2256_rp_groups), + PEF2256_FUNCTION("RFMB", PEF2256_12_PC_RPC_RFMB, pef2256_rp_groups), + PEF2256_FUNCTION("RSIGM", PEF2256_12_PC_RPC_RSIGM, pef2256_rp_groups), + PEF2256_FUNCTION("RSIG", PEF2256_12_PC_RPC_RSIG, pef2256_rp_groups), + PEF2256_FUNCTION("DLR", PEF2256_12_PC_RPC_DLR, pef2256_rp_groups), + PEF2256_FUNCTION("FREEZE", PEF2256_12_PC_RPC_FREEZE, pef2256_rp_groups), + PEF2256_FUNCTION("RFSP", PEF2256_12_PC_RPC_RFSP, pef2256_rp_groups), + + PEF2256_FUNCTION("SYPX", PEF2256_12_PC_XPC_SYPX, pef2256_xp_groups), + PEF2256_FUNCTION("XFMS", PEF2256_12_PC_XPC_XFMS, pef2256_xp_groups), + PEF2256_FUNCTION("XSIG", PEF2256_12_PC_XPC_XSIG, pef2256_xp_groups), + PEF2256_FUNCTION("TCLK", PEF2256_12_PC_XPC_TCLK, pef2256_xp_groups), + PEF2256_FUNCTION("XMFB", PEF2256_12_PC_XPC_XMFB, pef2256_xp_groups), + PEF2256_FUNCTION("XSIGM", PEF2256_12_PC_XPC_XSIGM, pef2256_xp_groups), + PEF2256_FUNCTION("DLX", PEF2256_12_PC_XPC_DLX, pef2256_xp_groups), + PEF2256_FUNCTION("XCLK", PEF2256_12_PC_XPC_XCLK, pef2256_xp_groups), + PEF2256_FUNCTION("XLT", PEF2256_12_PC_XPC_XLT, pef2256_xp_groups), +}; + +static int pef2256_register_pinctrl(struct pef2256_pinctrl *pef2256) +{ + struct pinctrl_dev *pctrl; + + pef2256->pinctrl.pctrl_desc.name = dev_name(pef2256->dev); + pef2256->pinctrl.pctrl_desc.owner = THIS_MODULE; + pef2256->pinctrl.pctrl_desc.pctlops = &pef2256_pctlops; + pef2256->pinctrl.pctrl_desc.pmxops = &pef2256_pmxops; + if (pef2256->version == PEF2256_VERSION_1_2) { + pef2256->pinctrl.pctrl_desc.pins = pef2256_v12_pins; + pef2256->pinctrl.pctrl_desc.npins = ARRAY_SIZE(pef2256_v12_pins); + pef2256->pinctrl.functions = pef2256_v12_functions; + pef2256->pinctrl.nfunctions = ARRAY_SIZE(pef2256_v12_functions); + } else { + pef2256->pinctrl.pctrl_desc.pins = pef2256_v2x_pins; + pef2256->pinctrl.pctrl_desc.npins = ARRAY_SIZE(pef2256_v2x_pins); + pef2256->pinctrl.functions = pef2256_v2x_functions; + pef2256->pinctrl.nfunctions = ARRAY_SIZE(pef2256_v2x_functions); + } + + pctrl = devm_pinctrl_register(pef2256->dev, &pef2256->pinctrl.pctrl_desc, pef2256); + if (IS_ERR(pctrl)) { + dev_err(pef2256->dev, "pinctrl driver registration failed\n"); + return PTR_ERR(pctrl); + } + + return 0; +} + +static void pef2256_reset_pinmux(struct pef2256_pinctrl *pef2256) +{ + u8 val; + /* + * Reset values cannot be used. + * They define the SYPR/SYPX pin mux for all the RPx and XPx pins and + * Only one pin can be muxed to SYPR and one pin can be muxed to SYPX. + * Choose here an other reset value. + */ + if (pef2256->version == PEF2256_VERSION_1_2) + val = PEF2256_12_PC_XPC_XCLK | PEF2256_12_PC_RPC_RFSP; + else + val = PEF2256_2X_PC_XPC_GPI | PEF2256_2X_PC_RPC_GPI; + + regmap_write(pef2256->regmap, PEF2256_PC1, val); + regmap_write(pef2256->regmap, PEF2256_PC2, val); + regmap_write(pef2256->regmap, PEF2256_PC3, val); + regmap_write(pef2256->regmap, PEF2256_PC4, val); +} + +static int pef2256_pinctrl_probe(struct platform_device *pdev) +{ + struct pef2256_pinctrl *pef2256_pinctrl; + struct pef2256 *pef2256; + int ret; + + pef2256_pinctrl = devm_kzalloc(&pdev->dev, sizeof(*pef2256_pinctrl), GFP_KERNEL); + if (!pef2256_pinctrl) + return -ENOMEM; + + device_set_node(&pdev->dev, dev_fwnode(pdev->dev.parent)); + + pef2256 = dev_get_drvdata(pdev->dev.parent); + + pef2256_pinctrl->dev = &pdev->dev; + pef2256_pinctrl->regmap = pef2256_get_regmap(pef2256); + pef2256_pinctrl->version = pef2256_get_version(pef2256); + + platform_set_drvdata(pdev, pef2256_pinctrl); + + pef2256_reset_pinmux(pef2256_pinctrl); + ret = pef2256_register_pinctrl(pef2256_pinctrl); + if (ret) + return ret; + + return 0; +} + +static struct platform_driver pef2256_pinctrl_driver = { + .driver = { + .name = "lantiq-pef2256-pinctrl", + }, + .probe = pef2256_pinctrl_probe, +}; +module_platform_driver(pef2256_pinctrl_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("PEF2256 pin controller driver"); +MODULE_LICENSE("GPL"); From patchwork Mon Apr 17 17:16:00 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214390 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 7B880C77B76 for ; Mon, 17 Apr 2023 17:18:58 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 70231E88; Mon, 17 Apr 2023 19:18:06 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 70231E88 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751936; bh=/QOxDZKNZhrIsNcMaibeG4ci7GAeFHw1t2NZSe4A7Js=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=sYAn7TPefZDmqKjc8rHJ5o2ESNUd2iCYwIfCZU2yTThzvoqMliM9Mdg2mjK1quOF8 PnFbj82LrIPuGswUSYCqXHV9tSoldUckQb/ogeM7//1GI3uBpSm1x1ByFGT3mbiwZc vbrgiUyc24KxA8Ik/heCF4KX/BSB9TpV0z8X3oY0= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 21390F80563; Mon, 17 Apr 2023 19:16:36 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 6/7] ASoC: codecs: Add support for the Lantiq PEF2256 codec Date: Mon, 17 Apr 2023 19:16:00 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175179477.26.13102310275124300857@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni Content-Disposition: inline The Lantiq PEF2256 is a framer and line interface component designed to fulfill all required interfacing between an analog E1/T1/J1 line and the digital PCM system highway/H.100 bus. The codec support allows to use some of the PCM system highway time-slots as audio channels to transport audio data over the E1/T1/J1 lines. It provides also line carrier detection events reported through the ALSA jack detection feature. Signed-off-by: Herve Codina --- sound/soc/codecs/Kconfig | 14 ++ sound/soc/codecs/Makefile | 2 + sound/soc/codecs/pef2256-codec.c | 390 +++++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+) create mode 100644 sound/soc/codecs/pef2256-codec.c diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0be061953e9a..4f78da914fc7 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -168,6 +168,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_PCM512x_I2C imply SND_SOC_PCM512x_SPI imply SND_SOC_PEB2466 + imply SND_SOC_PEF2256 imply SND_SOC_RK3328 imply SND_SOC_RK817 imply SND_SOC_RT274 @@ -1252,6 +1253,19 @@ config SND_SOC_PEB2466 To compile this driver as a module, choose M here: the module will be called snd-soc-peb2466. +config SND_SOC_PEF2256 + tristate "Lantiq PEF2256 codec" + depends on MFD_PEF2256 + help + Enable support for the Lantiq PEF2256 (FALC56) codec. + The PEF2256 is a framer and line interface between analog E1/T1/J1 + line and a digital PCM bus. + This codec allows to use some of the time slots available on the + PEF2256 PCM bus to transport some audio data. + + To compile this driver as a module, choose M here: the module + will be called snd-soc-pef2256. + config SND_SOC_RK3328 tristate "Rockchip RK3328 audio CODEC" select REGMAP_MMIO diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 20b388b77f1f..11bd66d46f7b 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -188,6 +188,7 @@ snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-peb2466-objs := peb2466.o +snd-soc-pef2256-objs := pef2256-codec.o snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rk817-objs := rk817_codec.o snd-soc-rl6231-objs := rl6231.o @@ -551,6 +552,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o obj-$(CONFIG_SND_SOC_PEB2466) += snd-soc-peb2466.o +obj-$(CONFIG_SND_SOC_PEF2256) += snd-soc-pef2256.o obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RK817) += snd-soc-rk817.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o diff --git a/sound/soc/codecs/pef2256-codec.c b/sound/soc/codecs/pef2256-codec.c new file mode 100644 index 000000000000..aa749fa6747e --- /dev/null +++ b/sound/soc/codecs/pef2256-codec.c @@ -0,0 +1,390 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// pef2256.c -- Lantiq PEF2256 ALSA SoC driver +// +// Copyright 2023 CS GROUP France +// +// Author: Herve Codina + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PEF2256_NB_CHANNEL 32 +#define PEF2256_JACK_MASK (SND_JACK_LINEIN | SND_JACK_LINEOUT) + +struct pef2256_codec { + struct pef2256 *pef2256; + struct device *dev; + struct snd_soc_jack jack; + struct notifier_block nb; + struct work_struct carrier_work; + int max_chan_playback; + int max_chan_capture; +}; + +static int pef2256_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int width) +{ + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(dai->component); + + switch (width) { + case 0: + /* Not set -> default 8 */ + case 8: + break; + default: + dev_err(dai->dev, "tdm slot width %d not supported\n", width); + return -EINVAL; + } + + pef2256->max_chan_playback = hweight32(tx_mask); + if (pef2256->max_chan_playback > PEF2256_NB_CHANNEL) { + dev_err(dai->dev, "too much tx slots defined (mask = 0x%x) support max %d\n", + tx_mask, PEF2256_NB_CHANNEL); + return -EINVAL; + } + + pef2256->max_chan_capture = hweight32(rx_mask); + if (pef2256->max_chan_capture > PEF2256_NB_CHANNEL) { + dev_err(dai->dev, "too much rx slots defined (mask = 0x%x) support max %d\n", + rx_mask, PEF2256_NB_CHANNEL); + return -EINVAL; + } + + return 0; +} + +/* + * The constraints for format/channel is to match with the number of 8bit + * time-slots available. + */ +static int pef2256_dai_hw_rule_channels_by_format(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_interval *c = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS); + snd_pcm_format_t format = params_format(params); + struct snd_interval ch = {0}; + + switch (snd_pcm_format_physical_width(format)) { + case 8: + ch.max = nb_ts; + break; + case 16: + ch.max = nb_ts / 2; + break; + case 32: + ch.max = nb_ts / 4; + break; + case 64: + ch.max = nb_ts / 8; + break; + default: + dev_err(dai->dev, "format physical width %u not supported\n", + snd_pcm_format_physical_width(format)); + return -EINVAL; + } + + ch.min = ch.max ? 1 : 0; + + return snd_interval_refine(c, &ch); +} + +static int pef2256_dai_hw_rule_playback_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(dai->component); + + return pef2256_dai_hw_rule_channels_by_format(dai, params, pef2256->max_chan_playback); +} + +static int pef2256_dai_hw_rule_capture_channels_by_format(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(dai->component); + + return pef2256_dai_hw_rule_channels_by_format(dai, params, pef2256->max_chan_capture); +} + +static int pef2256_dai_hw_rule_format_by_channels(struct snd_soc_dai *dai, + struct snd_pcm_hw_params *params, + unsigned int nb_ts) +{ + struct snd_mask *f_old = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + unsigned int channels = params_channels(params); + unsigned int slot_width; + struct snd_mask f_new; + unsigned int i; + + if (!channels || channels > nb_ts) { + dev_err(dai->dev, "channels %u not supported\n", nb_ts); + return -EINVAL; + } + + slot_width = (nb_ts / channels) * 8; + + snd_mask_none(&f_new); + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + if (snd_mask_test(f_old, i)) { + if (snd_pcm_format_physical_width(i) <= slot_width) + snd_mask_set(&f_new, i); + } + } + + return snd_mask_refine(f_old, &f_new); +} + +static int pef2256_dai_hw_rule_playback_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(dai->component); + + return pef2256_dai_hw_rule_format_by_channels(dai, params, pef2256->max_chan_playback); +} + +static int pef2256_dai_hw_rule_capture_format_by_channels(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_soc_dai *dai = rule->private; + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(dai->component); + + return pef2256_dai_hw_rule_format_by_channels(dai, params, pef2256->max_chan_capture); +} + +static u64 pef2256_formats(u8 nb_ts) +{ + u64 formats; + unsigned int chan_width; + unsigned int format_width; + int i; + + if (!nb_ts) + return 0; + + formats = 0; + chan_width = nb_ts * 8; + for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) { + /* Support physical width multiple of 8bit */ + format_width = snd_pcm_format_physical_width(i); + if (format_width == 0 || format_width % 8) + continue; + + /* + * And support physical width that can fit N times in the + * channel + */ + if (format_width > chan_width || chan_width % format_width) + continue; + + formats |= (1ULL << i); + } + return formats; +} + +static int pef2256_dai_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(dai->component); + snd_pcm_hw_rule_func_t hw_rule_channels_by_format; + snd_pcm_hw_rule_func_t hw_rule_format_by_channels; + unsigned int frame_bits; + u64 format; + int ret; + + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { + format = pef2256_formats(pef2256->max_chan_capture); + hw_rule_channels_by_format = pef2256_dai_hw_rule_capture_channels_by_format; + hw_rule_format_by_channels = pef2256_dai_hw_rule_capture_format_by_channels; + frame_bits = pef2256->max_chan_capture * 8; + } else { + format = pef2256_formats(pef2256->max_chan_playback); + hw_rule_channels_by_format = pef2256_dai_hw_rule_playback_channels_by_format; + hw_rule_format_by_channels = pef2256_dai_hw_rule_playback_format_by_channels; + frame_bits = pef2256->max_chan_playback * 8; + } + + ret = snd_pcm_hw_constraint_mask64(substream->runtime, + SNDRV_PCM_HW_PARAM_FORMAT, format); + if (ret) { + dev_err(dai->dev, "Failed to add format constraint (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + hw_rule_channels_by_format, dai, + SNDRV_PCM_HW_PARAM_FORMAT, -1); + if (ret) { + dev_err(dai->dev, "Failed to add channels rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_rule_add(substream->runtime, 0, SNDRV_PCM_HW_PARAM_FORMAT, + hw_rule_format_by_channels, dai, + SNDRV_PCM_HW_PARAM_CHANNELS, -1); + if (ret) { + dev_err(dai->dev, "Failed to add format rule (%d)\n", ret); + return ret; + } + + ret = snd_pcm_hw_constraint_single(substream->runtime, + SNDRV_PCM_HW_PARAM_FRAME_BITS, + frame_bits); + if (ret < 0) { + dev_err(dai->dev, "Failed to add frame_bits constraint (%d)\n", ret); + return ret; + } + + return 0; +} + +static u64 pef2256_dai_formats[] = { + SND_SOC_POSSIBLE_DAIFMT_DSP_B, +}; + +static const struct snd_soc_dai_ops pef2256_dai_ops = { + .startup = pef2256_dai_startup, + .set_tdm_slot = pef2256_dai_set_tdm_slot, + .auto_selectable_formats = pef2256_dai_formats, + .num_auto_selectable_formats = ARRAY_SIZE(pef2256_dai_formats), +}; + +static struct snd_soc_dai_driver pef2256_dai_driver = { + .name = "pef2256", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = PEF2256_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = PEF2256_NB_CHANNEL, + .rates = SNDRV_PCM_RATE_8000, + .formats = U64_MAX, /* Will be refined on DAI .startup() */ + }, + .ops = &pef2256_dai_ops, +}; + +static void pef2256_carrier_work(struct work_struct *work) +{ + struct pef2256_codec *pef2256 = container_of(work, struct pef2256_codec, carrier_work); + int status; + + status = pef2256_get_carrier(pef2256->pef2256) ? PEF2256_JACK_MASK : 0; + snd_soc_jack_report(&pef2256->jack, status, PEF2256_JACK_MASK); +} + +static int pef2256_carrier_notifier(struct notifier_block *nb, unsigned long action, + void *data) +{ + struct pef2256_codec *pef2256 = container_of(nb, struct pef2256_codec, nb); + + switch (action) { + case PEF2256_EVENT_CARRIER: + queue_work(system_power_efficient_wq, &pef2256->carrier_work); + break; + default: + return NOTIFY_DONE; + } + + return NOTIFY_OK; +} + +static int pef2256_component_probe(struct snd_soc_component *component) +{ + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(component); + char *name; + int ret; + + INIT_WORK(&pef2256->carrier_work, pef2256_carrier_work); + + name = "carrier"; + if (component->name_prefix) { + name = kasprintf(GFP_KERNEL, "%s carrier", component->name_prefix); + if (!name) + return -ENOMEM; + } + + ret = snd_soc_card_jack_new(component->card, name, PEF2256_JACK_MASK, &pef2256->jack); + if (component->name_prefix) + kfree(name); /* A copy is done by snd_soc_card_jack_new */ + if (ret) { + dev_err(component->dev, "Cannot create jack\n"); + return ret; + } + + pef2256->nb.notifier_call = pef2256_carrier_notifier; + ret = pef2256_register_event_notifier(pef2256->pef2256, &pef2256->nb); + if (ret) { + dev_err(component->dev, "Cannot register event notifier\n"); + return ret; + } + + /* Queue work to set the initial value */ + queue_work(system_power_efficient_wq, &pef2256->carrier_work); + + return 0; +} + +static void pef2256_component_remove(struct snd_soc_component *component) +{ + struct pef2256_codec *pef2256 = snd_soc_component_get_drvdata(component); + + pef2256_unregister_event_notifier(pef2256->pef2256, &pef2256->nb); + cancel_work_sync(&pef2256->carrier_work); +} + +static const struct snd_soc_component_driver pef2256_component_driver = { + .probe = pef2256_component_probe, + .remove = pef2256_component_remove, + .endianness = 1, +}; + +static int pef2256_codec_probe(struct platform_device *pdev) +{ + struct pef2256_codec *pef2256; + + pef2256 = devm_kzalloc(&pdev->dev, sizeof(*pef2256), GFP_KERNEL); + if (!pef2256) + return -ENOMEM; + + pef2256->dev = &pdev->dev; + pef2256->pef2256 = dev_get_drvdata(pef2256->dev->parent); + + platform_set_drvdata(pdev, pef2256); + + return devm_snd_soc_register_component(&pdev->dev, &pef2256_component_driver, + &pef2256_dai_driver, 1); +} + +static const struct of_device_id pef2256_codec_of_match[] = { + { .compatible = "lantiq,pef2256-codec" }, + {} /* sentinel */ +}; +MODULE_DEVICE_TABLE(of, pef2256_codec_of_match); + +static struct platform_driver pef2256_codec_driver = { + .driver = { + .name = "lantiq-pef2256-codec", + .of_match_table = pef2256_codec_of_match, + }, + .probe = pef2256_codec_probe, +}; +module_platform_driver(pef2256_codec_driver); + +MODULE_AUTHOR("Herve Codina "); +MODULE_DESCRIPTION("PEF2256 ALSA SoC driver"); +MODULE_LICENSE("GPL"); From patchwork Mon Apr 17 17:16:01 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Herve Codina X-Patchwork-Id: 13214389 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from alsa0.perex.cz (alsa0.perex.cz [77.48.224.243]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 3D853C77B7A for ; Mon, 17 Apr 2023 17:18:50 +0000 (UTC) Received: from alsa1.perex.cz (alsa1.perex.cz [207.180.221.201]) (using TLSv1.2 with cipher ADH-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by alsa0.perex.cz (Postfix) with ESMTPS id 2A5D8DF3; Mon, 17 Apr 2023 19:17:58 +0200 (CEST) DKIM-Filter: OpenDKIM Filter v2.11.0 alsa0.perex.cz 2A5D8DF3 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=alsa-project.org; s=default; t=1681751928; bh=XDU4jlyzmdQW4/QfGeW1fbimLHGUhYg1t2ws2GQbQ4M=; h=To:Subject:Date:In-Reply-To:References:List-Id:List-Archive: List-Help:List-Owner:List-Post:List-Subscribe:List-Unsubscribe: From:Reply-To:Cc:From; b=eQ7YM6WMXKG72NC1I0sFQ76UXzcIXEO0xEpa+SDBN9j+01GQCF339fXKxqsvPKgyQ g7VUc/3PnlY5o5NKUjywZb7SgFLdaWXy2C9Vdz5PmjuH11/MyNH+K5/qYIVtZyKew+ lM3j+ToGr9oqIBjtDVNwid2xidAo2ScdFpiLKjTw= Received: from mailman-core.alsa-project.org (mailman-core.alsa-project.org [10.254.200.10]) by alsa1.perex.cz (Postfix) with ESMTP id 056DEF80552; Mon, 17 Apr 2023 19:16:31 +0200 (CEST) To: Herve Codina , Lee Jones , Rob Herring , Krzysztof Kozlowski , Liam Girdwood , Mark Brown , Jaroslav Kysela , Takashi Iwai Subject: [PATCH v6 7/7] MAINTAINERS: Add the Lantiq PEF2256 driver entry Date: Mon, 17 Apr 2023 19:16:01 +0200 In-Reply-To: <20230417171601.74656-1-herve.codina@bootlin.com> References: <20230417171601.74656-1-herve.codina@bootlin.com> X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-alsa-devel.alsa-project.org-0; header-match-alsa-devel.alsa-project.org-1; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.8 Precedence: list List-Id: "Alsa-devel mailing list for ALSA developers - http://www.alsa-project.org" Archived-At: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Message-ID: <168175178971.26.14882333995549062928@mailman-core.alsa-project.org> X-Patchwork-Original-From: Herve Codina via Alsa-devel From: Herve Codina Reply-To: Herve Codina Cc: linux-kernel@vger.kernel.org, devicetree@vger.kernel.org, alsa-devel@alsa-project.org, Christophe Leroy , Thomas Petazzoni Content-Disposition: inline After contributing the driver, add myself as the maintainer for the Lantiq PEF2256 driver. Signed-off-by: Herve Codina --- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fcb69242cd19..dc579a8b5562 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11651,6 +11651,18 @@ S: Maintained F: arch/mips/lantiq F: drivers/soc/lantiq +LANTIQ PEF2256 DRIVER +M: Herve Codina +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-platform-devices-pef2256 +F: Documentation/devicetree/bindings/mfd/lantiq,pef2256.yaml +F: drivers/mfd/pef2256-regs.h +F: drivers/mfd/pef2256.c +F: drivers/pinctrl/pinctrl-pef2256-regs.h +F: drivers/pinctrl/pinctrl-pef2256.c +F: include/linux/mfd/pef2256.h +F: sound/soc/codecs/pef2256-codec.c + LASI 53c700 driver for PARISC M: "James E.J. Bottomley" L: linux-scsi@vger.kernel.org