18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * PPC4xx gpio driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2008 Harris Corporation 68c2ecf20Sopenharmony_ci * Copyright (c) 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix 78c2ecf20Sopenharmony_ci * Copyright (c) MontaVista Software, Inc. 2008. 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Author: Steve Falco <sfalco@harris.com> 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 158c2ecf20Sopenharmony_ci#include <linux/io.h> 168c2ecf20Sopenharmony_ci#include <linux/of.h> 178c2ecf20Sopenharmony_ci#include <linux/of_gpio.h> 188c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 198c2ecf20Sopenharmony_ci#include <linux/types.h> 208c2ecf20Sopenharmony_ci#include <linux/slab.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define GPIO_MASK(gpio) (0x80000000 >> (gpio)) 238c2ecf20Sopenharmony_ci#define GPIO_MASK2(gpio) (0xc0000000 >> ((gpio) * 2)) 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Physical GPIO register layout */ 268c2ecf20Sopenharmony_cistruct ppc4xx_gpio { 278c2ecf20Sopenharmony_ci __be32 or; 288c2ecf20Sopenharmony_ci __be32 tcr; 298c2ecf20Sopenharmony_ci __be32 osrl; 308c2ecf20Sopenharmony_ci __be32 osrh; 318c2ecf20Sopenharmony_ci __be32 tsrl; 328c2ecf20Sopenharmony_ci __be32 tsrh; 338c2ecf20Sopenharmony_ci __be32 odr; 348c2ecf20Sopenharmony_ci __be32 ir; 358c2ecf20Sopenharmony_ci __be32 rr1; 368c2ecf20Sopenharmony_ci __be32 rr2; 378c2ecf20Sopenharmony_ci __be32 rr3; 388c2ecf20Sopenharmony_ci __be32 reserved1; 398c2ecf20Sopenharmony_ci __be32 isr1l; 408c2ecf20Sopenharmony_ci __be32 isr1h; 418c2ecf20Sopenharmony_ci __be32 isr2l; 428c2ecf20Sopenharmony_ci __be32 isr2h; 438c2ecf20Sopenharmony_ci __be32 isr3l; 448c2ecf20Sopenharmony_ci __be32 isr3h; 458c2ecf20Sopenharmony_ci}; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistruct ppc4xx_gpio_chip { 488c2ecf20Sopenharmony_ci struct of_mm_gpio_chip mm_gc; 498c2ecf20Sopenharmony_ci spinlock_t lock; 508c2ecf20Sopenharmony_ci}; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci/* 538c2ecf20Sopenharmony_ci * GPIO LIB API implementation for GPIOs 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * There are a maximum of 32 gpios in each gpio controller. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_cistatic int ppc4xx_gpio_get(struct gpio_chip *gc, unsigned int gpio) 598c2ecf20Sopenharmony_ci{ 608c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); 618c2ecf20Sopenharmony_ci struct ppc4xx_gpio __iomem *regs = mm_gc->regs; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci return !!(in_be32(®s->ir) & GPIO_MASK(gpio)); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic inline void 678c2ecf20Sopenharmony_ci__ppc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); 708c2ecf20Sopenharmony_ci struct ppc4xx_gpio __iomem *regs = mm_gc->regs; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci if (val) 738c2ecf20Sopenharmony_ci setbits32(®s->or, GPIO_MASK(gpio)); 748c2ecf20Sopenharmony_ci else 758c2ecf20Sopenharmony_ci clrbits32(®s->or, GPIO_MASK(gpio)); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void 798c2ecf20Sopenharmony_cippc4xx_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); 828c2ecf20Sopenharmony_ci unsigned long flags; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci __ppc4xx_gpio_set(gc, gpio, val); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cistatic int ppc4xx_gpio_dir_in(struct gpio_chip *gc, unsigned int gpio) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); 968c2ecf20Sopenharmony_ci struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); 978c2ecf20Sopenharmony_ci struct ppc4xx_gpio __iomem *regs = mm_gc->regs; 988c2ecf20Sopenharmony_ci unsigned long flags; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci /* Disable open-drain function */ 1038c2ecf20Sopenharmony_ci clrbits32(®s->odr, GPIO_MASK(gpio)); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Float the pin */ 1068c2ecf20Sopenharmony_ci clrbits32(®s->tcr, GPIO_MASK(gpio)); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Bits 0-15 use TSRL/OSRL, bits 16-31 use TSRH/OSRH */ 1098c2ecf20Sopenharmony_ci if (gpio < 16) { 1108c2ecf20Sopenharmony_ci clrbits32(®s->osrl, GPIO_MASK2(gpio)); 1118c2ecf20Sopenharmony_ci clrbits32(®s->tsrl, GPIO_MASK2(gpio)); 1128c2ecf20Sopenharmony_ci } else { 1138c2ecf20Sopenharmony_ci clrbits32(®s->osrh, GPIO_MASK2(gpio)); 1148c2ecf20Sopenharmony_ci clrbits32(®s->tsrh, GPIO_MASK2(gpio)); 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic int 1238c2ecf20Sopenharmony_cippc4xx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); 1268c2ecf20Sopenharmony_ci struct ppc4xx_gpio_chip *chip = gpiochip_get_data(gc); 1278c2ecf20Sopenharmony_ci struct ppc4xx_gpio __iomem *regs = mm_gc->regs; 1288c2ecf20Sopenharmony_ci unsigned long flags; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock_irqsave(&chip->lock, flags); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* First set initial value */ 1338c2ecf20Sopenharmony_ci __ppc4xx_gpio_set(gc, gpio, val); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci /* Disable open-drain function */ 1368c2ecf20Sopenharmony_ci clrbits32(®s->odr, GPIO_MASK(gpio)); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* Drive the pin */ 1398c2ecf20Sopenharmony_ci setbits32(®s->tcr, GPIO_MASK(gpio)); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* Bits 0-15 use TSRL, bits 16-31 use TSRH */ 1428c2ecf20Sopenharmony_ci if (gpio < 16) { 1438c2ecf20Sopenharmony_ci clrbits32(®s->osrl, GPIO_MASK2(gpio)); 1448c2ecf20Sopenharmony_ci clrbits32(®s->tsrl, GPIO_MASK2(gpio)); 1458c2ecf20Sopenharmony_ci } else { 1468c2ecf20Sopenharmony_ci clrbits32(®s->osrh, GPIO_MASK2(gpio)); 1478c2ecf20Sopenharmony_ci clrbits32(®s->tsrh, GPIO_MASK2(gpio)); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chip->lock, flags); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci pr_debug("%s: gpio: %d val: %d\n", __func__, gpio, val); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return 0; 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_cistatic int __init ppc4xx_add_gpiochips(void) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct device_node *np; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for_each_compatible_node(np, NULL, "ibm,ppc4xx-gpio") { 1628c2ecf20Sopenharmony_ci int ret; 1638c2ecf20Sopenharmony_ci struct ppc4xx_gpio_chip *ppc4xx_gc; 1648c2ecf20Sopenharmony_ci struct of_mm_gpio_chip *mm_gc; 1658c2ecf20Sopenharmony_ci struct gpio_chip *gc; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci ppc4xx_gc = kzalloc(sizeof(*ppc4xx_gc), GFP_KERNEL); 1688c2ecf20Sopenharmony_ci if (!ppc4xx_gc) { 1698c2ecf20Sopenharmony_ci ret = -ENOMEM; 1708c2ecf20Sopenharmony_ci goto err; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci spin_lock_init(&ppc4xx_gc->lock); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci mm_gc = &ppc4xx_gc->mm_gc; 1768c2ecf20Sopenharmony_ci gc = &mm_gc->gc; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci gc->ngpio = 32; 1798c2ecf20Sopenharmony_ci gc->direction_input = ppc4xx_gpio_dir_in; 1808c2ecf20Sopenharmony_ci gc->direction_output = ppc4xx_gpio_dir_out; 1818c2ecf20Sopenharmony_ci gc->get = ppc4xx_gpio_get; 1828c2ecf20Sopenharmony_ci gc->set = ppc4xx_gpio_set; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = of_mm_gpiochip_add_data(np, mm_gc, ppc4xx_gc); 1858c2ecf20Sopenharmony_ci if (ret) 1868c2ecf20Sopenharmony_ci goto err; 1878c2ecf20Sopenharmony_ci continue; 1888c2ecf20Sopenharmony_cierr: 1898c2ecf20Sopenharmony_ci pr_err("%pOF: registration failed with status %d\n", np, ret); 1908c2ecf20Sopenharmony_ci kfree(ppc4xx_gc); 1918c2ecf20Sopenharmony_ci /* try others anyway */ 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci return 0; 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ciarch_initcall(ppc4xx_add_gpiochips); 196