2 * Copyright (C) 2013, Broadcom Corporation
5 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
6 * the contents of this file may not be disclosed to third parties, copied
7 * or duplicated in any form, in whole or in part, without the prior
8 * written permission of Broadcom Corporation.
10 * Functions to read/write GPIO pins
12 * $Id: bcmgpio.c 241182 2011-02-17 21:50:03Z $
23 #include <sys/ioctl.h>
29 /* directly access the gpio registers */
32 static void *gpio_sih
;
34 #include <linux_gpio.h>
38 #define GPIO_ERROR(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
40 #define GPIO_ERROR(fmt, args...)
44 #define BCMGPIO_REG_IN 0
45 #define BCMGPIO_REG_OUT 1
46 #define BCMGPIO_REG_OUTEN 2
47 #define BCMGPIO_REG_RESERVE 3
48 #define BCMGPIO_REG_RELEASE 4
50 #define BCMGPIO_MAX_FD 4
52 #define BCMGPIO_STRB_MS_TIME 50 /* 50 ms */
53 #define BCMGPIO_STRB_NS_TIME (BCMGPIO_STRB_MS_TIME * 1000 * 1000) /* in ns units */
55 /* GPIO information */
57 int connected
; /* is gpio being used? */
58 bcmgpio_dirn_t dirn
; /* direction: IN or OUT */
59 unsigned long on_time
; /* in 10 ms units */
60 unsigned long strobe_period
; /* in 10 ms units */
61 unsigned long timer_count
; /* 10 ms tick counter */
62 unsigned long strobe_count
; /* strobe counter */
63 unsigned long tot_strobes
; /* total number of strobes */
64 unsigned long orig_state
; /* original state of GPIO before blinking */
65 int strobing
; /* is gpio strobing? */
66 int *strobe_done
; /* pointer to memory which is used to signal strobe completion */
67 bcm_timer_id timer_id
; /* id of the strobe timer */
70 /* Bitmask of connected pins */
71 static unsigned long connect_mask
;
73 /* Global containing the file descriptor of the GPIO kernel drivers */
74 static int bcmgpio_fd
;
76 /* Global containing information about each GPIO pin */
77 static bcmgpio_info_t bcmgpio_info
[BCMGPIO_MAXPINS
];
79 /* Static function prototypes */
81 /* Generic functions to read/write Chip Common core's GPIO registers on the AP */
82 static int bcmgpio_drvinit(void);
83 static void bcmgpio_drvcleanup(void);
85 static void bcmgpio_toggle (unsigned long gpio_mask
);
86 static void bcmgpio_timercb (bcm_timer_id tid
, int gpio_pin
);
88 /**********************************************************************************************
89 * Functions visible to this file only
90 ******************************************************************************************** */
95 if (!(gpio_sih
= (void *)si_kattach(SI_OSH
)))
98 si_gpiosetcore(gpio_sih
);
101 bcmgpio_fd
= open("/dev/gpio", O_RDWR
);
102 if (bcmgpio_fd
== -1) {
103 GPIO_ERROR ("Failed to open /dev/gpio\n");
111 bcmgpio_drvcleanup ()
115 if (bcmgpio_fd
!= -1) {
123 bcmgpio_ioctl(int gpioreg
, unsigned int mask
, unsigned int val
)
129 value
= si_gpioin(gpio_sih
);
131 case BCMGPIO_REG_OUT
:
132 value
= si_gpioout(gpio_sih
, mask
, val
,GPIO_APP_PRIORITY
);
134 case BCMGPIO_REG_OUTEN
:
135 value
= si_gpioouten(gpio_sih
, mask
, val
,GPIO_APP_PRIORITY
);
137 case BCMGPIO_REG_RESERVE
:
138 value
= si_gpioreserve(gpio_sih
, mask
, GPIO_APP_PRIORITY
);
140 case BCMGPIO_REG_RELEASE
:
142 * releasing the gpio doesn't change the current
143 * value on the GPIO last write value
144 * persists till some one overwrites it
146 value
= si_gpiorelease(gpio_sih
, mask
, GPIO_APP_PRIORITY
);
149 GPIO_ERROR ("invalid gpioreg %d\n", gpioreg
);
155 struct gpio_ioctl gpio
;
165 case BCMGPIO_REG_OUT
:
168 case BCMGPIO_REG_OUTEN
:
169 type
= GPIO_IOC_OUTEN
;
171 case BCMGPIO_REG_RESERVE
:
172 type
= GPIO_IOC_RESERVE
;
174 case BCMGPIO_REG_RELEASE
:
175 type
= GPIO_IOC_RELEASE
;
178 GPIO_ERROR ("invalid gpioreg %d\n", gpioreg
);
181 if (ioctl(bcmgpio_fd
, type
, &gpio
) < 0) {
182 GPIO_ERROR ("invalid gpioreg %d\n", gpioreg
);
190 bcmgpio_toggle (unsigned long gpio_mask
)
192 unsigned long regval
;
194 regval
= bcmgpio_ioctl(BCMGPIO_REG_OUT
,0,0);
195 if (regval
& gpio_mask
)
196 regval
&= ~gpio_mask
;
199 bcmgpio_ioctl(BCMGPIO_REG_OUT
,gpio_mask
,regval
);
204 bcmgpio_timercb (bcm_timer_id tid
, int gpio_pin
)
206 unsigned long bitmask
;
208 if (bcmgpio_info
[gpio_pin
].strobing
) {
209 bitmask
= (unsigned long) 1 << gpio_pin
;
211 bcmgpio_info
[gpio_pin
].timer_count
++;
213 if (bcmgpio_info
[gpio_pin
].timer_count
== bcmgpio_info
[gpio_pin
].on_time
) {
214 bcmgpio_toggle (bitmask
);
216 else if (bcmgpio_info
[gpio_pin
].timer_count
> bcmgpio_info
[gpio_pin
].on_time
) {
217 if (bcmgpio_info
[gpio_pin
].timer_count
== bcmgpio_info
[gpio_pin
].strobe_period
) {
218 bcmgpio_info
[gpio_pin
].timer_count
= 0;
220 if (bcmgpio_info
[gpio_pin
].tot_strobes
> 0) {
221 bcmgpio_info
[gpio_pin
].strobe_count
++;
223 if (bcmgpio_info
[gpio_pin
].strobe_count
== bcmgpio_info
[gpio_pin
].tot_strobes
) {
224 bcmgpio_strobe_stop (gpio_pin
);
225 bcmgpio_out (bitmask
, bcmgpio_info
[gpio_pin
].orig_state
);
226 if (bcmgpio_info
[gpio_pin
].strobe_done
!= NULL
)
227 *(bcmgpio_info
[gpio_pin
].strobe_done
) = 1;
232 bcmgpio_toggle (bitmask
);
239 /**********************************************************************************************
241 ******************************************************************************************** */
243 bcmgpio_connect (int gpio_pin
, bcmgpio_dirn_t gpio_dirn
)
245 unsigned long bitmask
;
247 assert ((gpio_pin
>= 0) && (gpio_pin
<= BCMGPIO_MAXINDEX
));
248 assert ((gpio_dirn
== BCMGPIO_DIRN_IN
) || (gpio_dirn
== BCMGPIO_DIRN_OUT
));
250 if (connect_mask
== 0) {
251 if (bcmgpio_drvinit () != 0)
254 if (bcmgpio_info
[gpio_pin
].connected
)
257 bitmask
= ((unsigned long) 1 << gpio_pin
);
259 bcmgpio_info
[gpio_pin
].connected
= 1;
260 bcmgpio_info
[gpio_pin
].dirn
= gpio_dirn
;
263 bcmgpio_ioctl(BCMGPIO_REG_RESERVE
, bitmask
, bitmask
);
265 if (gpio_dirn
== BCMGPIO_DIRN_IN
)
266 bcmgpio_ioctl(BCMGPIO_REG_OUTEN
, bitmask
, 0);
268 bcmgpio_ioctl(BCMGPIO_REG_OUTEN
, bitmask
, bitmask
);
270 connect_mask
|= bitmask
;
276 * releasing the gpio doesn't change the current value on the GPIO last write value
277 * persists till some one overwrites it
280 bcmgpio_disconnect (int gpio_pin
)
282 unsigned long bitmask
;
284 assert ((gpio_pin
>= 0) && (gpio_pin
<= BCMGPIO_MAXINDEX
));
286 if (! bcmgpio_info
[gpio_pin
].connected
)
289 bitmask
= ((unsigned long) 1 << gpio_pin
);
291 if (bcmgpio_info
[gpio_pin
].strobing
)
292 bcmgpio_strobe_stop (gpio_pin
);
294 bcmgpio_info
[gpio_pin
].connected
= 0;
297 bcmgpio_ioctl(BCMGPIO_REG_RELEASE
, bitmask
, 0);
299 connect_mask
&= ~bitmask
;
301 if (connect_mask
== 0)
302 bcmgpio_drvcleanup ();
308 bcmgpio_in (unsigned long gpio_mask
, unsigned long *value
)
312 if ((gpio_mask
& connect_mask
) != gpio_mask
)
315 regin
= bcmgpio_ioctl (BCMGPIO_REG_IN
, 0, 0);
317 *value
= regin
& gpio_mask
;
324 bcmgpio_out (unsigned long gpio_mask
, unsigned long value
)
327 if ((gpio_mask
& connect_mask
) != gpio_mask
)
330 bcmgpio_ioctl (BCMGPIO_REG_OUT
, gpio_mask
, value
);
337 bcmgpio_strobe_start (int gpio_pin
, bcmgpio_strobe_t
*strobe_info
)
339 unsigned long regout
;
341 struct itimerspec its
;
343 assert ((gpio_pin
>= 0) && (gpio_pin
<= BCMGPIO_MAXINDEX
));
345 if (! strobe_info
->timer_module
) {
346 GPIO_ERROR ("bcmgpio_strobe: Invalid timer module ID\n");
350 if (! bcmgpio_info
[gpio_pin
].connected
)
353 if (bcmgpio_info
[gpio_pin
].dirn
== BCMGPIO_DIRN_IN
)
356 if (bcmgpio_info
[gpio_pin
].strobing
)
359 if ((status
= bcm_timer_create (strobe_info
->timer_module
, &bcmgpio_info
[gpio_pin
].timer_id
)) != 0) {
360 bcmgpio_info
[gpio_pin
].timer_id
= 0;
361 GPIO_ERROR ("bcmgpio_strobe: Timer creation failed with error %d\n", status
);
365 if ((status
= bcm_timer_connect (bcmgpio_info
[gpio_pin
].timer_id
, (bcm_timer_cb
) bcmgpio_timercb
, (int) gpio_pin
)) != 0) {
366 bcm_timer_delete (bcmgpio_info
[gpio_pin
].timer_id
);
367 bcmgpio_info
[gpio_pin
].timer_id
= 0;
368 GPIO_ERROR ("bcmgpio_strobe: Timer connect failed with error %d\n", status
);
372 its
.it_interval
.tv_sec
= 0;
373 its
.it_interval
.tv_nsec
= BCMGPIO_STRB_NS_TIME
;
374 its
.it_value
.tv_sec
= 0;
375 its
.it_value
.tv_nsec
= BCMGPIO_STRB_NS_TIME
;
377 if ((status
= bcm_timer_settime (bcmgpio_info
[gpio_pin
].timer_id
, &its
)) != 0) {
378 bcm_timer_delete (bcmgpio_info
[gpio_pin
].timer_id
);
379 bcmgpio_info
[gpio_pin
].timer_id
= 0;
380 GPIO_ERROR ("bcmgpio_strobe: Timer set failed with error %d\n", status
);
384 regout
= bcmgpio_ioctl (BCMGPIO_REG_OUT
, 0, 0);
386 bcmgpio_info
[gpio_pin
].orig_state
= regout
& ((unsigned long) 1 << gpio_pin
);
388 bcmgpio_info
[gpio_pin
].strobe_period
= strobe_info
->strobe_period_in_ms
/ BCMGPIO_STRB_MS_TIME
;
389 bcmgpio_info
[gpio_pin
].on_time
=
390 (strobe_info
->duty_percent
* bcmgpio_info
[gpio_pin
].strobe_period
) / 100;
391 bcmgpio_info
[gpio_pin
].tot_strobes
= strobe_info
->num_strobes
;
392 bcmgpio_info
[gpio_pin
].strobe_count
= 0;
393 bcmgpio_info
[gpio_pin
].timer_count
= 0;
395 bcmgpio_info
[gpio_pin
].strobing
= 1;
397 bcmgpio_info
[gpio_pin
].strobe_done
= strobe_info
->strobe_done
;
398 if (bcmgpio_info
[gpio_pin
].strobe_done
!= NULL
)
399 *(bcmgpio_info
[gpio_pin
].strobe_done
) = 0;
406 bcmgpio_strobe_stop (int gpio_pin
)
408 assert ((gpio_pin
>= 0) && (gpio_pin
<= BCMGPIO_MAXINDEX
));
410 if (! bcmgpio_info
[gpio_pin
].connected
)
413 if (bcmgpio_info
[gpio_pin
].strobing
) {
414 bcmgpio_info
[gpio_pin
].strobing
= 0;
416 if (bcmgpio_info
[gpio_pin
].timer_id
!= 0) {
417 bcm_timer_delete (bcmgpio_info
[gpio_pin
].timer_id
);
418 bcmgpio_info
[gpio_pin
].timer_id
= 0;
425 /* Search for token in comma separated token-string */
427 findmatch(char *string
, char *name
)
433 while ((c
= strchr(string
, ',')) != NULL
) {
434 if (len
== (uint
)(c
- string
) && !strncmp(string
, name
, len
))
439 return (!strcmp(string
, name
));
443 /* Return gpio pin number assigned to the named pin */
445 * Variable should be in format:
447 * gpio<N>=pin_name,pin_name
449 * This format allows multiple features to share the gpio with
450 * mutual understanding.
452 * 'def_pin' is returned if a specific gpio is not defined for the requested functionality
453 * and if def_pin is not used by others.
456 bcmgpio_getpin(char *pin_name
, uint def_pin
)
458 char name
[] = "gpioXXXX";
462 /* Go thru all possibilities till a match in pin name */
463 for (pin
= 0; pin
< BCMGPIO_MAXPINS
; pin
++) {
464 sprintf(name
, "gpio%d", pin
);
465 val
= nvram_get(name
);
466 if (val
&& findmatch(val
, pin_name
))
470 if (def_pin
!= BCMGPIO_UNDEFINED
) {
471 /* make sure the default pin is not used by someone else */
472 sprintf(name
, "gpio%d", def_pin
);
475 def_pin
= BCMGPIO_UNDEFINED
;