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
pca9698_read40(u8 addr,u8 offset,u8 * buffer)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
pca9698_write40(u8 addr,u8 offset,u8 * buffer)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
pca9698_set_bit(unsigned gpio,u8 * buffer,unsigned value)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
pca9698_request(unsigned gpio,const char * label)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
pca9698_free(unsigned gpio)62042f9f10SDirk Eibach void pca9698_free(unsigned gpio)
63042f9f10SDirk Eibach {
64042f9f10SDirk Eibach }
65042f9f10SDirk Eibach
pca9698_direction_input(u8 addr,unsigned gpio)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
pca9698_direction_output(u8 addr,unsigned gpio,int value)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
pca9698_get_value(u8 addr,unsigned gpio)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
pca9698_set_value(u8 addr,unsigned gpio,int value)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