1 /*!
2  * \file      gpio-ioe.h
3  *
4  * \brief     IO expander driver implementation (based on the sx1509)
5  *
6  * \copyright Revised BSD License, see section \ref LICENSE.
7  *
8  * \code
9  *                ______                              _
10  *               / _____)             _              | |
11  *              ( (____  _____ ____ _| |_ _____  ____| |__
12  *               \____ \| ___ |    (_   _) ___ |/ ___)  _ \
13  *               _____) ) ____| | | || |_| ____( (___| | | |
14  *              (______/|_____)_|_|_| \__)_____)\____)_| |_|
15  *              (C)2013-2017 Semtech
16  *
17  * \endcode
18  *
19  * \author    Miguel Luis ( Semtech )
20  *
21  * \author    Gregory Cristian ( Semtech )
22  */
23 #include <stdlib.h>
24 #include <stdbool.h>
25 #include "gpio-ioe.h"
26 #include "sx1509.h"
27 
28 static Gpio_t *GpioIrq[16];
29 
GpioIoeInit(Gpio_t * obj,PinNames pin,PinModes mode,PinConfigs config,PinTypes type,uint32_t value)30 void GpioIoeInit( Gpio_t *obj, PinNames pin, PinModes mode,  PinConfigs config, PinTypes type, uint32_t value )
31 {
32     uint8_t regAdd = 0;
33     uint8_t regVal = 0;
34     uint8_t tempVal = 0;
35 
36     SX1509Init( );
37 
38     obj->pin = pin;
39     obj->pinIndex = ( 0x01 << pin % 16 );
40 
41     if( ( obj->pin % 16 ) > 0x07 )
42     {
43         regAdd = RegDirB;
44         obj->pinIndex = ( obj->pinIndex >> 8 ) & 0x00FF;
45     }
46     else
47     {
48         regAdd = RegDirA;
49         obj->pinIndex = ( obj->pinIndex ) & 0x00FF;
50     }
51 
52     SX1509Read( regAdd, &regVal );
53 
54     if( mode == PIN_OUTPUT )
55     {
56         regVal = regVal & ~obj->pinIndex;
57     }
58     else
59     {
60         regVal = regVal | obj->pinIndex;
61     }
62     SX1509Write( regAdd, regVal );
63 
64 
65     if( ( obj->pin % 16 ) > 0x07 )
66     {
67         SX1509Read( RegOpenDrainB, &tempVal );
68         if( config == PIN_OPEN_DRAIN )
69         {
70             SX1509Write( RegOpenDrainB, tempVal | obj->pinIndex );
71         }
72         else
73         {
74             SX1509Write( RegOpenDrainB, tempVal & ~obj->pinIndex );
75         }
76         regAdd = RegDataB;
77     }
78     else
79     {
80         SX1509Read( RegOpenDrainA, &tempVal );
81         if( config == PIN_OPEN_DRAIN )
82         {
83             SX1509Write( RegOpenDrainA, tempVal | obj->pinIndex );
84         }
85         else
86         {
87             SX1509Write( RegOpenDrainA, tempVal & ~obj->pinIndex );
88         }
89         regAdd = RegDataA;
90     }
91 
92     SX1509Read( regAdd, &regVal );
93 
94     // Sets initial output value
95     if( value == 0 )
96     {
97         regVal = regVal & ~obj->pinIndex;
98     }
99     else
100     {
101         regVal = regVal | obj->pinIndex;
102     }
103     SX1509Write( regAdd, regVal );
104 }
105 
GpioIoeSetContext(Gpio_t * obj,void * context)106 void GpioIoeSetContext( Gpio_t *obj, void* context )
107 {
108     obj->Context = context;
109 }
110 
GpioIoeSetInterrupt(Gpio_t * obj,IrqModes irqMode,IrqPriorities irqPriority,GpioIrqHandler * irqHandler)111 void GpioIoeSetInterrupt( Gpio_t *obj, IrqModes irqMode, IrqPriorities irqPriority, GpioIrqHandler *irqHandler )
112 {
113     uint8_t regAdd = 0;
114     uint8_t regVal = 0;
115     uint8_t i = 0;
116     uint16_t tempVal = 0;
117     uint8_t val = 0;
118 
119     if( irqHandler == NULL )
120     {
121         return;
122     }
123 
124     obj->IrqHandler = irqHandler;
125 
126     if( ( obj->pin % 16 ) > 0x07 )
127     {
128         regAdd = RegInterruptMaskB;
129     }
130     else
131     {
132         regAdd = RegInterruptMaskA;
133     }
134 
135     SX1509Read( regAdd, &regVal );
136 
137     regVal = regVal & ~( obj->pinIndex );
138     SX1509Write( regAdd, regVal );
139 
140     if( irqMode == IRQ_RISING_EDGE )
141     {
142         val = 0x01;
143     }
144     else if( irqMode == IRQ_FALLING_EDGE )
145     {
146         val = 0x02;
147     }
148     else // IRQ_RISING_FALLING_EDGE
149     {
150         val = 0x03;
151     }
152 
153     tempVal = 0x0000;
154     i = 0;
155     while( tempVal != obj->pinIndex )
156     {
157         tempVal = 0x01 << i;
158         i++;
159     }
160 
161     if( i < 4 )
162     {
163         regAdd = RegSenseLowA;
164     }
165     else if( i < 9 )
166     {
167         regAdd = RegSenseHighA;
168     }
169     else if( i < 13 )
170     {
171         regAdd = RegSenseLowB;
172     }
173     else
174     {
175         regAdd = RegSenseHighB;
176     }
177     SX1509Read( regAdd, &regVal );
178 
179     switch( i )
180     {
181         case 1:
182         case 5:
183         case 9:
184         case 13:
185             regVal = ( regVal & REG_SENSE_PIN_MASK_1 ) | val;
186             break;
187 
188         case 2:
189         case 6:
190         case 10:
191         case 14:
192             regVal = ( regVal & REG_SENSE_PIN_MASK_2 ) | ( val << 2 );
193             break;
194 
195         case 3:
196         case 7:
197         case 11:
198         case 15:
199             regVal = ( regVal & REG_SENSE_PIN_MASK_3 ) | ( val << 4 );
200             break;
201 
202         case 4:
203         case 8:
204         case 12:
205         case 16:
206             regVal = ( regVal & REG_SENSE_PIN_MASK_4 ) | ( val << 6 );
207             break;
208     }
209     SX1509Write( regAdd, regVal );
210 
211     GpioIrq[obj->pin & 0x0F] = obj;
212 }
213 
GpioIoeRemoveInterrupt(Gpio_t * obj)214 void GpioIoeRemoveInterrupt( Gpio_t *obj )
215 {
216     uint8_t regAdd = 0;
217     uint8_t regVal = 0;
218     uint8_t i = 0;
219     uint16_t tempVal = 0;
220 
221     // Clear callback before changing pin mode
222     GpioIrq[obj->pin & 0x0F] = NULL;
223 
224     if( ( obj->pin % 16 ) > 0x07 )
225     {
226         regAdd = RegInterruptMaskB;
227     }
228     else
229     {
230         regAdd = RegInterruptMaskA;
231     }
232 
233     SX1509Read( regAdd, &regVal );
234 
235     regVal = regVal | obj->pinIndex;
236     SX1509Write( regAdd, regVal );
237 
238     tempVal = 0x0000;
239     i = 0;
240     while( tempVal != obj->pinIndex )
241     {
242         tempVal = 0x01 << i;
243         i++;
244     }
245 
246     if( i < 4 )
247     {
248         regAdd = RegSenseLowA;
249     }
250     else if( i < 9 )
251     {
252         regAdd = RegSenseHighA;
253     }
254     else if( i < 13 )
255     {
256         regAdd = RegSenseLowB;
257     }
258     else
259     {
260         regAdd = RegSenseHighB;
261     }
262     SX1509Read( regAdd, &regVal );
263 
264     switch( i )
265     {
266         case 1:
267         case 5:
268         case 9:
269         case 13:
270             regVal = ( regVal & REG_SENSE_PIN_MASK_1 );
271             break;
272 
273         case 2:
274         case 6:
275         case 10:
276         case 14:
277             regVal = ( regVal & REG_SENSE_PIN_MASK_2 );
278             break;
279 
280         case 3:
281         case 7:
282         case 11:
283         case 15:
284             regVal = ( regVal & REG_SENSE_PIN_MASK_3 );
285             break;
286 
287         case 4:
288         case 8:
289         case 12:
290         case 16:
291             regVal = ( regVal & REG_SENSE_PIN_MASK_4 );
292             break;
293     }
294     SX1509Write( regAdd, regVal );
295 }
296 
GpioIoeWrite(Gpio_t * obj,uint32_t value)297 void GpioIoeWrite( Gpio_t *obj, uint32_t value )
298 {
299     uint8_t regAdd = 0;
300     uint8_t regVal = 0;
301 
302     if( ( obj->pin % 16 ) > 0x07 )
303     {
304         regAdd = RegDataB;
305     }
306     else
307     {
308         regAdd = RegDataA;
309     }
310 
311     SX1509Read( regAdd, &regVal );
312 
313     // Sets initial output value
314     if( value == 0 )
315     {
316         regVal = regVal & ~obj->pinIndex;
317     }
318     else
319     {
320         regVal = regVal | obj->pinIndex;
321     }
322     SX1509Write( regAdd, regVal );
323 }
324 
GpioIoeToggle(Gpio_t * obj)325 void GpioIoeToggle( Gpio_t *obj )
326 {
327     GpioIoeWrite( obj, GpioIoeRead( obj ) ^ 1 );
328 }
329 
GpioIoeRead(Gpio_t * obj)330 uint32_t GpioIoeRead( Gpio_t *obj )
331 {
332     uint8_t regAdd = 0;
333     uint8_t regVal = 0;
334 
335     if( ( obj->pin % 16 ) > 0x07 )
336     {
337         regAdd = RegDataB;
338     }
339     else
340     {
341         regAdd = RegDataA;
342     }
343 
344     SX1509Read( regAdd, &regVal );
345 
346     if( ( regVal & obj->pinIndex ) == 0x00 )
347     {
348         return 0;
349     }
350     else
351     {
352         return 1;
353     }
354 }
355 
GpioIoeInterruptHandler(void)356 void GpioIoeInterruptHandler( void )
357 {
358     uint8_t irqLsb = 0;
359     uint8_t irqMsb = 0;
360     uint16_t irq = 0;
361 
362     SX1509Read( RegInterruptSourceA, &irqLsb );
363     SX1509Read( RegInterruptSourceB, &irqMsb );
364 
365     irq = ( irqMsb << 8 ) | irqLsb;
366     if( irq != 0x00 )
367     {
368         for( uint16_t mask = 0x0001, pinIndex = 0; mask != 0x000; mask <<= 1, pinIndex++ )
369         {
370             if( ( irq & mask ) != 0 )
371             {
372                 if( ( GpioIrq[pinIndex] != NULL ) && ( GpioIrq[pinIndex]->IrqHandler != NULL ) )
373                 {
374                     GpioIrq[pinIndex]->IrqHandler( GpioIrq[pinIndex]->Context );
375                 }
376             }
377         }
378     }
379 
380     // Clear all interrupts/events
381     SX1509Write( RegInterruptSourceA, 0xFF );
382     SX1509Write( RegInterruptSourceB, 0xFF );
383     SX1509Write( RegEventStatusB, 0xFF );
384     SX1509Write( RegEventStatusA, 0xFF );
385 }
386