xref: /openbmc/u-boot/drivers/gpio/pca9698.c (revision 1221ce45)
1486cad03SDirk Eibach /*
2486cad03SDirk Eibach  * (C) Copyright 2011
3486cad03SDirk Eibach  * Dirk Eibach,  Guntermann & Drunck GmbH, eibach@gdsys.de
4486cad03SDirk Eibach  *
51a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
6486cad03SDirk Eibach  */
7486cad03SDirk Eibach 
8486cad03SDirk Eibach /*
9486cad03SDirk Eibach  * Driver for NXP's pca9698 40 bit I2C gpio expander
10486cad03SDirk Eibach  */
11486cad03SDirk Eibach 
12486cad03SDirk Eibach #include <common.h>
13486cad03SDirk Eibach #include <i2c.h>
14*1221ce45SMasahiro Yamada #include <linux/errno.h>
15486cad03SDirk Eibach #include <pca9698.h>
16486cad03SDirk Eibach 
17486cad03SDirk Eibach /*
18486cad03SDirk Eibach  * The pca9698 registers
19486cad03SDirk Eibach  */
20486cad03SDirk Eibach 
21486cad03SDirk Eibach #define PCA9698_REG_INPUT		0x00
22486cad03SDirk Eibach #define PCA9698_REG_OUTPUT		0x08
23486cad03SDirk Eibach #define PCA9698_REG_POLARITY		0x10
24486cad03SDirk Eibach #define PCA9698_REG_CONFIG		0x18
25486cad03SDirk Eibach 
26486cad03SDirk Eibach #define PCA9698_BUFFER_SIZE		5
27042f9f10SDirk Eibach #define PCA9698_GPIO_COUNT		40
28486cad03SDirk Eibach 
29042f9f10SDirk Eibach static int pca9698_read40(u8 addr, u8 offset, u8 *buffer)
30486cad03SDirk Eibach {
31486cad03SDirk Eibach 	u8 command = offset | 0x80;  /* autoincrement */
32486cad03SDirk Eibach 
33042f9f10SDirk Eibach 	return i2c_read(addr, command, 1, buffer, PCA9698_BUFFER_SIZE);
34486cad03SDirk Eibach }
35486cad03SDirk Eibach 
36042f9f10SDirk Eibach static int pca9698_write40(u8 addr, u8 offset, u8 *buffer)
37486cad03SDirk Eibach {
38486cad03SDirk Eibach 	u8 command = offset | 0x80;  /* autoincrement */
39486cad03SDirk Eibach 
40042f9f10SDirk Eibach 	return i2c_write(addr, command, 1, buffer, PCA9698_BUFFER_SIZE);
41486cad03SDirk Eibach }
42486cad03SDirk Eibach 
43486cad03SDirk Eibach static void pca9698_set_bit(unsigned gpio, u8 *buffer, unsigned value)
44486cad03SDirk Eibach {
45486cad03SDirk Eibach 	unsigned byte = gpio / 8;
46486cad03SDirk Eibach 	unsigned bit = gpio % 8;
47486cad03SDirk Eibach 
48486cad03SDirk Eibach 	if (value)
49486cad03SDirk Eibach 		buffer[byte] |= (1 << bit);
50486cad03SDirk Eibach 	else
51486cad03SDirk Eibach 		buffer[byte] &= ~(1 << bit);
52486cad03SDirk Eibach }
53486cad03SDirk Eibach 
54042f9f10SDirk Eibach int pca9698_request(unsigned gpio, const char *label)
55042f9f10SDirk Eibach {
56042f9f10SDirk Eibach 	if (gpio >= PCA9698_GPIO_COUNT)
57042f9f10SDirk Eibach 		return -EINVAL;
58042f9f10SDirk Eibach 
59042f9f10SDirk Eibach 	return 0;
60042f9f10SDirk Eibach }
61042f9f10SDirk Eibach 
62042f9f10SDirk Eibach void pca9698_free(unsigned gpio)
63042f9f10SDirk Eibach {
64042f9f10SDirk Eibach }
65042f9f10SDirk Eibach 
66042f9f10SDirk Eibach int pca9698_direction_input(u8 addr, unsigned gpio)
67486cad03SDirk Eibach {
68486cad03SDirk Eibach 	u8 data[PCA9698_BUFFER_SIZE];
69486cad03SDirk Eibach 	int res;
70486cad03SDirk Eibach 
71042f9f10SDirk Eibach 	res = pca9698_read40(addr, PCA9698_REG_CONFIG, data);
72486cad03SDirk Eibach 	if (res)
73486cad03SDirk Eibach 		return res;
74486cad03SDirk Eibach 
75042f9f10SDirk Eibach 	pca9698_set_bit(gpio, data, 1);
76042f9f10SDirk Eibach 
77042f9f10SDirk Eibach 	return pca9698_write40(addr, PCA9698_REG_CONFIG, data);
78486cad03SDirk Eibach }
79486cad03SDirk Eibach 
80042f9f10SDirk Eibach int pca9698_direction_output(u8 addr, unsigned gpio, int value)
81486cad03SDirk Eibach {
82486cad03SDirk Eibach 	u8 data[PCA9698_BUFFER_SIZE];
83486cad03SDirk Eibach 	int res;
84486cad03SDirk Eibach 
85042f9f10SDirk Eibach 	res = pca9698_set_value(addr, gpio, value);
86486cad03SDirk Eibach 	if (res)
87486cad03SDirk Eibach 		return res;
88486cad03SDirk Eibach 
89042f9f10SDirk Eibach 	res = pca9698_read40(addr, PCA9698_REG_CONFIG, data);
90042f9f10SDirk Eibach 	if (res)
91042f9f10SDirk Eibach 		return res;
92042f9f10SDirk Eibach 
93042f9f10SDirk Eibach 	pca9698_set_bit(gpio, data, 0);
94042f9f10SDirk Eibach 
95042f9f10SDirk Eibach 	return pca9698_write40(addr, PCA9698_REG_CONFIG, data);
96486cad03SDirk Eibach }
97486cad03SDirk Eibach 
98042f9f10SDirk Eibach int pca9698_get_value(u8 addr, unsigned gpio)
99486cad03SDirk Eibach {
100042f9f10SDirk Eibach 	unsigned config_byte = gpio / 8;
101042f9f10SDirk Eibach 	unsigned config_bit = gpio % 8;
102486cad03SDirk Eibach 	unsigned value;
103486cad03SDirk Eibach 	u8 data[PCA9698_BUFFER_SIZE];
104486cad03SDirk Eibach 	int res;
105486cad03SDirk Eibach 
106042f9f10SDirk Eibach 	res = pca9698_read40(addr, PCA9698_REG_INPUT, data);
107486cad03SDirk Eibach 	if (res)
108486cad03SDirk Eibach 		return -1;
109486cad03SDirk Eibach 
110486cad03SDirk Eibach 	value = data[config_byte] & (1 << config_bit);
111486cad03SDirk Eibach 
112486cad03SDirk Eibach 	return !!value;
113486cad03SDirk Eibach }
114486cad03SDirk Eibach 
115042f9f10SDirk Eibach int pca9698_set_value(u8 addr, unsigned gpio, int value)
116486cad03SDirk Eibach {
117486cad03SDirk Eibach 	u8 data[PCA9698_BUFFER_SIZE];
118486cad03SDirk Eibach 	int res;
119486cad03SDirk Eibach 
120042f9f10SDirk Eibach 	res = pca9698_read40(addr, PCA9698_REG_OUTPUT, data);
121486cad03SDirk Eibach 	if (res)
122486cad03SDirk Eibach 		return res;
123486cad03SDirk Eibach 
124042f9f10SDirk Eibach 	pca9698_set_bit(gpio, data, value);
125042f9f10SDirk Eibach 
126042f9f10SDirk Eibach 	return pca9698_write40(addr, PCA9698_REG_OUTPUT, data);
127486cad03SDirk Eibach }
128