18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/** 38c2ecf20Sopenharmony_ci * Copyright (C) 2006 Juergen Beisert, Pengutronix 48c2ecf20Sopenharmony_ci * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix 58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Wolfram Sang, Pengutronix 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are 88c2ecf20Sopenharmony_ci * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more 98c2ecf20Sopenharmony_ci * details 108c2ecf20Sopenharmony_ci * Note: 118c2ecf20Sopenharmony_ci * - DIN must be stable at the rising edge of clock. 128c2ecf20Sopenharmony_ci * - when writing: 138c2ecf20Sopenharmony_ci * - always clock in 16 clocks at once 148c2ecf20Sopenharmony_ci * - at DIN: D15 first, D0 last 158c2ecf20Sopenharmony_ci * - D0..D7 = databyte, D8..D14 = commandbyte 168c2ecf20Sopenharmony_ci * - D15 = low -> write command 178c2ecf20Sopenharmony_ci * - when reading 188c2ecf20Sopenharmony_ci * - always clock in 16 clocks at once 198c2ecf20Sopenharmony_ci * - at DIN: D15 first, D0 last 208c2ecf20Sopenharmony_ci * - D0..D7 = dummy, D8..D14 = register address 218c2ecf20Sopenharmony_ci * - D15 = high -> read command 228c2ecf20Sopenharmony_ci * - raise CS and assert it again 238c2ecf20Sopenharmony_ci * - always clock in 16 clocks at once 248c2ecf20Sopenharmony_ci * - at DOUT: D15 first, D0 last 258c2ecf20Sopenharmony_ci * - D0..D7 contains the data from the first cycle 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * The driver exports a standard gpiochip interface 288c2ecf20Sopenharmony_ci */ 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <linux/module.h> 318c2ecf20Sopenharmony_ci#include <linux/init.h> 328c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 338c2ecf20Sopenharmony_ci#include <linux/mutex.h> 348c2ecf20Sopenharmony_ci#include <linux/spi/max7301.h> 358c2ecf20Sopenharmony_ci#include <linux/gpio/driver.h> 368c2ecf20Sopenharmony_ci#include <linux/slab.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci/* 398c2ecf20Sopenharmony_ci * Pin configurations, see MAX7301 datasheet page 6 408c2ecf20Sopenharmony_ci */ 418c2ecf20Sopenharmony_ci#define PIN_CONFIG_MASK 0x03 428c2ecf20Sopenharmony_ci#define PIN_CONFIG_IN_PULLUP 0x03 438c2ecf20Sopenharmony_ci#define PIN_CONFIG_IN_WO_PULLUP 0x02 448c2ecf20Sopenharmony_ci#define PIN_CONFIG_OUT 0x01 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#define PIN_NUMBER 28 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int max7301_direction_input(struct gpio_chip *chip, unsigned offset) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct max7301 *ts = container_of(chip, struct max7301, chip); 518c2ecf20Sopenharmony_ci u8 *config; 528c2ecf20Sopenharmony_ci u8 offset_bits, pin_config; 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* First 4 pins are unused in the controller */ 568c2ecf20Sopenharmony_ci offset += 4; 578c2ecf20Sopenharmony_ci offset_bits = (offset & 3) << 1; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci config = &ts->port_config[offset >> 2]; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (ts->input_pullup_active & BIT(offset)) 628c2ecf20Sopenharmony_ci pin_config = PIN_CONFIG_IN_PULLUP; 638c2ecf20Sopenharmony_ci else 648c2ecf20Sopenharmony_ci pin_config = PIN_CONFIG_IN_WO_PULLUP; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci mutex_lock(&ts->lock); 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) 698c2ecf20Sopenharmony_ci | (pin_config << offset_bits); 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci mutex_unlock(&ts->lock); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return ret; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic int __max7301_set(struct max7301 *ts, unsigned offset, int value) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci if (value) { 818c2ecf20Sopenharmony_ci ts->out_level |= 1 << offset; 828c2ecf20Sopenharmony_ci return ts->write(ts->dev, 0x20 + offset, 0x01); 838c2ecf20Sopenharmony_ci } else { 848c2ecf20Sopenharmony_ci ts->out_level &= ~(1 << offset); 858c2ecf20Sopenharmony_ci return ts->write(ts->dev, 0x20 + offset, 0x00); 868c2ecf20Sopenharmony_ci } 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int max7301_direction_output(struct gpio_chip *chip, unsigned offset, 908c2ecf20Sopenharmony_ci int value) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct max7301 *ts = container_of(chip, struct max7301, chip); 938c2ecf20Sopenharmony_ci u8 *config; 948c2ecf20Sopenharmony_ci u8 offset_bits; 958c2ecf20Sopenharmony_ci int ret; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* First 4 pins are unused in the controller */ 988c2ecf20Sopenharmony_ci offset += 4; 998c2ecf20Sopenharmony_ci offset_bits = (offset & 3) << 1; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci config = &ts->port_config[offset >> 2]; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci mutex_lock(&ts->lock); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci *config = (*config & ~(PIN_CONFIG_MASK << offset_bits)) 1068c2ecf20Sopenharmony_ci | (PIN_CONFIG_OUT << offset_bits); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ret = __max7301_set(ts, offset, value); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!ret) 1118c2ecf20Sopenharmony_ci ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci mutex_unlock(&ts->lock); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci return ret; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic int max7301_get(struct gpio_chip *chip, unsigned offset) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct max7301 *ts = gpiochip_get_data(chip); 1218c2ecf20Sopenharmony_ci int config, level = -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci /* First 4 pins are unused in the controller */ 1248c2ecf20Sopenharmony_ci offset += 4; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci mutex_lock(&ts->lock); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1)) 1298c2ecf20Sopenharmony_ci & PIN_CONFIG_MASK; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci switch (config) { 1328c2ecf20Sopenharmony_ci case PIN_CONFIG_OUT: 1338c2ecf20Sopenharmony_ci /* Output: return cached level */ 1348c2ecf20Sopenharmony_ci level = !!(ts->out_level & (1 << offset)); 1358c2ecf20Sopenharmony_ci break; 1368c2ecf20Sopenharmony_ci case PIN_CONFIG_IN_WO_PULLUP: 1378c2ecf20Sopenharmony_ci case PIN_CONFIG_IN_PULLUP: 1388c2ecf20Sopenharmony_ci /* Input: read out */ 1398c2ecf20Sopenharmony_ci level = ts->read(ts->dev, 0x20 + offset) & 0x01; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci mutex_unlock(&ts->lock); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci return level; 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_cistatic void max7301_set(struct gpio_chip *chip, unsigned offset, int value) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci struct max7301 *ts = gpiochip_get_data(chip); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci /* First 4 pins are unused in the controller */ 1518c2ecf20Sopenharmony_ci offset += 4; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci mutex_lock(&ts->lock); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci __max7301_set(ts, offset, value); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci mutex_unlock(&ts->lock); 1588c2ecf20Sopenharmony_ci} 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ciint __max730x_probe(struct max7301 *ts) 1618c2ecf20Sopenharmony_ci{ 1628c2ecf20Sopenharmony_ci struct device *dev = ts->dev; 1638c2ecf20Sopenharmony_ci struct max7301_platform_data *pdata; 1648c2ecf20Sopenharmony_ci int i, ret; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci pdata = dev_get_platdata(dev); 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci mutex_init(&ts->lock); 1698c2ecf20Sopenharmony_ci dev_set_drvdata(dev, ts); 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci /* Power up the chip and disable IRQ output */ 1728c2ecf20Sopenharmony_ci ts->write(dev, 0x04, 0x01); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci if (pdata) { 1758c2ecf20Sopenharmony_ci ts->input_pullup_active = pdata->input_pullup_active; 1768c2ecf20Sopenharmony_ci ts->chip.base = pdata->base; 1778c2ecf20Sopenharmony_ci } else { 1788c2ecf20Sopenharmony_ci ts->chip.base = -1; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci ts->chip.label = dev->driver->name; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci ts->chip.direction_input = max7301_direction_input; 1838c2ecf20Sopenharmony_ci ts->chip.get = max7301_get; 1848c2ecf20Sopenharmony_ci ts->chip.direction_output = max7301_direction_output; 1858c2ecf20Sopenharmony_ci ts->chip.set = max7301_set; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci ts->chip.ngpio = PIN_NUMBER; 1888c2ecf20Sopenharmony_ci ts->chip.can_sleep = true; 1898c2ecf20Sopenharmony_ci ts->chip.parent = dev; 1908c2ecf20Sopenharmony_ci ts->chip.owner = THIS_MODULE; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* 1938c2ecf20Sopenharmony_ci * initialize pullups according to platform data and cache the 1948c2ecf20Sopenharmony_ci * register values for later use. 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_ci for (i = 1; i < 8; i++) { 1978c2ecf20Sopenharmony_ci int j; 1988c2ecf20Sopenharmony_ci /* 1998c2ecf20Sopenharmony_ci * initialize port_config with "0xAA", which means 2008c2ecf20Sopenharmony_ci * input with internal pullup disabled. This is needed 2018c2ecf20Sopenharmony_ci * to avoid writing zeros (in the inner for loop), 2028c2ecf20Sopenharmony_ci * which is not allowed according to the datasheet. 2038c2ecf20Sopenharmony_ci */ 2048c2ecf20Sopenharmony_ci ts->port_config[i] = 0xAA; 2058c2ecf20Sopenharmony_ci for (j = 0; j < 4; j++) { 2068c2ecf20Sopenharmony_ci int offset = (i - 1) * 4 + j; 2078c2ecf20Sopenharmony_ci ret = max7301_direction_input(&ts->chip, offset); 2088c2ecf20Sopenharmony_ci if (ret) 2098c2ecf20Sopenharmony_ci goto exit_destroy; 2108c2ecf20Sopenharmony_ci } 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ret = gpiochip_add_data(&ts->chip, ts); 2148c2ecf20Sopenharmony_ci if (!ret) 2158c2ecf20Sopenharmony_ci return ret; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ciexit_destroy: 2188c2ecf20Sopenharmony_ci mutex_destroy(&ts->lock); 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__max730x_probe); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ciint __max730x_remove(struct device *dev) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci struct max7301 *ts = dev_get_drvdata(dev); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci if (ts == NULL) 2288c2ecf20Sopenharmony_ci return -ENODEV; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci /* Power down the chip and disable IRQ output */ 2318c2ecf20Sopenharmony_ci ts->write(dev, 0x04, 0x00); 2328c2ecf20Sopenharmony_ci gpiochip_remove(&ts->chip); 2338c2ecf20Sopenharmony_ci mutex_destroy(&ts->lock); 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__max730x_remove); 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ciMODULE_AUTHOR("Juergen Beisert, Wolfram Sang"); 2398c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2408c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts"); 241