Received: by 2002:ac0:a582:0:0:0:0:0 with SMTP id m2-v6csp3583562imm; Mon, 8 Oct 2018 06:27:33 -0700 (PDT) X-Google-Smtp-Source: ACcGV60RUqox2VRxdwqmkSCtZS6V6upQ3kUQCrjFqTrYMgxIZGuIoAmqJUQxf66ITLKqjphEdiU4 X-Received: by 2002:a17:902:40d:: with SMTP id 13-v6mr24039413ple.257.1539005253826; Mon, 08 Oct 2018 06:27:33 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1539005253; cv=none; d=google.com; s=arc-20160816; b=rCbsnpV0CZ2+3xYsnVA0wwPEWaFm5G6v0obEjBmbQEgK2QyYRLRTk5sMGK/og9ojcr sXb4kZBAut6jYnw5eI95ZvhoI+mfT8ASpY1PuLHVQi4xGwOXyCIigD7TdbXsvS4X2Run iJNaVViZ/ImD9yu8JD0gbLTYVFD7oOe0Mmsvr2P0LuSOPPPLWkdgbIJL6Zd7iolkTXby jVs2kl2vYUeIaOTiWXLI7LnaI7NP5CvqGUQ3m6GBsbhF89iI3REe2EYhSWu1WCKBebi9 M8nmYEsghfmzMd+zasoUJ8IhEjeZCc8tsJFcm0b6VJyIHCjUdSRK/Ktia05CiVFzZqAX qCoQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from; bh=WJkOCXj2vGeSxhJSKQhY07gMLm06c0J8UktTANcYRwE=; b=mz6Upzvn4H7bRo8JU9VEDkSmY1blvQFlvA+318d5W4cHE2eqgBEg1QR4YXl/15Azmh kHBiQJxynTQU8d0rZGx1RJBfuNBPLeFEZKNTJ0j6QdZTx1oBlRsO05flAJrlsS06ky4W Ygw3d7z/CJAkGQ/GYkXQLfehM3S11IsfHjFrNVO98KLCJhjUCjxBQtJ6MSN7jdBWteB1 T2dZ1PDKUdLxKOmozHEKL/mvwS5UnaCP7dY6EDUaUDtFBIjBplZUEThpMdSrBNfIP747 TognEUma1tIukYBKuZppcEUKj0Ep6cP3Xj3bjLnErzlJj0CCZZjpQMG9SkIpTlxV0aLN kJ7g== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=cirrus.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id j16-v6si16241126pgm.501.2018.10.08.06.27.18; Mon, 08 Oct 2018 06:27:33 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=fail (p=NONE sp=NONE dis=NONE) header.from=cirrus.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726592AbeJHUhj (ORCPT + 99 others); Mon, 8 Oct 2018 16:37:39 -0400 Received: from mx0a-001ae601.pphosted.com ([67.231.149.25]:46226 "EHLO mx0b-001ae601.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726383AbeJHUhi (ORCPT ); Mon, 8 Oct 2018 16:37:38 -0400 Received: from pps.filterd (m0077473.ppops.net [127.0.0.1]) by mx0a-001ae601.pphosted.com (8.16.0.22/8.16.0.22) with SMTP id w98DOXuv006913; Mon, 8 Oct 2018 08:25:46 -0500 Authentication-Results: ppops.net; spf=none smtp.mailfrom=ckeepax@opensource.cirrus.com Received: from mail1.cirrus.com (mail1.cirrus.com [141.131.3.20]) by mx0a-001ae601.pphosted.com with ESMTP id 2mxwhea5he-1; Mon, 08 Oct 2018 08:25:45 -0500 Received: from EX17.ad.cirrus.com (unknown [172.20.9.81]) by mail1.cirrus.com (Postfix) with ESMTP id E5BC9611E124; Mon, 8 Oct 2018 08:25:44 -0500 (CDT) Received: from imbe.wolfsonmicro.main (198.61.95.81) by EX17.ad.cirrus.com (172.20.9.81) with Microsoft SMTP Server id 14.3.408.0; Mon, 8 Oct 2018 14:25:43 +0100 Received: from algalon.ad.cirrus.com (algalon.ad.cirrus.com [198.90.251.122]) by imbe.wolfsonmicro.main (8.14.4/8.14.4) with ESMTP id w98DPg7v012033; Mon, 8 Oct 2018 14:25:43 +0100 From: Charles Keepax To: , , , , , CC: , , , , , , Subject: [PATCH v2 5/5] pinctrl: lochnagar: Add support for the Cirrus Logic Lochnagar Date: Mon, 8 Oct 2018 14:25:42 +0100 Message-ID: <20181008132542.19775-5-ckeepax@opensource.cirrus.com> X-Mailer: git-send-email 2.11.0 In-Reply-To: <20181008132542.19775-1-ckeepax@opensource.cirrus.com> References: <20181008132542.19775-1-ckeepax@opensource.cirrus.com> MIME-Version: 1.0 Content-Type: text/plain X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 priorityscore=1501 malwarescore=0 suspectscore=2 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 mlxscore=0 impostorscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1807170000 definitions=main-1810080131 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Charles Keepax Lochnagar is an evaluation and development board for Cirrus Logic Smart CODEC and Amp devices. It allows the connection of most Cirrus Logic devices on mini-cards, as well as allowing connection of various application processor systems to provide a full evaluation platform. This driver supports the board controller chip on the Lochnagar board. Lochnagar provides many pins which can generally be used for an audio function such as an AIF or a PDM interface, but also as GPIOs. Signed-off-by: Charles Keepax --- Changes since v1: - Remove binding include file - Update commit message a little Thanks, Charles drivers/pinctrl/cirrus/Kconfig | 10 + drivers/pinctrl/cirrus/Makefile | 2 + drivers/pinctrl/cirrus/pinctrl-lochnagar.c | 1158 ++++++++++++++++++++++++++++ drivers/pinctrl/cirrus/pinctrl-lochnagar.h | 112 +++ 4 files changed, 1282 insertions(+) create mode 100644 drivers/pinctrl/cirrus/pinctrl-lochnagar.c create mode 100644 drivers/pinctrl/cirrus/pinctrl-lochnagar.h diff --git a/drivers/pinctrl/cirrus/Kconfig b/drivers/pinctrl/cirrus/Kconfig index 27013e5949bc1..74af07e251741 100644 --- a/drivers/pinctrl/cirrus/Kconfig +++ b/drivers/pinctrl/cirrus/Kconfig @@ -1,3 +1,13 @@ +config PINCTRL_LOCHNAGAR + tristate "Cirrus Logic Lochnagar pinctrl driver" + depends on MFD_LOCHNAGAR + select PINMUX + select PINCONF + select GENERIC_PINCONF + help + This driver supports configuring the GPIO and other pin configuration + of the Cirrus Logic Lochnagar audio development board. + # This is all selected by the Madera MFD driver Kconfig options config PINCTRL_MADERA tristate diff --git a/drivers/pinctrl/cirrus/Makefile b/drivers/pinctrl/cirrus/Makefile index 6e4938cde9e30..20baebf438f62 100644 --- a/drivers/pinctrl/cirrus/Makefile +++ b/drivers/pinctrl/cirrus/Makefile @@ -1,4 +1,6 @@ # Cirrus Logic pinctrl drivers +obj-$(CONFIG_PINCTRL_LOCHNAGAR) += pinctrl-lochnagar.o + pinctrl-madera-objs := pinctrl-madera-core.o ifeq ($(CONFIG_PINCTRL_CS47L35),y) pinctrl-madera-objs += pinctrl-cs47l35.o diff --git a/drivers/pinctrl/cirrus/pinctrl-lochnagar.c b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c new file mode 100644 index 0000000000000..931f89ade760c --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.c @@ -0,0 +1,1158 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Lochnagar pin and GPIO control + * + * Copyright (c) 2017-2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../pinctrl-utils.h" +#include "pinctrl-lochnagar.h" + +#define LN_PIN_GPIO(REV, ID, NAME, REG, SHIFT, INVERT) \ +static const struct lochnagar_pin lochnagar##REV##_##ID##_pin = { \ + .name = NAME, .type = LN_PTYPE_GPIO, .reg = LOCHNAGAR##REV##_##REG, \ + .shift = LOCHNAGAR##REV##_##SHIFT##_SHIFT, .invert = INVERT, \ +} + +#define LN_PIN_SAIF(REV, ID, NAME) \ +static const struct lochnagar_pin lochnagar##REV##_##ID##_pin = \ + { .name = NAME, .type = LN_PTYPE_AIF, } + +#define LN_PIN_AIF(REV, ID) \ + LN_PIN_SAIF(REV, ID##_BCLK, LN_##ID##_STR"-bclk"); \ + LN_PIN_SAIF(REV, ID##_LRCLK, LN_##ID##_STR"-lrclk"); \ + LN_PIN_SAIF(REV, ID##_RXDAT, LN_##ID##_STR"-rxdat"); \ + LN_PIN_SAIF(REV, ID##_TXDAT, LN_##ID##_STR"-txdat") + +#define LN1_PIN_GPIO(ID, NAME, REG, SHIFT, INVERT) \ + LN_PIN_GPIO(1, ID, NAME, REG, SHIFT, INVERT) + +#define LN1_PIN_MUX(ID, NAME) \ +static const struct lochnagar_pin lochnagar1_##ID##_pin = \ + { .name = NAME, .type = LN_PTYPE_MUX, .reg = LOCHNAGAR1_##ID, } + +#define LN1_PIN_AIF(ID) LN_PIN_AIF(1, ID) + +#define LN2_PIN_GPIO(ID, NAME, REG, SHIFT, INVERT) \ + LN_PIN_GPIO(2, ID, NAME, REG, SHIFT, INVERT) + +#define LN2_PIN_MUX(ID, NAME) \ +static const struct lochnagar_pin lochnagar2_##ID##_pin = \ + { .name = NAME, .type = LN_PTYPE_MUX, .reg = LOCHNAGAR2_GPIO_##ID, } + +#define LN2_PIN_AIF(ID) LN_PIN_AIF(2, ID) + +#define LN2_PIN_GAI(ID) \ + LN2_PIN_MUX(ID##_BCLK, LN_##ID##_STR"-bclk"); \ + LN2_PIN_MUX(ID##_LRCLK, LN_##ID##_STR"-lrclk"); \ + LN2_PIN_MUX(ID##_RXDAT, LN_##ID##_STR"-rxdat"); \ + LN2_PIN_MUX(ID##_TXDAT, LN_##ID##_STR"-txdat") + +#define LN_PIN(REV, ID) [LOCHNAGAR##REV##_PIN_##ID] = { \ + .number = LOCHNAGAR##REV##_PIN_##ID, \ + .name = lochnagar##REV##_##ID##_pin.name, \ + .drv_data = (void *)&lochnagar##REV##_##ID##_pin, \ +} + +#define LN1_PIN(ID) LN_PIN(1, ID) +#define LN2_PIN(ID) LN_PIN(2, ID) + +#define LN_PINS(REV, ID) \ + LN_PIN(REV, ID##_BCLK), LN_PIN(REV, ID##_LRCLK), \ + LN_PIN(REV, ID##_RXDAT), LN_PIN(REV, ID##_TXDAT) + +#define LN1_PINS(ID) LN_PINS(1, ID) +#define LN2_PINS(ID) LN_PINS(2, ID) + +enum lochnagar_pin_type { + LN_PTYPE_GPIO, + LN_PTYPE_MUX, + LN_PTYPE_AIF, + LN_PTYPE_COUNT, +}; + +struct lochnagar_pin { + const char name[20]; + + enum lochnagar_pin_type type; + + unsigned int reg; + int shift; + bool invert; +}; + +LN1_PIN_GPIO(CDC_RESET, "codec-reset", RST, CDC_RESET, 1); +LN1_PIN_GPIO(DSP_RESET, "dsp-reset", RST, DSP_RESET, 1); +LN1_PIN_GPIO(CDC_CIF1MODE, "codec-cif1mode", I2C_CTRL, CDC_CIF_MODE, 0); +LN1_PIN_MUX(GF_GPIO2, "gf-gpio2"); +LN1_PIN_MUX(GF_GPIO3, "gf-gpio3"); +LN1_PIN_MUX(GF_GPIO7, "gf-gpio7"); +LN1_PIN_MUX(LED1, "led1"); +LN1_PIN_MUX(LED2, "led2"); +LN1_PIN_AIF(CDC_AIF1); +LN1_PIN_AIF(CDC_AIF2); +LN1_PIN_AIF(CDC_AIF3); +LN1_PIN_AIF(DSP_AIF1); +LN1_PIN_AIF(DSP_AIF2); +LN1_PIN_AIF(PSIA1); +LN1_PIN_AIF(PSIA2); +LN1_PIN_AIF(SPDIF_AIF); +LN1_PIN_AIF(GF_AIF1); +LN1_PIN_AIF(GF_AIF2); +LN1_PIN_AIF(GF_AIF3); +LN1_PIN_AIF(GF_AIF4); + +LN2_PIN_GPIO(CDC_RESET, "codec-reset", MINICARD_RESETS, CDC_RESET, 1); +LN2_PIN_GPIO(DSP_RESET, "dsp-reset", MINICARD_RESETS, DSP_RESET, 1); +LN2_PIN_GPIO(CDC_CIF1MODE, "codec-cif1mode", COMMS_CTRL4, CDC_CIF1MODE, 0); +LN2_PIN_GPIO(CDC_LDOENA, "codec-ldoena", POWER_CTRL, PWR_ENA, 0); +LN2_PIN_GPIO(SPDIF_HWMODE, "spdif-hwmode", SPDIF_CTRL, SPDIF_HWMODE, 0); +LN2_PIN_GPIO(SPDIF_RESET, "spdif-reset", SPDIF_CTRL, SPDIF_RESET, 1); +LN2_PIN_MUX(FPGA_GPIO1, "fgpa-gpio1"); +LN2_PIN_MUX(FPGA_GPIO2, "fgpa-gpio2"); +LN2_PIN_MUX(FPGA_GPIO3, "fgpa-gpio3"); +LN2_PIN_MUX(FPGA_GPIO4, "fgpa-gpio4"); +LN2_PIN_MUX(FPGA_GPIO5, "fgpa-gpio5"); +LN2_PIN_MUX(FPGA_GPIO6, "fgpa-gpio6"); +LN2_PIN_MUX(CDC_GPIO1, "codec-gpio1"); +LN2_PIN_MUX(CDC_GPIO2, "codec-gpio2"); +LN2_PIN_MUX(CDC_GPIO3, "codec-gpio3"); +LN2_PIN_MUX(CDC_GPIO4, "codec-gpio4"); +LN2_PIN_MUX(CDC_GPIO5, "codec-gpio5"); +LN2_PIN_MUX(CDC_GPIO6, "codec-gpio6"); +LN2_PIN_MUX(CDC_GPIO7, "codec-gpio7"); +LN2_PIN_MUX(CDC_GPIO8, "codec-gpio8"); +LN2_PIN_MUX(DSP_GPIO1, "dsp-gpio1"); +LN2_PIN_MUX(DSP_GPIO2, "dsp-gpio2"); +LN2_PIN_MUX(DSP_GPIO3, "dsp-gpio3"); +LN2_PIN_MUX(DSP_GPIO4, "dsp-gpio4"); +LN2_PIN_MUX(DSP_GPIO5, "dsp-gpio5"); +LN2_PIN_MUX(DSP_GPIO6, "dsp-gpio6"); +LN2_PIN_MUX(GF_GPIO2, "gf-gpio2"); +LN2_PIN_MUX(GF_GPIO3, "gf-gpio3"); +LN2_PIN_MUX(GF_GPIO7, "gf-gpio7"); +LN2_PIN_MUX(DSP_UART1_RX, "dsp-uart1-rx"); +LN2_PIN_MUX(DSP_UART1_TX, "dsp-uart1-tx"); +LN2_PIN_MUX(DSP_UART2_RX, "dsp-uart2-rx"); +LN2_PIN_MUX(DSP_UART2_TX, "dsp-uart2-tx"); +LN2_PIN_MUX(GF_UART2_RX, "gf-uart2-rx"); +LN2_PIN_MUX(GF_UART2_TX, "gf-uart2-tx"); +LN2_PIN_MUX(USB_UART_RX, "usb-uart-rx"); +LN2_PIN_MUX(CDC_PDMCLK1, "codec-pdmclk1"); +LN2_PIN_MUX(CDC_PDMDAT1, "codec-pdmdat1"); +LN2_PIN_MUX(CDC_PDMCLK2, "codec-pdmclk2"); +LN2_PIN_MUX(CDC_PDMDAT2, "codec-pdmdat2"); +LN2_PIN_MUX(CDC_DMICCLK1, "codec-dmicclk1"); +LN2_PIN_MUX(CDC_DMICDAT1, "codec-dmicdat1"); +LN2_PIN_MUX(CDC_DMICCLK2, "codec-dmicclk2"); +LN2_PIN_MUX(CDC_DMICDAT2, "codec-dmicdat2"); +LN2_PIN_MUX(CDC_DMICCLK3, "codec-dmicclk3"); +LN2_PIN_MUX(CDC_DMICDAT3, "codec-dmicdat3"); +LN2_PIN_MUX(CDC_DMICCLK4, "codec-dmicclk4"); +LN2_PIN_MUX(CDC_DMICDAT4, "codec-dmicdat4"); +LN2_PIN_MUX(DSP_DMICCLK1, "dsp-dmicclk1"); +LN2_PIN_MUX(DSP_DMICDAT1, "dsp-dmicdat1"); +LN2_PIN_MUX(DSP_DMICCLK2, "dsp-dmicclk2"); +LN2_PIN_MUX(DSP_DMICDAT2, "dsp-dmicdat2"); +LN2_PIN_MUX(I2C2_SCL, "i2c2-scl"); +LN2_PIN_MUX(I2C2_SDA, "i2c2-sda"); +LN2_PIN_MUX(I2C3_SCL, "i2c3-scl"); +LN2_PIN_MUX(I2C3_SDA, "i2c3-sda"); +LN2_PIN_MUX(I2C4_SCL, "i2c4-scl"); +LN2_PIN_MUX(I2C4_SDA, "i2c4-sda"); +LN2_PIN_MUX(DSP_STANDBY, "dsp-standby"); +LN2_PIN_MUX(CDC_MCLK1, "codec-mclk1"); +LN2_PIN_MUX(CDC_MCLK2, "codec-mclk2"); +LN2_PIN_MUX(DSP_CLKIN, "dsp-clkin"); +LN2_PIN_MUX(PSIA1_MCLK, "psia1-mclk"); +LN2_PIN_MUX(PSIA2_MCLK, "psia2-mclk"); +LN2_PIN_MUX(GF_GPIO1, "gf-gpio1"); +LN2_PIN_MUX(GF_GPIO5, "gf-gpio5"); +LN2_PIN_MUX(DSP_GPIO20, "dsp-gpio20"); +LN2_PIN_GAI(CDC_AIF1); +LN2_PIN_GAI(CDC_AIF2); +LN2_PIN_GAI(CDC_AIF3); +LN2_PIN_GAI(DSP_AIF1); +LN2_PIN_GAI(DSP_AIF2); +LN2_PIN_GAI(PSIA1); +LN2_PIN_GAI(PSIA2); +LN2_PIN_GAI(GF_AIF1); +LN2_PIN_GAI(GF_AIF2); +LN2_PIN_GAI(GF_AIF3); +LN2_PIN_GAI(GF_AIF4); +LN2_PIN_AIF(SPDIF_AIF); +LN2_PIN_AIF(USB_AIF1); +LN2_PIN_AIF(USB_AIF2); +LN2_PIN_AIF(ADAT_AIF); +LN2_PIN_AIF(SOUNDCARD_AIF); + +static const struct pinctrl_pin_desc lochnagar1_pins[] = { + LN1_PIN(CDC_RESET), LN1_PIN(DSP_RESET), LN1_PIN(CDC_CIF1MODE), + LN1_PIN(GF_GPIO2), LN1_PIN(GF_GPIO3), LN1_PIN(GF_GPIO7), + LN1_PIN(LED1), LN1_PIN(LED2), + LN1_PINS(CDC_AIF1), LN1_PINS(CDC_AIF2), LN1_PINS(CDC_AIF3), + LN1_PINS(DSP_AIF1), LN1_PINS(DSP_AIF2), + LN1_PINS(PSIA1), LN1_PINS(PSIA2), + LN1_PINS(SPDIF_AIF), + LN1_PINS(GF_AIF1), LN1_PINS(GF_AIF2), + LN1_PINS(GF_AIF3), LN1_PINS(GF_AIF4), +}; + +static const struct pinctrl_pin_desc lochnagar2_pins[] = { + LN2_PIN(CDC_RESET), LN2_PIN(DSP_RESET), LN2_PIN(CDC_CIF1MODE), + LN2_PIN(CDC_LDOENA), + LN2_PIN(SPDIF_HWMODE), LN2_PIN(SPDIF_RESET), + LN2_PIN(FPGA_GPIO1), LN2_PIN(FPGA_GPIO2), LN2_PIN(FPGA_GPIO3), + LN2_PIN(FPGA_GPIO4), LN2_PIN(FPGA_GPIO5), LN2_PIN(FPGA_GPIO6), + LN2_PIN(CDC_GPIO1), LN2_PIN(CDC_GPIO2), LN2_PIN(CDC_GPIO3), + LN2_PIN(CDC_GPIO4), LN2_PIN(CDC_GPIO5), LN2_PIN(CDC_GPIO6), + LN2_PIN(CDC_GPIO7), LN2_PIN(CDC_GPIO8), + LN2_PIN(DSP_GPIO1), LN2_PIN(DSP_GPIO2), LN2_PIN(DSP_GPIO3), + LN2_PIN(DSP_GPIO4), LN2_PIN(DSP_GPIO5), LN2_PIN(DSP_GPIO6), + LN2_PIN(DSP_GPIO20), + LN2_PIN(GF_GPIO1), LN2_PIN(GF_GPIO2), LN2_PIN(GF_GPIO3), + LN2_PIN(GF_GPIO5), LN2_PIN(GF_GPIO7), + LN2_PINS(CDC_AIF1), LN2_PINS(CDC_AIF2), LN2_PINS(CDC_AIF3), + LN2_PINS(DSP_AIF1), LN2_PINS(DSP_AIF2), + LN2_PINS(PSIA1), LN2_PINS(PSIA2), + LN2_PINS(GF_AIF1), LN2_PINS(GF_AIF2), + LN2_PINS(GF_AIF3), LN2_PINS(GF_AIF4), + LN2_PIN(DSP_UART1_RX), LN2_PIN(DSP_UART1_TX), + LN2_PIN(DSP_UART2_RX), LN2_PIN(DSP_UART2_TX), + LN2_PIN(GF_UART2_RX), LN2_PIN(GF_UART2_TX), + LN2_PIN(USB_UART_RX), + LN2_PIN(CDC_PDMCLK1), LN2_PIN(CDC_PDMDAT1), + LN2_PIN(CDC_PDMCLK2), LN2_PIN(CDC_PDMDAT2), + LN2_PIN(CDC_DMICCLK1), LN2_PIN(CDC_DMICDAT1), + LN2_PIN(CDC_DMICCLK2), LN2_PIN(CDC_DMICDAT2), + LN2_PIN(CDC_DMICCLK3), LN2_PIN(CDC_DMICDAT3), + LN2_PIN(CDC_DMICCLK4), LN2_PIN(CDC_DMICDAT4), + LN2_PIN(DSP_DMICCLK1), LN2_PIN(DSP_DMICDAT1), + LN2_PIN(DSP_DMICCLK2), LN2_PIN(DSP_DMICDAT2), + LN2_PIN(I2C2_SCL), LN2_PIN(I2C2_SDA), + LN2_PIN(I2C3_SCL), LN2_PIN(I2C3_SDA), + LN2_PIN(I2C4_SCL), LN2_PIN(I2C4_SDA), + LN2_PIN(DSP_STANDBY), + LN2_PIN(CDC_MCLK1), LN2_PIN(CDC_MCLK2), + LN2_PIN(DSP_CLKIN), + LN2_PIN(PSIA1_MCLK), LN2_PIN(PSIA2_MCLK), + LN2_PINS(SPDIF_AIF), + LN2_PINS(USB_AIF1), LN2_PINS(USB_AIF2), + LN2_PINS(ADAT_AIF), + LN2_PINS(SOUNDCARD_AIF), +}; + +#define LN_AIF_PINS(REV, ID) \ + LOCHNAGAR##REV##_PIN_##ID##_BCLK, \ + LOCHNAGAR##REV##_PIN_##ID##_LRCLK, \ + LOCHNAGAR##REV##_PIN_##ID##_TXDAT, \ + LOCHNAGAR##REV##_PIN_##ID##_RXDAT, + +#define LN1_AIF(ID, CTRL) \ +static const struct lochnagar_aif lochnagar1_##ID##_aif = { \ + .name = LN_##ID##_STR, \ + .pins = { LN_AIF_PINS(1, ID) }, \ + .src_reg = LOCHNAGAR1_##ID##_SEL, \ + .src_mask = LOCHNAGAR1_SRC_MASK, \ + .ctrl_reg = LOCHNAGAR1_##CTRL, \ + .ena_mask = LOCHNAGAR1_##ID##_ENA_MASK, \ + .master_mask = LOCHNAGAR1_##ID##_LRCLK_DIR_MASK | \ + LOCHNAGAR1_##ID##_BCLK_DIR_MASK, \ +} + +#define LN2_AIF(ID) \ +static const struct lochnagar_aif lochnagar2_##ID##_aif = { \ + .name = LN_##ID##_STR, \ + .pins = { LN_AIF_PINS(2, ID) }, \ + .src_reg = LOCHNAGAR2_##ID##_CTRL, \ + .src_mask = LOCHNAGAR2_AIF_SRC_MASK, \ + .ctrl_reg = LOCHNAGAR2_##ID##_CTRL, \ + .ena_mask = LOCHNAGAR2_AIF_ENA_MASK, \ + .master_mask = LOCHNAGAR2_AIF_LRCLK_DIR_MASK | \ + LOCHNAGAR2_AIF_BCLK_DIR_MASK, \ +} + +struct lochnagar_aif { + const char name[16]; + + unsigned int pins[4]; + + unsigned int src_reg; + unsigned int src_mask; + + unsigned int ctrl_reg; + unsigned int ena_mask; + unsigned int master_mask; +}; + +LN1_AIF(CDC_AIF1, CDC_AIF_CTRL1); +LN1_AIF(CDC_AIF2, CDC_AIF_CTRL1); +LN1_AIF(CDC_AIF3, CDC_AIF_CTRL2); +LN1_AIF(DSP_AIF1, DSP_AIF); +LN1_AIF(DSP_AIF2, DSP_AIF); +LN1_AIF(PSIA1, PSIA_AIF); +LN1_AIF(PSIA2, PSIA_AIF); +LN1_AIF(GF_AIF1, GF_AIF1); +LN1_AIF(GF_AIF2, GF_AIF2); +LN1_AIF(GF_AIF3, GF_AIF1); +LN1_AIF(GF_AIF4, GF_AIF2); +LN1_AIF(SPDIF_AIF, EXT_AIF_CTRL); + +LN2_AIF(CDC_AIF1); +LN2_AIF(CDC_AIF2); +LN2_AIF(CDC_AIF3); +LN2_AIF(DSP_AIF1); +LN2_AIF(DSP_AIF2); +LN2_AIF(PSIA1); +LN2_AIF(PSIA2); +LN2_AIF(GF_AIF1); +LN2_AIF(GF_AIF2); +LN2_AIF(GF_AIF3); +LN2_AIF(GF_AIF4); +LN2_AIF(SPDIF_AIF); +LN2_AIF(USB_AIF1); +LN2_AIF(USB_AIF2); +LN2_AIF(ADAT_AIF); +LN2_AIF(SOUNDCARD_AIF); + +#define LN2_OP_AIF 0x00 +#define LN2_OP_GPIO 0xFE + +#define LN_FUNC(NAME, TYPE, OP) \ + { .name = NAME, .type = LN_FTYPE_##TYPE, .op = OP } + +#define LN_FUNC_PIN(REV, ID, OP) \ + LN_FUNC(lochnagar##REV##_##ID##_pin.name, PIN, OP) + +#define LN1_FUNC_PIN(ID, OP) LN_FUNC_PIN(1, ID, OP) +#define LN2_FUNC_PIN(ID, OP) LN_FUNC_PIN(2, ID, OP) + +#define LN_FUNC_AIF(REV, ID, OP) \ + LN_FUNC(lochnagar##REV##_##ID##_aif.name, AIF, OP) + +#define LN1_FUNC_AIF(ID, OP) LN_FUNC_AIF(1, ID, OP) +#define LN2_FUNC_AIF(ID, OP) LN_FUNC_AIF(2, ID, OP) + +#define LN2_FUNC_GAI(ID, OP, BOP, LROP, RXOP, TXOP) \ + LN2_FUNC_AIF(ID, OP), \ + LN_FUNC(lochnagar2_##ID##_BCLK_pin.name, PIN, BOP), \ + LN_FUNC(lochnagar2_##ID##_LRCLK_pin.name, PIN, LROP), \ + LN_FUNC(lochnagar2_##ID##_RXDAT_pin.name, PIN, RXOP), \ + LN_FUNC(lochnagar2_##ID##_TXDAT_pin.name, PIN, TXOP) + +enum lochnagar_func_type { + LN_FTYPE_PIN, + LN_FTYPE_AIF, + LN_FTYPE_COUNT, +}; + +struct lochnagar_func { + const char * const name; + + enum lochnagar_func_type type; + + int op; +}; + +static const struct lochnagar_func lochnagar1_funcs[] = { + LN_FUNC("dsp-gpio1", PIN, 0x01), + LN_FUNC("dsp-gpio2", PIN, 0x02), + LN_FUNC("dsp-gpio3", PIN, 0x03), + LN_FUNC("codec-gpio1", PIN, 0x04), + LN_FUNC("codec-gpio2", PIN, 0x05), + LN_FUNC("codec-gpio3", PIN, 0x06), + LN_FUNC("codec-gpio4", PIN, 0x07), + LN_FUNC("codec-gpio5", PIN, 0x08), + LN_FUNC("codec-gpio6", PIN, 0x09), + LN_FUNC("codec-gpio7", PIN, 0x0A), + LN_FUNC("codec-gpio8", PIN, 0x0B), + LN1_FUNC_PIN(GF_GPIO2, 0x0C), + LN1_FUNC_PIN(GF_GPIO3, 0x0D), + LN1_FUNC_PIN(GF_GPIO7, 0x0E), + + LN1_FUNC_AIF(SPDIF_AIF, 0x01), + LN1_FUNC_AIF(PSIA1, 0x02), + LN1_FUNC_AIF(PSIA2, 0x03), + LN1_FUNC_AIF(CDC_AIF1, 0x04), + LN1_FUNC_AIF(CDC_AIF2, 0x05), + LN1_FUNC_AIF(CDC_AIF3, 0x06), + LN1_FUNC_AIF(DSP_AIF1, 0x07), + LN1_FUNC_AIF(DSP_AIF2, 0x08), + LN1_FUNC_AIF(GF_AIF3, 0x09), + LN1_FUNC_AIF(GF_AIF4, 0x0A), + LN1_FUNC_AIF(GF_AIF1, 0x0B), + LN1_FUNC_AIF(GF_AIF2, 0x0C), +}; + +static const struct lochnagar_func lochnagar2_funcs[] = { + LN_FUNC("aif", PIN, LN2_OP_AIF), + LN2_FUNC_PIN(FPGA_GPIO1, 0x01), + LN2_FUNC_PIN(FPGA_GPIO2, 0x02), + LN2_FUNC_PIN(FPGA_GPIO3, 0x03), + LN2_FUNC_PIN(FPGA_GPIO4, 0x04), + LN2_FUNC_PIN(FPGA_GPIO5, 0x05), + LN2_FUNC_PIN(FPGA_GPIO6, 0x06), + LN2_FUNC_PIN(CDC_GPIO1, 0x07), + LN2_FUNC_PIN(CDC_GPIO2, 0x08), + LN2_FUNC_PIN(CDC_GPIO3, 0x09), + LN2_FUNC_PIN(CDC_GPIO4, 0x0A), + LN2_FUNC_PIN(CDC_GPIO5, 0x0B), + LN2_FUNC_PIN(CDC_GPIO6, 0x0C), + LN2_FUNC_PIN(CDC_GPIO7, 0x0D), + LN2_FUNC_PIN(CDC_GPIO8, 0x0E), + LN2_FUNC_PIN(DSP_GPIO1, 0x0F), + LN2_FUNC_PIN(DSP_GPIO2, 0x10), + LN2_FUNC_PIN(DSP_GPIO3, 0x11), + LN2_FUNC_PIN(DSP_GPIO4, 0x12), + LN2_FUNC_PIN(DSP_GPIO5, 0x13), + LN2_FUNC_PIN(DSP_GPIO6, 0x14), + LN2_FUNC_PIN(GF_GPIO2, 0x15), + LN2_FUNC_PIN(GF_GPIO3, 0x16), + LN2_FUNC_PIN(GF_GPIO7, 0x17), + LN2_FUNC_PIN(GF_GPIO1, 0x18), + LN2_FUNC_PIN(GF_GPIO5, 0x19), + LN2_FUNC_PIN(DSP_GPIO20, 0x1A), + LN_FUNC("codec-clkout", PIN, 0x20), + LN_FUNC("dsp-clkout", PIN, 0x21), + LN_FUNC("pmic-32k", PIN, 0x22), + LN_FUNC("spdif-clkout", PIN, 0x23), + LN_FUNC("clk-12m288", PIN, 0x24), + LN_FUNC("clk-11m2986", PIN, 0x25), + LN_FUNC("clk-24m576", PIN, 0x26), + LN_FUNC("clk-22m5792", PIN, 0x27), + LN_FUNC("xmos-mclk", PIN, 0x29), + LN_FUNC("gf-clkout1", PIN, 0x2A), + LN_FUNC("gf-mclk1", PIN, 0x2B), + LN_FUNC("gf-mclk3", PIN, 0x2C), + LN_FUNC("gf-mclk2", PIN, 0x2D), + LN_FUNC("gf-clkout2", PIN, 0x2E), + LN2_FUNC_PIN(CDC_MCLK1, 0x2F), + LN2_FUNC_PIN(CDC_MCLK2, 0x30), + LN2_FUNC_PIN(DSP_CLKIN, 0x31), + LN2_FUNC_PIN(PSIA1_MCLK, 0x32), + LN2_FUNC_PIN(PSIA2_MCLK, 0x33), + LN_FUNC("spdif-mclk", PIN, 0x34), + LN_FUNC("codec-irq", PIN, 0x42), + LN2_FUNC_PIN(CDC_RESET, 0x43), + LN2_FUNC_PIN(DSP_RESET, 0x44), + LN_FUNC("dsp-irq", PIN, 0x45), + LN2_FUNC_PIN(DSP_STANDBY, 0x46), + LN2_FUNC_PIN(CDC_PDMCLK1, 0x90), + LN2_FUNC_PIN(CDC_PDMDAT1, 0x91), + LN2_FUNC_PIN(CDC_PDMCLK2, 0x92), + LN2_FUNC_PIN(CDC_PDMDAT2, 0x93), + LN2_FUNC_PIN(CDC_DMICCLK1, 0xA0), + LN2_FUNC_PIN(CDC_DMICDAT1, 0xA1), + LN2_FUNC_PIN(CDC_DMICCLK2, 0xA2), + LN2_FUNC_PIN(CDC_DMICDAT2, 0xA3), + LN2_FUNC_PIN(CDC_DMICCLK3, 0xA4), + LN2_FUNC_PIN(CDC_DMICDAT3, 0xA5), + LN2_FUNC_PIN(CDC_DMICCLK4, 0xA6), + LN2_FUNC_PIN(CDC_DMICDAT4, 0xA7), + LN2_FUNC_PIN(DSP_DMICCLK1, 0xA8), + LN2_FUNC_PIN(DSP_DMICDAT1, 0xA9), + LN2_FUNC_PIN(DSP_DMICCLK2, 0xAA), + LN2_FUNC_PIN(DSP_DMICDAT2, 0xAB), + LN2_FUNC_PIN(DSP_UART1_RX, 0xC0), + LN2_FUNC_PIN(DSP_UART1_TX, 0xC1), + LN2_FUNC_PIN(DSP_UART2_RX, 0xC2), + LN2_FUNC_PIN(DSP_UART2_TX, 0xC3), + LN2_FUNC_PIN(GF_UART2_RX, 0xC4), + LN2_FUNC_PIN(GF_UART2_TX, 0xC5), + LN2_FUNC_PIN(USB_UART_RX, 0xC6), + LN_FUNC("usb-uart-tx", PIN, 0xC7), + LN2_FUNC_PIN(I2C2_SCL, 0xE0), + LN2_FUNC_PIN(I2C2_SDA, 0xE1), + LN2_FUNC_PIN(I2C3_SCL, 0xE2), + LN2_FUNC_PIN(I2C3_SDA, 0xE3), + LN2_FUNC_PIN(I2C4_SCL, 0xE4), + LN2_FUNC_PIN(I2C4_SDA, 0xE5), + LN_FUNC("gpio", PIN, LN2_OP_GPIO), + + LN2_FUNC_AIF(SPDIF_AIF, 0x01), + LN2_FUNC_GAI(PSIA1, 0x02, 0x50, 0x51, 0x52, 0x53), + LN2_FUNC_GAI(PSIA2, 0x03, 0x54, 0x55, 0x56, 0x57), + LN2_FUNC_GAI(CDC_AIF1, 0x04, 0x59, 0x5B, 0x5A, 0x58), + LN2_FUNC_GAI(CDC_AIF2, 0x05, 0x5D, 0x5F, 0x5E, 0x5C), + LN2_FUNC_GAI(CDC_AIF3, 0x06, 0x61, 0x62, 0x63, 0x60), + LN2_FUNC_GAI(DSP_AIF1, 0x07, 0x65, 0x67, 0x66, 0x64), + LN2_FUNC_GAI(DSP_AIF2, 0x08, 0x69, 0x6B, 0x6A, 0x68), + LN2_FUNC_GAI(GF_AIF3, 0x09, 0x6D, 0x6F, 0x6C, 0x6E), + LN2_FUNC_GAI(GF_AIF4, 0x0A, 0x71, 0x73, 0x70, 0x72), + LN2_FUNC_GAI(GF_AIF1, 0x0B, 0x75, 0x77, 0x74, 0x76), + LN2_FUNC_GAI(GF_AIF2, 0x0C, 0x79, 0x7B, 0x78, 0x7A), + LN2_FUNC_AIF(USB_AIF1, 0x0D), + LN2_FUNC_AIF(USB_AIF2, 0x0E), + LN2_FUNC_AIF(ADAT_AIF, 0x0F), + LN2_FUNC_AIF(SOUNDCARD_AIF, 0x10), +}; + +#define LN_GROUP_PIN(REV, ID) { \ + .name = lochnagar##REV##_##ID##_pin.name, \ + .type = LN_FTYPE_PIN, \ + .pins = &lochnagar##REV##_pins[LOCHNAGAR##REV##_PIN_##ID].number, \ + .npins = 1, \ + .priv = &lochnagar##REV##_pins[LOCHNAGAR##REV##_PIN_##ID], \ +} + +#define LN_GROUP_AIF(REV, ID) { \ + .name = lochnagar##REV##_##ID##_aif.name, \ + .type = LN_FTYPE_AIF, \ + .pins = lochnagar##REV##_##ID##_aif.pins, \ + .npins = ARRAY_SIZE(lochnagar##REV##_##ID##_aif.pins), \ + .priv = &lochnagar##REV##_##ID##_aif, \ +} + +#define LN1_GROUP_PIN(ID) LN_GROUP_PIN(1, ID) +#define LN2_GROUP_PIN(ID) LN_GROUP_PIN(2, ID) + +#define LN1_GROUP_AIF(ID) LN_GROUP_AIF(1, ID) +#define LN2_GROUP_AIF(ID) LN_GROUP_AIF(2, ID) + +#define LN2_GROUP_GAI(ID) \ + LN2_GROUP_AIF(ID), \ + LN2_GROUP_PIN(ID##_BCLK), LN2_GROUP_PIN(ID##_LRCLK), \ + LN2_GROUP_PIN(ID##_RXDAT), LN2_GROUP_PIN(ID##_TXDAT) + +struct lochnagar_group { + const char * const name; + + enum lochnagar_func_type type; + + const unsigned int *pins; + int npins; + + const void *priv; +}; + +static const struct lochnagar_group lochnagar1_groups[] = { + LN1_GROUP_PIN(GF_GPIO2), LN1_GROUP_PIN(GF_GPIO3), + LN1_GROUP_PIN(GF_GPIO7), + LN1_GROUP_PIN(LED1), LN1_GROUP_PIN(LED2), + LN1_GROUP_AIF(CDC_AIF1), LN1_GROUP_AIF(CDC_AIF2), + LN1_GROUP_AIF(CDC_AIF3), + LN1_GROUP_AIF(DSP_AIF1), LN1_GROUP_AIF(DSP_AIF2), + LN1_GROUP_AIF(PSIA1), LN1_GROUP_AIF(PSIA2), + LN1_GROUP_AIF(GF_AIF1), LN1_GROUP_AIF(GF_AIF2), + LN1_GROUP_AIF(GF_AIF3), LN1_GROUP_AIF(GF_AIF4), + LN1_GROUP_AIF(SPDIF_AIF), +}; + +static const struct lochnagar_group lochnagar2_groups[] = { + LN2_GROUP_PIN(FPGA_GPIO1), LN2_GROUP_PIN(FPGA_GPIO2), + LN2_GROUP_PIN(FPGA_GPIO3), LN2_GROUP_PIN(FPGA_GPIO4), + LN2_GROUP_PIN(FPGA_GPIO5), LN2_GROUP_PIN(FPGA_GPIO6), + LN2_GROUP_PIN(CDC_GPIO1), LN2_GROUP_PIN(CDC_GPIO2), + LN2_GROUP_PIN(CDC_GPIO3), LN2_GROUP_PIN(CDC_GPIO4), + LN2_GROUP_PIN(CDC_GPIO5), LN2_GROUP_PIN(CDC_GPIO6), + LN2_GROUP_PIN(CDC_GPIO7), LN2_GROUP_PIN(CDC_GPIO8), + LN2_GROUP_PIN(DSP_GPIO1), LN2_GROUP_PIN(DSP_GPIO2), + LN2_GROUP_PIN(DSP_GPIO3), LN2_GROUP_PIN(DSP_GPIO4), + LN2_GROUP_PIN(DSP_GPIO5), LN2_GROUP_PIN(DSP_GPIO6), + LN2_GROUP_PIN(DSP_GPIO20), + LN2_GROUP_PIN(GF_GPIO1), + LN2_GROUP_PIN(GF_GPIO2), LN2_GROUP_PIN(GF_GPIO5), + LN2_GROUP_PIN(GF_GPIO3), LN2_GROUP_PIN(GF_GPIO7), + LN2_GROUP_PIN(DSP_UART1_RX), LN2_GROUP_PIN(DSP_UART1_TX), + LN2_GROUP_PIN(DSP_UART2_RX), LN2_GROUP_PIN(DSP_UART2_TX), + LN2_GROUP_PIN(GF_UART2_RX), LN2_GROUP_PIN(GF_UART2_TX), + LN2_GROUP_PIN(USB_UART_RX), + LN2_GROUP_PIN(CDC_PDMCLK1), LN2_GROUP_PIN(CDC_PDMDAT1), + LN2_GROUP_PIN(CDC_PDMCLK2), LN2_GROUP_PIN(CDC_PDMDAT2), + LN2_GROUP_PIN(CDC_DMICCLK1), LN2_GROUP_PIN(CDC_DMICDAT1), + LN2_GROUP_PIN(CDC_DMICCLK2), LN2_GROUP_PIN(CDC_DMICDAT2), + LN2_GROUP_PIN(CDC_DMICCLK3), LN2_GROUP_PIN(CDC_DMICDAT3), + LN2_GROUP_PIN(CDC_DMICCLK4), LN2_GROUP_PIN(CDC_DMICDAT4), + LN2_GROUP_PIN(DSP_DMICCLK1), LN2_GROUP_PIN(DSP_DMICDAT1), + LN2_GROUP_PIN(DSP_DMICCLK2), LN2_GROUP_PIN(DSP_DMICDAT2), + LN2_GROUP_PIN(I2C2_SCL), LN2_GROUP_PIN(I2C2_SDA), + LN2_GROUP_PIN(I2C3_SCL), LN2_GROUP_PIN(I2C3_SDA), + LN2_GROUP_PIN(I2C4_SCL), LN2_GROUP_PIN(I2C4_SDA), + LN2_GROUP_PIN(DSP_STANDBY), + LN2_GROUP_PIN(CDC_MCLK1), LN2_GROUP_PIN(CDC_MCLK2), + LN2_GROUP_PIN(DSP_CLKIN), + LN2_GROUP_PIN(PSIA1_MCLK), LN2_GROUP_PIN(PSIA2_MCLK), + LN2_GROUP_GAI(CDC_AIF1), LN2_GROUP_GAI(CDC_AIF2), + LN2_GROUP_GAI(CDC_AIF3), + LN2_GROUP_GAI(DSP_AIF1), LN2_GROUP_GAI(DSP_AIF2), + LN2_GROUP_GAI(PSIA1), LN2_GROUP_GAI(PSIA2), + LN2_GROUP_GAI(GF_AIF1), LN2_GROUP_GAI(GF_AIF2), + LN2_GROUP_GAI(GF_AIF3), LN2_GROUP_GAI(GF_AIF4), + LN2_GROUP_AIF(SPDIF_AIF), + LN2_GROUP_AIF(USB_AIF1), LN2_GROUP_AIF(USB_AIF2), + LN2_GROUP_AIF(ADAT_AIF), + LN2_GROUP_AIF(SOUNDCARD_AIF), +}; + +struct lochnagar_func_groups { + const char **groups; + int ngroups; +}; + +struct lochnagar_pin_priv { + struct lochnagar *lochnagar; + struct device *dev; + + const struct lochnagar_func *funcs; + int nfuncs; + + const struct pinctrl_pin_desc *pins; + int npins; + + const struct lochnagar_group *groups; + int ngroups; + + struct lochnagar_func_groups func_groups[LN_FTYPE_COUNT]; + + struct gpio_chip gpio_chip; +}; + +static int lochnagar_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + + return priv->ngroups; +} + +static const char *lochnagar_get_group_name(struct pinctrl_dev *pctldev, + unsigned int group_idx) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + + return priv->groups[group_idx].name; +} + +static int lochnagar_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int group_idx, + const unsigned int **pins, + unsigned int *num_pins) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + + *pins = priv->groups[group_idx].pins; + *num_pins = priv->groups[group_idx].npins; + + return 0; +} + +static const struct pinctrl_ops lochnagar_pin_group_ops = { + .get_groups_count = lochnagar_get_groups_count, + .get_group_name = lochnagar_get_group_name, + .get_group_pins = lochnagar_get_group_pins, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinctrl_utils_free_map, +}; + +static int lochnagar_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + + return priv->nfuncs; +} + +static const char *lochnagar_get_func_name(struct pinctrl_dev *pctldev, + unsigned int func_idx) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + + return priv->funcs[func_idx].name; +} + +static int lochnagar_get_func_groups(struct pinctrl_dev *pctldev, + unsigned int func_idx, + const char * const **groups, + unsigned int * const num_groups) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + int func_type; + + func_type = priv->funcs[func_idx].type; + + *groups = priv->func_groups[func_type].groups; + *num_groups = priv->func_groups[func_type].ngroups; + + return 0; +} + +static int lochnagar2_get_gpio_chan(struct lochnagar_pin_priv *priv, + unsigned int op) +{ + struct regmap *regmap = priv->lochnagar->regmap; + unsigned int val; + int free = -1; + int i, ret; + + for (i = 0; i < LN2_NUM_GPIO_CHANNELS; i++) { + ret = regmap_read(regmap, LOCHNAGAR2_GPIO_CHANNEL1 + i, &val); + if (ret) + return ret; + + val &= LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK; + + if (val == op) + return i + 1; + + if (free < 0 && !val) + free = i; + } + + if (free >= 0) { + ret = regmap_update_bits(regmap, + LOCHNAGAR2_GPIO_CHANNEL1 + free, + LOCHNAGAR2_GPIO_CHANNEL_SRC_MASK, op); + if (ret) + return ret; + + free++; + + dev_dbg(priv->dev, "Set channel %d to 0x%x\n", free, op); + + return free; + } + + return -ENOSPC; +} + +static int lochnagar_pin_set_mux(struct lochnagar_pin_priv *priv, + const struct lochnagar_pin *pin, + unsigned int op) +{ + int ret; + + switch (priv->lochnagar->type) { + case LOCHNAGAR1: + break; + default: + ret = lochnagar2_get_gpio_chan(priv, op); + if (ret < 0) { + dev_err(priv->dev, "Failed to get channel for %s: %d\n", + pin->name, ret); + return ret; + } + + op = ret; + break; + } + + dev_dbg(priv->dev, "Set pin %s to 0x%x\n", pin->name, op); + + ret = regmap_write(priv->lochnagar->regmap, pin->reg, op); + if (ret) + dev_err(priv->dev, "Failed to set %s mux: %d\n", + pin->name, ret); + + return 0; +} + +static int lochnagar_aif_set_mux(struct lochnagar_pin_priv *priv, + const struct lochnagar_group *group, + unsigned int op) +{ + struct regmap *regmap = priv->lochnagar->regmap; + const struct lochnagar_aif *aif = group->priv; + const struct lochnagar_pin *pin; + int i, ret; + + ret = regmap_update_bits(regmap, aif->src_reg, aif->src_mask, op); + if (ret) { + dev_err(priv->dev, "Failed to set %s source: %d\n", + group->name, ret); + return ret; + } + + ret = regmap_update_bits(regmap, aif->ctrl_reg, + aif->ena_mask, aif->ena_mask); + if (ret) { + dev_err(priv->dev, "Failed to set %s enable: %d\n", + group->name, ret); + return ret; + } + + for (i = 0; i < group->npins; i++) { + pin = priv->pins[group->pins[i]].drv_data; + + if (pin->type != LN_PTYPE_MUX) + continue; + + dev_dbg(priv->dev, "Set pin %s to AIF\n", pin->name); + + ret = regmap_update_bits(regmap, pin->reg, + LOCHNAGAR2_GPIO_SRC_MASK, + LN2_OP_AIF); + if (ret) { + dev_err(priv->dev, "Failed to set %s to AIF: %d\n", + pin->name, ret); + return ret; + } + } + + return 0; +} + +static int lochnagar_set_mux(struct pinctrl_dev *pctldev, + unsigned int func_idx, unsigned int group_idx) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + const struct lochnagar_func *func = &priv->funcs[func_idx]; + const struct lochnagar_group *group = &priv->groups[group_idx]; + const struct lochnagar_pin *pin; + + switch (func->type) { + case LN_FTYPE_AIF: + dev_dbg(priv->dev, "Set group %s to %s\n", + group->name, func->name); + + return lochnagar_aif_set_mux(priv, group, func->op); + case LN_FTYPE_PIN: + pin = priv->pins[*group->pins].drv_data; + + dev_dbg(priv->dev, "Set pin %s to %s\n", pin->name, func->name); + + return lochnagar_pin_set_mux(priv, pin, func->op); + default: + return -EINVAL; + } +} + +static int lochnagar_gpio_request(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + struct lochnagar *lochnagar = priv->lochnagar; + const struct lochnagar_pin *pin = priv->pins[offset].drv_data; + int ret; + + dev_dbg(priv->dev, "Requesting GPIO %s\n", pin->name); + + if (lochnagar->type == LOCHNAGAR1 || pin->type != LN_PTYPE_MUX) + return 0; + + ret = lochnagar2_get_gpio_chan(priv, LN2_OP_GPIO); + if (ret < 0) { + dev_err(priv->dev, "Failed to get low channel: %d\n", ret); + return ret; + } + + ret = lochnagar2_get_gpio_chan(priv, LN2_OP_GPIO | 0x1); + if (ret < 0) { + dev_err(priv->dev, "Failed to get high channel: %d\n", ret); + return ret; + } + + return 0; +} + +static int lochnagar_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned int offset, + bool input) +{ + /* The GPIOs only support output */ + if (input) + return -EINVAL; + + return 0; +} + +static const struct pinmux_ops lochnagar_pin_mux_ops = { + .get_functions_count = lochnagar_get_funcs_count, + .get_function_name = lochnagar_get_func_name, + .get_function_groups = lochnagar_get_func_groups, + .set_mux = lochnagar_set_mux, + + .gpio_request_enable = lochnagar_gpio_request, + .gpio_set_direction = lochnagar_gpio_set_direction, + + .strict = true, +}; + +enum lochnagar_pin_config_param { + PIN_CONFIG_AIFMASTER = PIN_CONFIG_END + 1, + PIN_CONFIG_AIFSLAVE, +}; + +static int lochnagar_aif_set_master(struct lochnagar_pin_priv *priv, + unsigned int group_idx, bool master) +{ + struct regmap *regmap = priv->lochnagar->regmap; + const struct lochnagar_group *group = &priv->groups[group_idx]; + const struct lochnagar_aif *aif = group->priv; + unsigned int val = 0; + int ret; + + if (group->type != LN_FTYPE_AIF) + return -EINVAL; + + if (!master) + val = aif->master_mask; + + dev_dbg(priv->dev, "Set AIF %s to %s\n", + group->name, master ? "master" : "slave"); + + ret = regmap_update_bits(regmap, aif->ctrl_reg, aif->master_mask, val); + if (ret) { + dev_err(priv->dev, "Failed to set %s mode: %d\n", + group->name, ret); + return ret; + } + + return 0; +} + +static int lochnagar_conf_group_set(struct pinctrl_dev *pctldev, + unsigned int group_idx, + unsigned long *configs, + unsigned int num_configs) +{ + struct lochnagar_pin_priv *priv = pinctrl_dev_get_drvdata(pctldev); + int i, ret; + + for (i = 0; i < num_configs; i++) { + unsigned int param = pinconf_to_config_param(*configs); + + switch (param) { + case PIN_CONFIG_AIFMASTER: + ret = lochnagar_aif_set_master(priv, group_idx, true); + if (ret) + return ret; + break; + case PIN_CONFIG_AIFSLAVE: + ret = lochnagar_aif_set_master(priv, group_idx, false); + if (ret) + return ret; + break; + default: + return -ENOTSUPP; + } + + configs++; + } + + return 0; +} + +static const struct pinconf_ops lochnagar_pin_conf_ops = { + .pin_config_group_set = lochnagar_conf_group_set, +}; + +static const struct pinconf_generic_params lochnagar_dt_params[] = { + {"aif-master", PIN_CONFIG_AIFMASTER, 0}, + {"aif-slave", PIN_CONFIG_AIFSLAVE, 0}, +}; + +static const struct pin_config_item lochnagar_conf_items[] = { + PCONFDUMP(PIN_CONFIG_AIFMASTER, "aif-master", NULL, false), + PCONFDUMP(PIN_CONFIG_AIFSLAVE, "aif-slave", NULL, false), +}; + +static const struct pinctrl_desc lochnagar_pin_desc = { + .name = "lochnagar-pinctrl", + .owner = THIS_MODULE, + + .pctlops = &lochnagar_pin_group_ops, + .pmxops = &lochnagar_pin_mux_ops, + .confops = &lochnagar_pin_conf_ops, + + .custom_params = lochnagar_dt_params, + .num_custom_params = ARRAY_SIZE(lochnagar_dt_params), + .custom_conf_items = lochnagar_conf_items, +}; + +static void lochnagar_gpio_set(struct gpio_chip *chip, + unsigned int offset, int value) +{ + struct lochnagar_pin_priv *priv = gpiochip_get_data(chip); + struct lochnagar *lochnagar = priv->lochnagar; + const struct lochnagar_pin *pin = priv->pins[offset].drv_data; + int ret; + + value = !!value; + + dev_dbg(priv->dev, "Set GPIO %s to %s\n", + pin->name, value ? "high" : "low"); + + switch (pin->type) { + case LN_PTYPE_MUX: + value |= LN2_OP_GPIO; + + ret = lochnagar_pin_set_mux(priv, pin, value); + break; + case LN_PTYPE_GPIO: + if (pin->invert) + value = !value; + + ret = regmap_update_bits(lochnagar->regmap, pin->reg, + 0x1 << pin->shift, + value << pin->shift); + break; + default: + ret = -EINVAL; + break; + } + + if (ret < 0) + dev_err(chip->parent, "Failed to set %s value: %d\n", + pin->name, ret); +} + +static int lochnagar_gpio_direction_out(struct gpio_chip *chip, + unsigned int offset, int value) +{ + lochnagar_gpio_set(chip, offset, value); + + return pinctrl_gpio_direction_output(chip->base + offset); +} + +static const struct gpio_chip template_chip = { + .label = "lochnagar-gpio", + + .request = gpiochip_generic_request, + .free = gpiochip_generic_free, + .direction_output = lochnagar_gpio_direction_out, + .set = lochnagar_gpio_set, + + .can_sleep = true, +}; + +static int lochnagar_fill_func_groups(struct lochnagar_pin_priv *priv) +{ + struct lochnagar_func_groups *funcs; + int i; + + for (i = 0; i < priv->ngroups; i++) + priv->func_groups[priv->groups[i].type].ngroups++; + + for (i = 0; i < LN_FTYPE_COUNT; i++) { + funcs = &priv->func_groups[i]; + + if (!funcs->ngroups) + continue; + + funcs->groups = devm_kcalloc(priv->dev, funcs->ngroups, + sizeof(*funcs->groups), + GFP_KERNEL); + if (!funcs->groups) + return -ENOMEM; + + funcs->ngroups = 0; + } + + for (i = 0; i < priv->ngroups; i++) { + funcs = &priv->func_groups[priv->groups[i].type]; + + funcs->groups[funcs->ngroups++] = priv->groups[i].name; + } + + return 0; +} + +static int lochnagar_pin_probe(struct platform_device *pdev) +{ + struct lochnagar *lochnagar = dev_get_drvdata(pdev->dev.parent); + struct lochnagar_pin_priv *priv; + struct pinctrl_desc *desc; + struct pinctrl_dev *pctl; + struct device *dev = &pdev->dev; + int ret; + + dev->of_node = lochnagar->dev->of_node; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->lochnagar = lochnagar; + + desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); + if (!desc) + return -ENOMEM; + + *desc = lochnagar_pin_desc; + + priv->gpio_chip = template_chip; + priv->gpio_chip.parent = dev; + priv->gpio_chip.base = -1; +#ifdef CONFIG_OF_GPIO + priv->gpio_chip.of_node = lochnagar->dev->of_node; +#endif + + switch (lochnagar->type) { + case LOCHNAGAR1: + priv->funcs = lochnagar1_funcs; + priv->nfuncs = ARRAY_SIZE(lochnagar1_funcs); + priv->pins = lochnagar1_pins; + priv->npins = ARRAY_SIZE(lochnagar1_pins); + priv->groups = lochnagar1_groups; + priv->ngroups = ARRAY_SIZE(lochnagar1_groups); + + priv->gpio_chip.ngpio = LOCHNAGAR1_PIN_NUM_GPIOS; + break; + case LOCHNAGAR2: + priv->funcs = lochnagar2_funcs; + priv->nfuncs = ARRAY_SIZE(lochnagar2_funcs); + priv->pins = lochnagar2_pins; + priv->npins = ARRAY_SIZE(lochnagar2_pins); + priv->groups = lochnagar2_groups; + priv->ngroups = ARRAY_SIZE(lochnagar2_groups); + + priv->gpio_chip.ngpio = LOCHNAGAR2_PIN_NUM_GPIOS; + break; + default: + dev_err(dev, "Unknown Lochnagar type: %d\n", lochnagar->type); + return -EINVAL; + } + + ret = lochnagar_fill_func_groups(priv); + if (ret < 0) + return ret; + + desc->pins = priv->pins; + desc->npins = priv->npins; + + pctl = devm_pinctrl_register(dev, desc, priv); + if (IS_ERR(pctl)) { + ret = PTR_ERR(pctl); + dev_err(priv->dev, "Failed to register pinctrl: %d\n", ret); + return ret; + } + + ret = devm_gpiochip_add_data(dev, &priv->gpio_chip, priv); + if (ret < 0) { + dev_err(&pdev->dev, "Failed to register gpiochip: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver lochnagar_pin_driver = { + .driver = { + .name = "lochnagar-pinctrl", + }, + + .probe = lochnagar_pin_probe, +}; +module_platform_driver(lochnagar_pin_driver); + +MODULE_AUTHOR("Charles Keepax "); +MODULE_DESCRIPTION("Pinctrl driver for Cirrus Logic Lochnagar Board"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:lochnagar-pinctrl"); diff --git a/drivers/pinctrl/cirrus/pinctrl-lochnagar.h b/drivers/pinctrl/cirrus/pinctrl-lochnagar.h new file mode 100644 index 0000000000000..325ce356a300a --- /dev/null +++ b/drivers/pinctrl/cirrus/pinctrl-lochnagar.h @@ -0,0 +1,112 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Defines for Lochnagar pinctrl + * + * Copyright (c) 2018 Cirrus Logic, Inc. and + * Cirrus Logic International Semiconductor Ltd. + * + * Author: Charles Keepax + */ + +#include + +#ifndef PINCTRL_LOCHNAGAR_H +#define PINCTRL_LOCHNAGAR_H + +#define LN2_NUM_GPIO_CHANNELS 16 + +#define LN_CDC_AIF1_STR "codec-aif1" +#define LN_CDC_AIF2_STR "codec-aif2" +#define LN_CDC_AIF3_STR "codec-aif3" +#define LN_DSP_AIF1_STR "dsp-aif1" +#define LN_DSP_AIF2_STR "dsp-aif2" +#define LN_PSIA1_STR "psia1" +#define LN_PSIA2_STR "psia2" +#define LN_GF_AIF1_STR "gf-aif1" +#define LN_GF_AIF2_STR "gf-aif2" +#define LN_GF_AIF3_STR "gf-aif3" +#define LN_GF_AIF4_STR "gf-aif4" +#define LN_SPDIF_AIF_STR "spdif-aif" +#define LN_USB_AIF1_STR "usb-aif1" +#define LN_USB_AIF2_STR "usb-aif2" +#define LN_ADAT_AIF_STR "adat-aif" +#define LN_SOUNDCARD_AIF_STR "soundcard-aif" + +enum { + LOCHNAGAR1_PIN_GF_GPIO2 = LOCHNAGAR1_PIN_NUM_GPIOS, + LOCHNAGAR1_PIN_GF_GPIO3, + LOCHNAGAR1_PIN_GF_GPIO7, + LOCHNAGAR1_PIN_LED1, + LOCHNAGAR1_PIN_LED2, + LOCHNAGAR1_PIN_CDC_AIF1_BCLK, + LOCHNAGAR1_PIN_CDC_AIF1_LRCLK, + LOCHNAGAR1_PIN_CDC_AIF1_RXDAT, + LOCHNAGAR1_PIN_CDC_AIF1_TXDAT, + LOCHNAGAR1_PIN_CDC_AIF2_BCLK, + LOCHNAGAR1_PIN_CDC_AIF2_LRCLK, + LOCHNAGAR1_PIN_CDC_AIF2_RXDAT, + LOCHNAGAR1_PIN_CDC_AIF2_TXDAT, + LOCHNAGAR1_PIN_CDC_AIF3_BCLK, + LOCHNAGAR1_PIN_CDC_AIF3_LRCLK, + LOCHNAGAR1_PIN_CDC_AIF3_RXDAT, + LOCHNAGAR1_PIN_CDC_AIF3_TXDAT, + LOCHNAGAR1_PIN_DSP_AIF1_BCLK, + LOCHNAGAR1_PIN_DSP_AIF1_LRCLK, + LOCHNAGAR1_PIN_DSP_AIF1_RXDAT, + LOCHNAGAR1_PIN_DSP_AIF1_TXDAT, + LOCHNAGAR1_PIN_DSP_AIF2_BCLK, + LOCHNAGAR1_PIN_DSP_AIF2_LRCLK, + LOCHNAGAR1_PIN_DSP_AIF2_RXDAT, + LOCHNAGAR1_PIN_DSP_AIF2_TXDAT, + LOCHNAGAR1_PIN_PSIA1_BCLK, + LOCHNAGAR1_PIN_PSIA1_LRCLK, + LOCHNAGAR1_PIN_PSIA1_RXDAT, + LOCHNAGAR1_PIN_PSIA1_TXDAT, + LOCHNAGAR1_PIN_PSIA2_BCLK, + LOCHNAGAR1_PIN_PSIA2_LRCLK, + LOCHNAGAR1_PIN_PSIA2_RXDAT, + LOCHNAGAR1_PIN_PSIA2_TXDAT, + LOCHNAGAR1_PIN_SPDIF_AIF_BCLK, + LOCHNAGAR1_PIN_SPDIF_AIF_LRCLK, + LOCHNAGAR1_PIN_SPDIF_AIF_RXDAT, + LOCHNAGAR1_PIN_SPDIF_AIF_TXDAT, + LOCHNAGAR1_PIN_GF_AIF3_BCLK, + LOCHNAGAR1_PIN_GF_AIF3_RXDAT, + LOCHNAGAR1_PIN_GF_AIF3_LRCLK, + LOCHNAGAR1_PIN_GF_AIF3_TXDAT, + LOCHNAGAR1_PIN_GF_AIF4_BCLK, + LOCHNAGAR1_PIN_GF_AIF4_RXDAT, + LOCHNAGAR1_PIN_GF_AIF4_LRCLK, + LOCHNAGAR1_PIN_GF_AIF4_TXDAT, + LOCHNAGAR1_PIN_GF_AIF1_BCLK, + LOCHNAGAR1_PIN_GF_AIF1_RXDAT, + LOCHNAGAR1_PIN_GF_AIF1_LRCLK, + LOCHNAGAR1_PIN_GF_AIF1_TXDAT, + LOCHNAGAR1_PIN_GF_AIF2_BCLK, + LOCHNAGAR1_PIN_GF_AIF2_RXDAT, + LOCHNAGAR1_PIN_GF_AIF2_LRCLK, + LOCHNAGAR1_PIN_GF_AIF2_TXDAT, + + LOCHNAGAR2_PIN_SPDIF_AIF_BCLK = LOCHNAGAR2_PIN_NUM_GPIOS, + LOCHNAGAR2_PIN_SPDIF_AIF_LRCLK, + LOCHNAGAR2_PIN_SPDIF_AIF_RXDAT, + LOCHNAGAR2_PIN_SPDIF_AIF_TXDAT, + LOCHNAGAR2_PIN_USB_AIF1_BCLK, + LOCHNAGAR2_PIN_USB_AIF1_LRCLK, + LOCHNAGAR2_PIN_USB_AIF1_RXDAT, + LOCHNAGAR2_PIN_USB_AIF1_TXDAT, + LOCHNAGAR2_PIN_USB_AIF2_BCLK, + LOCHNAGAR2_PIN_USB_AIF2_LRCLK, + LOCHNAGAR2_PIN_USB_AIF2_RXDAT, + LOCHNAGAR2_PIN_USB_AIF2_TXDAT, + LOCHNAGAR2_PIN_ADAT_AIF_BCLK, + LOCHNAGAR2_PIN_ADAT_AIF_LRCLK, + LOCHNAGAR2_PIN_ADAT_AIF_RXDAT, + LOCHNAGAR2_PIN_ADAT_AIF_TXDAT, + LOCHNAGAR2_PIN_SOUNDCARD_AIF_BCLK, + LOCHNAGAR2_PIN_SOUNDCARD_AIF_LRCLK, + LOCHNAGAR2_PIN_SOUNDCARD_AIF_RXDAT, + LOCHNAGAR2_PIN_SOUNDCARD_AIF_TXDAT, +}; + +#endif -- 2.11.0