From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1755279AbdBNRue (ORCPT ); Tue, 14 Feb 2017 12:50:34 -0500 Received: from mail-cys01nam02on0075.outbound.protection.outlook.com ([104.47.37.75]:26048 "EHLO NAM02-CY1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1754718AbdBNRuE (ORCPT ); Tue, 14 Feb 2017 12:50:04 -0500 Authentication-Results: spf=none (sender IP is ) smtp.mailfrom=David.Daney@cavium.com; From: David Daney To: Linus Walleij , Alexandre Courbot , Rob Herring , Mark Rutland , Marc Zyngier , Thomas Gleixner , linux-gpio@vger.kernel.org, devicetree@vger.kernel.org Cc: linux-kernel@vger.kernel.org, David Daney Subject: [PATCH v3 5/6] gpio: Add gpio driver support for ThunderX and OCTEON-TX Date: Tue, 14 Feb 2017 09:49:35 -0800 Message-Id: <1487094576-229208-6-git-send-email-david.daney@cavium.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1487094576-229208-1-git-send-email-david.daney@cavium.com> References: <1487094576-229208-1-git-send-email-david.daney@cavium.com> MIME-Version: 1.0 Content-Type: text/plain X-Originating-IP: [50.233.148.156] X-ClientProxiedBy: SN1PR0701CA0033.namprd07.prod.outlook.com (10.162.96.43) To SN1PR0701MB1982.namprd07.prod.outlook.com (10.163.131.21) X-MS-Office365-Filtering-Correlation-Id: 3aa51045-a255-4171-7b16-08d45501e096 X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:(22001);SRVR:SN1PR0701MB1982; X-Microsoft-Exchange-Diagnostics: 1;SN1PR0701MB1982;3:zTAPaq5py/VeHI8XqEiJcVHvJ5wAGyEBAeKW5OSzKF+MHYWnE6V9+JQ06hv7ykg8P0jOq9/F7VNOett1Zu+1E+BJafp1FiIwYOZjZ34b27ikEOXxDIuLvCTxY+FLYyhw4g5sotvTeW/65aNODS4hyTnJTNcZt39YqxW2bsgMoKufUGHm57PPZXkkf+QFHaRIZbNaWAyceWFJ/5rVY30JtOntUa/iKLbn8/GiagaGlmqmZ46DTYy3Kxo8GgKhlEu7PjszsCyKXwatyQrycA5+Ug==;25:MtFeySiLRUP1oZbINlgPdNvZrXMAXn5QijJHZI3W0X8dnlU7lXcA9toEoQgzMMcS0sSx7dQG3EyzL0ahtl2so4/DpZPm2LtWwQkAQBGu6OON6Plvx03e7HjcKPvn21gScRgJey2fPt55RPNFU2U/OxrrCfx7HuCC6ofutz7mafc6YnPFYMBiBOXg6pvAh6XeGFPbiJDuWIpQDLl43NjP1SBqWOjCxqdP8RvMnw3LO5J00RJ26tRncHo1iDsswYqk2tuoPqjXHXmjJT/2o/1e660J4uE9EEz4/DRSY+xSP3Nx1mr7meGkllrRI+ScKqxNWXrBagHaDLLlosMRlwynJ2zZV69WXexsizZfpwiaCDbRG0EU+L7x0AKq+7OBbsoaquw3CK1/0YLo5lF5c9VukvE9b2P9s84m9eJCiffATfyeM5Jfn2ylLWsTy4De7raCfZXXFb1hKU0WRx+GxbCN5A== X-Microsoft-Exchange-Diagnostics: 1;SN1PR0701MB1982;31:o6u+brCDje3gMb2JhW2eVkslSCkwBIf2K67AsTt6XzGdzc/ABzUCe1sKLMjz/VuV61rkiodp1yphYu0jdN5GxB94KA+KJPXLvOJDVLnGigfr+J9WYlVpIAvP4XAGlZ6BChRTvd2W7d4bIaFgUUAm9BfEY9380dcAlN6HWyo5B2FUaU8Fuh+dtRko5uayVTenhOieVqjxeWG81MTk4/pcMIWNJUYB4d6fE8Xg8KZaUB7pJXItalwDBcgybqHvnzWn;20:C9BHP7wFlzwZ2QnVygtH3rbqsOfvLI2w6vJTwfyF+vcU17Hmw15jV851oLVVGOJbLkEr+ENPcZjtKzHU/Zwm4tvnZmXgorMitTLR6lIETfz+gng3V3fDCp6ifv/MmHTOntodzGgw0JiXyEJXA8OgV0eOfm/9dX0ALnT5QfTrcWsiOHFck6riJ5ZQFj+KkGrRBnr3IU19vbz3lEEW1CDAzmTrbialj/KJ7Vx/rsddNBIOcA9+iXn5gwGe7wGrpfvNzPUbL6r1LorKhMhaXFNqyRi9bKARSyjFHhPEOTi+43AtSETFKRVMvTvVxhW0FBu4c2kxSnHdw7XYvS5dwh8xueGufUrKwgyUocgwnXAVAUHH13vv+c7V1aIP68IwVfhwEPdgcZIDsZcPZyCYBdZ+gwX8uhyiEzRmZNlVVKR3ARZE/YJcKr74/VwrbJIPJ/69/unitC8KB9CJ37OS/9XLyrXAZkXVxPx/k1lYYqbIOIcxy+zT1+p35FPgrno9fAX8 X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(6040375)(601004)(2401047)(5005006)(8121501046)(3002001)(10201501046)(6041248)(20161123560025)(20161123558025)(20161123555025)(20161123564025)(20161123562025)(6072148);SRVR:SN1PR0701MB1982;BCL:0;PCL:0;RULEID:;SRVR:SN1PR0701MB1982; X-Microsoft-Exchange-Diagnostics: 1;SN1PR0701MB1982;4:WjeBIPCnYa93OYG2PD9MNPkyJ4bIf8a0AEd+Oy3vXyZelqNgx/vqKwB1XIup53dZSIW19qkzFkA1HjvAHqXS4MopC+AN1Td4ONMSw3BlY805hDN2dWlH8UZnxwuP0VSo8qgF6Rvy4ECQUvo6D0uD4tm3zY2G7PVx3dx1nBL5jCfu7yXeeme8rG7X0YnO2F9zP3m2AexTIBHblUgbj10utmJYeKyboNsVe4a/tGYYReqBz7nVVBZ2vealYRpTaAEvRYYEkfsfS8YKm6Ca+/Myz5XN/hAMebUzwEZkh0+h3TCPoa8Ju8XHRnDNQyUjbrE15ZnX1rF+CgM60L/75Ck8ofw4fm6BYwYDiSoIjRhjDTOB8w7w2qCucv3jUa+8Hqp1LyCyo03RH/0zQLVv5uAYlFLdHBTR/piGGsDslXYpnsDhCzCIXuW3vHn8zJDkBR/fpXt0R02ZKprIXH3mH60gp6BPXeXqTCUpAF67N18IWtldmAiZ5sjvEvPiJqQO2O1mf78QHDLSF1ZMzytFswsTaTZO/8rirgcUbeU6HUbmYOyXPRoxOrVnlzYAYUDKSbHhsaNepqf0pEQ3iuu1HUAQZT/+4W71bksXMJEpH2GNkAg= X-Forefront-PRVS: 0218A015FA X-Forefront-Antispam-Report: SFV:NSPM;SFS:(10009020)(4630300001)(6069001)(6009001)(7916002)(39450400003)(199003)(189002)(5003940100001)(36756003)(81003)(53416004)(53936002)(105586002)(305945005)(5660300001)(6116002)(47776003)(38730400002)(107886003)(66066001)(42186005)(7736002)(6512007)(3846002)(189998001)(4326007)(2950100002)(106356001)(76506005)(6666003)(50466002)(92566002)(76176999)(6486002)(50986999)(25786008)(4720700003)(33646002)(81166006)(81156014)(101416001)(68736007)(97736004)(48376002)(8676002)(86362001)(50226002)(2906002)(6506006)(69596002);DIR:OUT;SFP:1101;SCL:1;SRVR:SN1PR0701MB1982;H:localhost.caveonetworks.com;FPR:;SPF:None;PTR:InfoNoRecords;A:1;MX:1;LANG:en; X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;SN1PR0701MB1982;23:DX5yh31ja0+YKY1/wjgo59DGrqIOVCTVT8h8/OP?= =?us-ascii?Q?6LQb4uRWW81YL815abSm8uZZScTrfA2dCEENyO1OdSDTOBxcGXLidozJcO86?= =?us-ascii?Q?M7ygMrONPlR36XpW/CepsfCBR+YapVTy/PACjTVRV6TpMRzklhpciIjeaK4o?= =?us-ascii?Q?+E3vUckURuUHxShIwEwE++w0VdYXzoqoKqVEBfHspUvhJqwXocRQL3vx5lqY?= =?us-ascii?Q?JW5LmHgDNY5m9CsdqjrOZJTMPV39T5vUWRMuQmt0bOxfVqqMOkcrIqkyyxpu?= =?us-ascii?Q?kjY5hKD8KfbSI0tmuNtsKC5s1tcYaTcNWzA7VMYOf+QxB5NPmpqXYDwHsufF?= =?us-ascii?Q?PXm2YSb964pXfLRm4u55eAAaCar5C26mOseNqZv/fZDFNSpr717FrrJTsSwo?= =?us-ascii?Q?+zAsZnVYohZgN+rtYlKL6upFu8aNN9FJvjQPEgC5SardtlAHZ6wcdd3y2eLP?= =?us-ascii?Q?qoJZ6mukOQbRrscXzFTiExTinE/9t++FzcGBRWEBZRzgfZIk5/kfOmVZ07/+?= =?us-ascii?Q?W6D6q812kT9sx08UPDwF6NnUNivpJaMyS4Au7tu4rQdZodz4kwM00bnObLbC?= =?us-ascii?Q?+PYcoEo62/R/478LvPom4UY9uSyjZgBGSbwpwHEgq3gbFi1j7Xi6ucZO4YIq?= =?us-ascii?Q?ETl2zI3juWYdNBLooDiY83fI3fnYjiE3C8X5MwiCbM+0EbAE3QHG17AyP2xA?= =?us-ascii?Q?loCimKSmTLLcgf2s46MBGsr8BY/t2Y+/q57xzE3tqYFxj2x5Qjh8OLkPdt5+?= =?us-ascii?Q?Mg/muaq7GCPJgiukHSFPh6ROjpgJYNNZlPNqjJAo+JqZcUQaA3fKze+VrHsY?= =?us-ascii?Q?4slP3PTvMP6v6BxEPyiGmQLvYTtvf5rR6n91XlmVxjE0c505f4Xke6RNW1Kg?= =?us-ascii?Q?VU+gVgBHpvk0LhqEVpMtYDJXQqXcFoeoPBUjCqbmiSAkfPQYpuBbpLP5Bb06?= =?us-ascii?Q?MKXcRUejejKTGqDh6kk1LPh2dk7u55rUe70AOJvd+LzC+Jv9/iyxa45lgT6n?= =?us-ascii?Q?4VWHSC+HlY/sedPR9XE2N23SNfkYXcANI1yEw0gIbXElLqRlA/9QJKqnD8po?= =?us-ascii?Q?/+4ou7mUYPZDQwdDA4k9nyb/cCtlc/eWHmR/z0fvSVPRvJVOOEEZJQ6e1Wkq?= =?us-ascii?Q?zDBdBxSu79LxhMaP9mjQXAr1T7ku2Z8A47g9deBKiOgDEfZ7gFTK3tvV2Fj+?= =?us-ascii?Q?mzQJbPKVn2a8oTCxXQFwRHJ3P+oq4romx7g3D?= X-Microsoft-Exchange-Diagnostics: 1;SN1PR0701MB1982;6:RrKUx5PV/cHFTMMnfOql8bQn4JNfvCDUz+HDe9uMM58lLEsMm6X4fC1hMH6Fobdb8rhZII2ZP8AdR2ZnYzmw7LSuxCuaC3OARf7BFJ5hvMYrwYOOs6z7cDuHsWD2G2CMHxowCPC+O0XDZ9bMZ2/EtwMGYwWWesXFH3yCmm5Ajx0WeEZwivL1UbcKE10gpS2RtDOnHgI6047MkB8suL6MIGgxDhdVERdT/3S9SlRAozy6V5fDC84FYqMMjLvoiLfZ2hzQanM5EDOu9AzTEMHxled7wfpU89ZRROh+3IWr8ThMxtCIydokA/NtnrXuNNB1gcGN2N5B1ohymwgJNg97DtEK9jdaolnwqw/pB3KngyiopTcsCLN0aVToaKsObmB40YNNUDhgj2QRwIHHf1P2uA==;5:DxexeOfHWs1Ng9bQtTlYJ9GvPD7ZnhYSocgy5AupuXzcwWlrcl9jYbIpspEJSVXTdCmYkS8lfAWRFzSm45FUNqsuqrCUicumuJ++0g+19FSMRQ/bEm63PTMD8TRkqzkr/Fl+eZchX1XRLQ/cH4q/DGfKxlGx3Rla+BiDgqLSmmg=;24:/lt1+8aM3UjBmTmX/0RC3BBNDWclI6aTrK9MV7H5QbbuFy8MubMh/LTRp1utEgpAfT0j/rwb6tZl3vLNqG55L1NyhaFHs5MNQVwkJjHPpLw= SpamDiagnosticOutput: 1:99 SpamDiagnosticMetadata: NSPM X-Microsoft-Exchange-Diagnostics: 1;SN1PR0701MB1982;7:BMKr3cmLJSAHmORyhSFuN2DReHeT8Kjd+aKetPITJFaBLEfclI2r1OOQ6gQbu6Rh2qjPqbWkaWKukiO3zAIF/W7mVqxhfeXiLaptBaAkZ2pvYQepubxpueLM8XtfKT3oOjjAEmur7f+O0aRPdqHELLSC9sQbVkgAZ/EnwmHdDP5siqaNR9LD0FnAtrmIcdeyUhUJwzgEowpjmH8t5vK8azO/hEERTxRdM+xupRSnxus4TF82cViyco6NdhuJkm5c8Msun4XiIKShWeRRsbb6DiQcwDc8az5FKBFGjqUXnq25UTELZSVULhhJpqQZYnWvGKO1sD4Gdeay5UA865Xin3+ViQoMcrHkcpFQ+FWuR35f1l3kahlQ9mtVTJwCaFzJb4aGyLm/IBvwNKqDFBBxaiKU/DQdX4vTqfulc1ekVdgNBwg94NLI5S2PYfac9ZixfOapg5dxVRSWzoYuUFZHEbs7ZJ4HkYPcQY7nl4+vA4tZ+MXYZH1p1Z2TaID19XhzdtkLdUy/4zNodfgrtjdJww== X-OriginatorOrg: cavium.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 14 Feb 2017 17:49:51.1503 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-Transport-CrossTenantHeadersStamped: SN1PR0701MB1982 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Cavium ThunderX and OCTEON-TX are arm64 based SoCs. Add driver for the on-chip GPIO pins. Signed-off-by: David Daney --- drivers/gpio/Kconfig | 7 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-thunderx.c | 490 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 498 insertions(+) create mode 100644 drivers/gpio/gpio-thunderx.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index d5d3654..cd55c050 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -421,6 +421,13 @@ config GPIO_TS4800 help This driver support TS-4800 FPGA GPIO controllers. +config GPIO_THUNDERX + tristate "Cavium ThunderX/OCTEON-TX GPIO" + depends on ARCH_THUNDER || (64BIT && COMPILE_TEST) + help + Say yes here to support the on-chip GPIO lines on the ThunderX + and OCTEON-TX families of SoCs. + config GPIO_TZ1090 bool "Toumaz Xenif TZ1090 GPIO support" depends on SOC_TZ1090 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index a7676b8..c62bc72 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -108,6 +108,7 @@ obj-$(CONFIG_GPIO_SYSCON) += gpio-syscon.o obj-$(CONFIG_GPIO_TB10X) += gpio-tb10x.o obj-$(CONFIG_GPIO_TC3589X) += gpio-tc3589x.o obj-$(CONFIG_GPIO_TEGRA) += gpio-tegra.o +obj-$(CONFIG_GPIO_THUNDERX) += gpio-thunderx.o obj-$(CONFIG_GPIO_TIMBERDALE) += gpio-timberdale.o obj-$(CONFIG_GPIO_PALMAS) += gpio-palmas.o obj-$(CONFIG_GPIO_TPIC2810) += gpio-tpic2810.o diff --git a/drivers/gpio/gpio-thunderx.c b/drivers/gpio/gpio-thunderx.c new file mode 100644 index 0000000..6afebc9 --- /dev/null +++ b/drivers/gpio/gpio-thunderx.c @@ -0,0 +1,490 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2016, 2017 Cavium Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define GPIO_RX_DAT 0x0 +#define GPIO_TX_SET 0x8 +#define GPIO_TX_CLR 0x10 +#define GPIO_CONST 0x90 +#define GPIO_CONST_GPIOS_MASK 0xff +#define GPIO_BIT_CFG 0x400 +#define GPIO_BIT_CFG_TX_OE BIT(0) +#define GPIO_BIT_CFG_PIN_XOR BIT(1) +#define GPIO_BIT_CFG_INT_EN BIT(2) +#define GPIO_BIT_CFG_INT_TYPE BIT(3) +#define GPIO_BIT_CFG_FIL_CNT_SHIFT 4 +#define GPIO_BIT_CFG_FIL_SEL_SHIFT 8 +#define GPIO_BIT_CFG_TX_OD BIT(12) +#define GPIO_BIT_CFG_PIN_SEL_MASK GENMASK(25, 16) +#define GPIO_INTR 0x800 +#define GPIO_INTR_INTR BIT(0) +#define GPIO_INTR_INTR_W1S BIT(1) +#define GPIO_INTR_ENA_W1C BIT(2) +#define GPIO_INTR_ENA_W1S BIT(3) +#define GPIO_2ND_BANK 0x1400 + +#define GLITCH_FILTER_400NS ((4ull << GPIO_BIT_CFG_FIL_SEL_SHIFT) | \ + (9ull << GPIO_BIT_CFG_FIL_CNT_SHIFT)) + +struct thunderx_gpio; + +struct thunderx_line { + struct thunderx_gpio *txgpio; + unsigned int line; +}; + +struct thunderx_gpio { + struct gpio_chip chip; + u8 __iomem *register_base; + struct irq_domain *irqd; + struct msix_entry *msix_entries; /* per line MSI-X */ + struct thunderx_line *line_entries; /* per line irq info */ + raw_spinlock_t lock; + unsigned long invert_mask[2]; + unsigned long od_mask[2]; + int base_msi; +}; + +static unsigned int bit_cfg_reg(unsigned int line) +{ + return 8 * line + GPIO_BIT_CFG; +} + +static unsigned int intr_reg(unsigned int line) +{ + return 8 * line + GPIO_INTR; +} + +/* + * Check (and WARN) that the pin is available for GPIO. We will not + * allow modification of the state of non-GPIO pins from this driver. + */ +static bool thunderx_gpio_is_gpio(struct thunderx_gpio *txgpio, + unsigned int line) +{ + u64 bit_cfg = readq(txgpio->register_base + bit_cfg_reg(line)); + bool rv = (bit_cfg & GPIO_BIT_CFG_PIN_SEL_MASK) == 0; + + WARN_RATELIMIT(!rv, "Pin %d not available for GPIO\n", line); + + return rv; +} + +static int thunderx_gpio_dir_in(struct gpio_chip *chip, unsigned int line) +{ + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + + if (!thunderx_gpio_is_gpio(txgpio, line)) + return -EIO; + + raw_spin_lock(&txgpio->lock); + clear_bit(line, txgpio->invert_mask); + clear_bit(line, txgpio->od_mask); + writeq(GLITCH_FILTER_400NS, txgpio->register_base + bit_cfg_reg(line)); + raw_spin_unlock(&txgpio->lock); + return 0; +} + +static void thunderx_gpio_set(struct gpio_chip *chip, unsigned int line, + int value) +{ + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + int bank = line / 64; + int bank_bit = line % 64; + + void __iomem *reg = txgpio->register_base + + (bank * GPIO_2ND_BANK) + (value ? GPIO_TX_SET : GPIO_TX_CLR); + + writeq(BIT_ULL(bank_bit), reg); +} + +static int thunderx_gpio_dir_out(struct gpio_chip *chip, unsigned int line, + int value) +{ + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + u64 bit_cfg = GPIO_BIT_CFG_TX_OE; + + if (!thunderx_gpio_is_gpio(txgpio, line)) + return -EIO; + + raw_spin_lock(&txgpio->lock); + + thunderx_gpio_set(chip, line, value); + + if (test_bit(line, txgpio->invert_mask)) + bit_cfg |= GPIO_BIT_CFG_PIN_XOR; + + if (test_bit(line, txgpio->od_mask)) + bit_cfg |= GPIO_BIT_CFG_TX_OD; + + writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(line)); + + raw_spin_unlock(&txgpio->lock); + return 0; +} + +/* + * Weird, setting open-drain mode causes signal inversion. Note this + * so we can compensate in the dir_out function. + */ +static int thunderx_gpio_set_single_ended(struct gpio_chip *chip, + unsigned int line, + enum single_ended_mode mode) +{ + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + + if (mode == LINE_MODE_OPEN_SOURCE) + return -ENOTSUPP; + + if (!thunderx_gpio_is_gpio(txgpio, line)) + return -EIO; + + raw_spin_lock(&txgpio->lock); + if (mode == LINE_MODE_OPEN_DRAIN) { + set_bit(line, txgpio->invert_mask); + set_bit(line, txgpio->od_mask); + } else { + clear_bit(line, txgpio->invert_mask); + clear_bit(line, txgpio->od_mask); + } + raw_spin_unlock(&txgpio->lock); + + return 0; +} + +static int thunderx_gpio_get(struct gpio_chip *chip, unsigned int line) +{ + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + int bank = line / 64; + int bank_bit = line % 64; + u64 read_bits = readq(txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_RX_DAT); + u64 masked_bits = read_bits & BIT_ULL(bank_bit); + + if (test_bit(line, txgpio->invert_mask)) + return masked_bits == 0; + else + return masked_bits != 0; +} + +static void thunderx_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + int bank; + u64 set_bits, clear_bits; + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + + for (bank = 0; bank <= chip->ngpio / 64; bank++) { + set_bits = bits[bank] & mask[bank]; + clear_bits = ~bits[bank] & mask[bank]; + writeq(set_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_SET); + writeq(clear_bits, txgpio->register_base + (bank * GPIO_2ND_BANK) + GPIO_TX_CLR); + } +} + +static void thunderx_gpio_irq_ack(struct irq_data *data) +{ + struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + + writeq(GPIO_INTR_INTR, + txline->txgpio->register_base + intr_reg(txline->line)); +} + +static void thunderx_gpio_irq_mask(struct irq_data *data) +{ + struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + + writeq(GPIO_INTR_ENA_W1C, + txline->txgpio->register_base + intr_reg(txline->line)); +} + +static void thunderx_gpio_irq_mask_ack(struct irq_data *data) +{ + struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + + writeq(GPIO_INTR_ENA_W1C | GPIO_INTR_INTR, + txline->txgpio->register_base + intr_reg(txline->line)); +} + +static void thunderx_gpio_irq_unmask(struct irq_data *data) +{ + struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + + writeq(GPIO_INTR_ENA_W1S, + txline->txgpio->register_base + intr_reg(txline->line)); +} + +static int thunderx_gpio_irq_set_type(struct irq_data *data, + unsigned int flow_type) +{ + struct thunderx_line *txline = irq_data_get_irq_chip_data(data); + struct thunderx_gpio *txgpio = txline->txgpio; + u64 bit_cfg; + + irqd_set_trigger_type(data, flow_type); + + bit_cfg = GLITCH_FILTER_400NS | GPIO_BIT_CFG_INT_EN; + + if (flow_type & IRQ_TYPE_EDGE_BOTH) { + irq_set_handler_locked(data, handle_fasteoi_edge_irq); + bit_cfg |= GPIO_BIT_CFG_INT_TYPE; + } else { + irq_set_handler_locked(data, handle_fasteoi_level_irq); + } + + raw_spin_lock(&txgpio->lock); + if (flow_type & (IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_LEVEL_LOW)) { + bit_cfg |= GPIO_BIT_CFG_PIN_XOR; + set_bit(txline->line, txgpio->invert_mask); + } else { + clear_bit(txline->line, txgpio->invert_mask); + } + clear_bit(txline->line, txgpio->od_mask); + writeq(bit_cfg, txgpio->register_base + bit_cfg_reg(txline->line)); + raw_spin_unlock(&txgpio->lock); + + return IRQ_SET_MASK_OK; +} + +static void thunderx_gpio_irq_enable(struct irq_data *data) +{ + irq_chip_enable_parent(data); + thunderx_gpio_irq_unmask(data); +} + +static void thunderx_gpio_irq_disable(struct irq_data *data) +{ + thunderx_gpio_irq_mask(data); + irq_chip_disable_parent(data); +} + +/* + * Interrupts are chained from underlying MSI-X vectors. We have + * these irq_chip functions to be able to handle level triggering + * semantics and other acknowledgment tasks associated with the GPIO + * mechanism. + */ +static struct irq_chip thunderx_gpio_irq_chip = { + .name = "GPIO", + .irq_enable = thunderx_gpio_irq_enable, + .irq_disable = thunderx_gpio_irq_disable, + .irq_ack = thunderx_gpio_irq_ack, + .irq_mask = thunderx_gpio_irq_mask, + .irq_mask_ack = thunderx_gpio_irq_mask_ack, + .irq_unmask = thunderx_gpio_irq_unmask, + .irq_eoi = irq_chip_eoi_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, + .irq_set_type = thunderx_gpio_irq_set_type, + + .flags = IRQCHIP_SET_TYPE_MASKED +}; + +static int thunderx_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW); + return 0; +} + +static int thunderx_gpio_irq_translate(struct irq_domain *d, + struct irq_fwspec *fwspec, + irq_hw_number_t *hwirq, + unsigned int *type) +{ + if (WARN_ON(fwspec->param_count < 2)) + return -EINVAL; + *hwirq = fwspec->param[0]; + *type = fwspec->param[1] & IRQ_TYPE_SENSE_MASK; + return 0; +} + +static int thunderx_gpio_irq_alloc(struct irq_domain *d, unsigned int virq, + unsigned int nr_irqs, void *arg) +{ + struct thunderx_line *txline = arg; + + return irq_domain_set_hwirq_and_chip(d, virq, txline->line, + &thunderx_gpio_irq_chip, txline); +} + +static const struct irq_domain_ops thunderx_gpio_irqd_ops = { + .map = thunderx_gpio_irq_map, + .alloc = thunderx_gpio_irq_alloc, + .translate = thunderx_gpio_irq_translate +}; + +static int thunderx_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) +{ + struct thunderx_gpio *txgpio = gpiochip_get_data(chip); + + return irq_find_mapping(txgpio->irqd, offset); +} + +static int thunderx_gpio_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + void __iomem * const *tbl; + struct device *dev = &pdev->dev; + struct thunderx_gpio *txgpio; + struct gpio_chip *chip; + int ngpio, i; + int err = 0; + + txgpio = devm_kzalloc(dev, sizeof(*txgpio), GFP_KERNEL); + if (!txgpio) + return -ENOMEM; + + raw_spin_lock_init(&txgpio->lock); + chip = &txgpio->chip; + + pci_set_drvdata(pdev, txgpio); + + err = pcim_enable_device(pdev); + if (err) { + dev_err(dev, "Failed to enable PCI device: err %d\n", err); + goto out; + } + + err = pcim_iomap_regions(pdev, 1 << 0, KBUILD_MODNAME); + if (err) { + dev_err(dev, "Failed to iomap PCI device: err %d\n", err); + goto out; + } + + tbl = pcim_iomap_table(pdev); + txgpio->register_base = tbl[0]; + if (!txgpio->register_base) { + dev_err(dev, "Cannot map PCI resource\n"); + err = -ENOMEM; + goto out; + } + + if (pdev->subsystem_device == 0xa10a) { + /* CN88XX has no GPIO_CONST register*/ + ngpio = 50; + txgpio->base_msi = 48; + } else { + u64 c = readq(txgpio->register_base + GPIO_CONST); + + ngpio = c & GPIO_CONST_GPIOS_MASK; + txgpio->base_msi = (c >> 8) & 0xff; + } + + txgpio->msix_entries = devm_kzalloc(dev, + sizeof(struct msix_entry) * ngpio, + GFP_KERNEL); + if (!txgpio->msix_entries) { + err = -ENOMEM; + goto out; + } + + txgpio->line_entries = devm_kzalloc(dev, + sizeof(struct thunderx_line) * ngpio, + GFP_KERNEL); + if (!txgpio->line_entries) { + err = -ENOMEM; + goto out; + } + + for (i = 0; i < ngpio; i++) { + txgpio->msix_entries[i].entry = txgpio->base_msi + (2 * i); + txgpio->line_entries[i].line = i; + txgpio->line_entries[i].txgpio = txgpio; + } + + + /* Enable all MSI-X for interrupts on all possible lines. */ + err = pci_enable_msix(pdev, txgpio->msix_entries, ngpio); + if (err < 0) + goto out; + + /* + * Push GPIO specific irqdomain on hierarchy created as a side + * effect of the pci_enable_msix() + */ + txgpio->irqd = irq_domain_create_hierarchy(irq_get_irq_data(txgpio->msix_entries[0].vector)->domain, + 0, 0, of_node_to_fwnode(dev->of_node), + &thunderx_gpio_irqd_ops, txgpio); + if (!txgpio->irqd) + goto out; + + /* Push on irq_data and the domain for each line. */ + for (i = 0; i < ngpio; i++) { + err = irq_domain_push_irq(txgpio->irqd, + txgpio->msix_entries[i].vector, + &txgpio->line_entries[i]); + if (err < 0) + dev_err(dev, "irq_domain_push_irq: %d\n", err); + } + + chip->label = KBUILD_MODNAME; + chip->parent = dev; + chip->owner = THIS_MODULE; + chip->base = -1; /* System allocated */ + chip->can_sleep = false; + chip->ngpio = ngpio; + chip->direction_input = thunderx_gpio_dir_in; + chip->get = thunderx_gpio_get; + chip->direction_output = thunderx_gpio_dir_out; + chip->set = thunderx_gpio_set; + chip->set_multiple = thunderx_gpio_set_multiple; + chip->set_single_ended = thunderx_gpio_set_single_ended; + chip->to_irq = thunderx_gpio_to_irq; + err = devm_gpiochip_add_data(dev, chip, txgpio); + if (err) + goto out; + + dev_info(dev, "ThunderX GPIO: %d lines with base %d.\n", + ngpio, chip->base); + return 0; +out: + pci_set_drvdata(pdev, NULL); + return err; +} + +static void thunderx_gpio_remove(struct pci_dev *pdev) +{ + int i; + struct thunderx_gpio *txgpio = pci_get_drvdata(pdev); + + for (i = 0; i < txgpio->chip.ngpio; i++) + irq_domain_pop_irq(txgpio->irqd, + txgpio->msix_entries[i].vector); + + irq_domain_remove(txgpio->irqd); + + pci_set_drvdata(pdev, NULL); +} + +static const struct pci_device_id thunderx_gpio_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA00A) }, + { 0, } /* end of table */ +}; + +MODULE_DEVICE_TABLE(pci, thunderx_gpio_id_table); + +static struct pci_driver thunderx_gpio_driver = { + .name = KBUILD_MODNAME, + .id_table = thunderx_gpio_id_table, + .probe = thunderx_gpio_probe, + .remove = thunderx_gpio_remove, +}; + +module_pci_driver(thunderx_gpio_driver); + +MODULE_DESCRIPTION("Cavium Inc. ThunderX/OCTEON-TX GPIO Driver"); +MODULE_LICENSE("GPL"); -- 1.8.3.1