brintos

brintos / linux-shallow public Read only

0
0
Text · 5.6 KiB · 701795b Raw
237 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2006 Juergen Beisert, Pengutronix4 * Copyright (C) 2008 Guennadi Liakhovetski, Pengutronix5 * Copyright (C) 2009 Wolfram Sang, Pengutronix6 *7 * The Maxim MAX7300/1 device is an I2C/SPI driven GPIO expander. There are8 * 28 GPIOs. 8 of them can trigger an interrupt. See datasheet for more9 * details10 * Note:11 * - DIN must be stable at the rising edge of clock.12 * - when writing:13 *   - always clock in 16 clocks at once14 *   - at DIN: D15 first, D0 last15 *   - D0..D7 = databyte, D8..D14 = commandbyte16 *   - D15 = low -> write command17 * - when reading18 *   - always clock in 16 clocks at once19 *   - at DIN: D15 first, D0 last20 *   - D0..D7 = dummy, D8..D14 = register address21 *   - D15 = high -> read command22 *   - raise CS and assert it again23 *   - always clock in 16 clocks at once24 *   - at DOUT: D15 first, D0 last25 *   - D0..D7 contains the data from the first cycle26 *27 * The driver exports a standard gpiochip interface28 */29 30#include <linux/module.h>31#include <linux/init.h>32#include <linux/platform_device.h>33#include <linux/mutex.h>34#include <linux/spi/max7301.h>35#include <linux/gpio/driver.h>36#include <linux/slab.h>37 38/*39 * Pin configurations, see MAX7301 datasheet page 640 */41#define PIN_CONFIG_MASK 0x0342#define PIN_CONFIG_IN_PULLUP 0x0343#define PIN_CONFIG_IN_WO_PULLUP 0x0244#define PIN_CONFIG_OUT 0x0145 46#define PIN_NUMBER 2847 48static int max7301_direction_input(struct gpio_chip *chip, unsigned offset)49{50	struct max7301 *ts = container_of(chip, struct max7301, chip);51	u8 *config;52	u8 offset_bits, pin_config;53	int ret;54 55	/* First 4 pins are unused in the controller */56	offset += 4;57	offset_bits = (offset & 3) << 1;58 59	config = &ts->port_config[offset >> 2];60 61	if (ts->input_pullup_active & BIT(offset))62		pin_config = PIN_CONFIG_IN_PULLUP;63	else64		pin_config = PIN_CONFIG_IN_WO_PULLUP;65 66	mutex_lock(&ts->lock);67 68	*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))69			   | (pin_config << offset_bits);70 71	ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);72 73	mutex_unlock(&ts->lock);74 75	return ret;76}77 78static int __max7301_set(struct max7301 *ts, unsigned offset, int value)79{80	if (value) {81		ts->out_level |= 1 << offset;82		return ts->write(ts->dev, 0x20 + offset, 0x01);83	} else {84		ts->out_level &= ~(1 << offset);85		return ts->write(ts->dev, 0x20 + offset, 0x00);86	}87}88 89static int max7301_direction_output(struct gpio_chip *chip, unsigned offset,90				    int value)91{92	struct max7301 *ts = container_of(chip, struct max7301, chip);93	u8 *config;94	u8 offset_bits;95	int ret;96 97	/* First 4 pins are unused in the controller */98	offset += 4;99	offset_bits = (offset & 3) << 1;100 101	config = &ts->port_config[offset >> 2];102 103	mutex_lock(&ts->lock);104 105	*config = (*config & ~(PIN_CONFIG_MASK << offset_bits))106			   | (PIN_CONFIG_OUT << offset_bits);107 108	ret = __max7301_set(ts, offset, value);109 110	if (!ret)111		ret = ts->write(ts->dev, 0x08 + (offset >> 2), *config);112 113	mutex_unlock(&ts->lock);114 115	return ret;116}117 118static int max7301_get(struct gpio_chip *chip, unsigned offset)119{120	struct max7301 *ts = gpiochip_get_data(chip);121	int config, level = -EINVAL;122 123	/* First 4 pins are unused in the controller */124	offset += 4;125 126	mutex_lock(&ts->lock);127 128	config = (ts->port_config[offset >> 2] >> ((offset & 3) << 1))129			& PIN_CONFIG_MASK;130 131	switch (config) {132	case PIN_CONFIG_OUT:133		/* Output: return cached level */134		level =  !!(ts->out_level & (1 << offset));135		break;136	case PIN_CONFIG_IN_WO_PULLUP:137	case PIN_CONFIG_IN_PULLUP:138		/* Input: read out */139		level = ts->read(ts->dev, 0x20 + offset) & 0x01;140	}141	mutex_unlock(&ts->lock);142 143	return level;144}145 146static void max7301_set(struct gpio_chip *chip, unsigned offset, int value)147{148	struct max7301 *ts = gpiochip_get_data(chip);149 150	/* First 4 pins are unused in the controller */151	offset += 4;152 153	mutex_lock(&ts->lock);154 155	__max7301_set(ts, offset, value);156 157	mutex_unlock(&ts->lock);158}159 160int __max730x_probe(struct max7301 *ts)161{162	struct device *dev = ts->dev;163	struct max7301_platform_data *pdata;164	int i, ret;165 166	pdata = dev_get_platdata(dev);167 168	mutex_init(&ts->lock);169	dev_set_drvdata(dev, ts);170 171	/* Power up the chip and disable IRQ output */172	ts->write(dev, 0x04, 0x01);173 174	if (pdata) {175		ts->input_pullup_active = pdata->input_pullup_active;176		ts->chip.base = pdata->base;177	} else {178		ts->chip.base = -1;179	}180	ts->chip.label = dev->driver->name;181 182	ts->chip.direction_input = max7301_direction_input;183	ts->chip.get = max7301_get;184	ts->chip.direction_output = max7301_direction_output;185	ts->chip.set = max7301_set;186 187	ts->chip.ngpio = PIN_NUMBER;188	ts->chip.can_sleep = true;189	ts->chip.parent = dev;190	ts->chip.owner = THIS_MODULE;191 192	/*193	 * initialize pullups according to platform data and cache the194	 * register values for later use.195	 */196	for (i = 1; i < 8; i++) {197		int j;198		/*199		 * initialize port_config with "0xAA", which means200		 * input with internal pullup disabled. This is needed201		 * to avoid writing zeros (in the inner for loop),202		 * which is not allowed according to the datasheet.203		 */204		ts->port_config[i] = 0xAA;205		for (j = 0; j < 4; j++) {206			int offset = (i - 1) * 4 + j;207			ret = max7301_direction_input(&ts->chip, offset);208			if (ret)209				goto exit_destroy;210		}211	}212 213	ret = gpiochip_add_data(&ts->chip, ts);214	if (!ret)215		return ret;216 217exit_destroy:218	mutex_destroy(&ts->lock);219	return ret;220}221EXPORT_SYMBOL_GPL(__max730x_probe);222 223void __max730x_remove(struct device *dev)224{225	struct max7301 *ts = dev_get_drvdata(dev);226 227	/* Power down the chip and disable IRQ output */228	ts->write(dev, 0x04, 0x00);229	gpiochip_remove(&ts->chip);230	mutex_destroy(&ts->lock);231}232EXPORT_SYMBOL_GPL(__max730x_remove);233 234MODULE_AUTHOR("Juergen Beisert, Wolfram Sang");235MODULE_LICENSE("GPL v2");236MODULE_DESCRIPTION("MAX730x GPIO-Expanders, generic parts");237