dnsmasq: stay close as possible to master branch
[tomato.git] / release / src-rt-6.x.4708 / router / libbcm / bcmgpio.c
blob4480af8f48294aa1a541d6671bf1dc22b7ffb775
1 /*
2 * Copyright (C) 2013, Broadcom Corporation
3 * All Rights Reserved.
4 *
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 $
15 #include <typedefs.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <fcntl.h>
20 #include <unistd.h>
21 #include <time.h>
22 #include <assert.h>
23 #include <sys/ioctl.h>
25 #include <bcmnvram.h>
26 #include <bcmgpio.h>
28 #if defined(__ECOS)
29 /* directly access the gpio registers */
30 #include <bcmutils.h>
31 #include <siutils.h>
32 static void *gpio_sih;
33 #else
34 #include <linux_gpio.h>
35 #endif
37 #ifdef BCMDBG
38 #define GPIO_ERROR(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
39 #else
40 #define GPIO_ERROR(fmt, args...)
41 #endif
43 /* GPIO registers */
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 */
56 typedef struct {
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 */
68 } bcmgpio_info_t;
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 ******************************************************************************************** */
91 static int
92 bcmgpio_drvinit ()
94 #if defined(__ECOS)
95 if (!(gpio_sih = (void *)si_kattach(SI_OSH)))
96 return -1;
97 bcmgpio_fd = 1;
98 si_gpiosetcore(gpio_sih);
99 return 0;
100 #else
101 bcmgpio_fd = open("/dev/gpio", O_RDWR);
102 if (bcmgpio_fd == -1) {
103 GPIO_ERROR ("Failed to open /dev/gpio\n");
104 return -1;
106 return 0;
107 #endif
110 static void
111 bcmgpio_drvcleanup ()
113 #if defined(__ECOS)
114 #else
115 if (bcmgpio_fd!= -1) {
116 close (bcmgpio_fd);
117 bcmgpio_fd = -1;
119 #endif
122 static int
123 bcmgpio_ioctl(int gpioreg, unsigned int mask , unsigned int val)
125 #if defined(__ECOS)
126 int value;
127 switch (gpioreg) {
128 case BCMGPIO_REG_IN:
129 value = si_gpioin(gpio_sih);
130 break;
131 case BCMGPIO_REG_OUT:
132 value = si_gpioout(gpio_sih, mask, val,GPIO_APP_PRIORITY);
133 break;
134 case BCMGPIO_REG_OUTEN:
135 value = si_gpioouten(gpio_sih, mask, val,GPIO_APP_PRIORITY);
136 break;
137 case BCMGPIO_REG_RESERVE:
138 value = si_gpioreserve(gpio_sih, mask, GPIO_APP_PRIORITY);
139 break;
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);
147 break;
148 default:
149 GPIO_ERROR ("invalid gpioreg %d\n", gpioreg);
150 value = -1;
151 break;
153 return value;
154 #else
155 struct gpio_ioctl gpio;
156 int type;
158 gpio.val = val;
159 gpio.mask = mask;
161 switch (gpioreg) {
162 case BCMGPIO_REG_IN:
163 type = GPIO_IOC_IN;
164 break;
165 case BCMGPIO_REG_OUT:
166 type = GPIO_IOC_OUT;
167 break;
168 case BCMGPIO_REG_OUTEN:
169 type = GPIO_IOC_OUTEN;
170 break;
171 case BCMGPIO_REG_RESERVE:
172 type = GPIO_IOC_RESERVE;
173 break;
174 case BCMGPIO_REG_RELEASE:
175 type = GPIO_IOC_RELEASE;
176 break;
177 default:
178 GPIO_ERROR ("invalid gpioreg %d\n", gpioreg);
179 return -1;
181 if (ioctl(bcmgpio_fd, type, &gpio) < 0) {
182 GPIO_ERROR ("invalid gpioreg %d\n", gpioreg);
183 return -1;
185 return (gpio.val);
186 #endif
189 static void
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;
197 else
198 regval |= gpio_mask;
199 bcmgpio_ioctl(BCMGPIO_REG_OUT,gpio_mask,regval);
203 static void
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;
228 return;
232 bcmgpio_toggle (bitmask);
239 /**********************************************************************************************
240 * GPIO functions
241 ******************************************************************************************** */
242 int
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)
252 return -1;
254 if (bcmgpio_info[gpio_pin].connected)
255 return -1;
257 bitmask = ((unsigned long) 1 << gpio_pin);
259 bcmgpio_info[gpio_pin].connected = 1;
260 bcmgpio_info[gpio_pin].dirn = gpio_dirn;
262 /* reserve the pin*/
263 bcmgpio_ioctl(BCMGPIO_REG_RESERVE, bitmask, bitmask);
265 if (gpio_dirn == BCMGPIO_DIRN_IN)
266 bcmgpio_ioctl(BCMGPIO_REG_OUTEN, bitmask, 0);
267 else
268 bcmgpio_ioctl(BCMGPIO_REG_OUTEN, bitmask, bitmask);
270 connect_mask |= bitmask;
272 return 0;
276 * releasing the gpio doesn't change the current value on the GPIO last write value
277 * persists till some one overwrites it
279 int
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)
287 return -1;
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;
296 /* release the pin*/
297 bcmgpio_ioctl(BCMGPIO_REG_RELEASE, bitmask, 0);
299 connect_mask &= ~bitmask;
301 if (connect_mask == 0)
302 bcmgpio_drvcleanup ();
304 return 0;
307 int
308 bcmgpio_in (unsigned long gpio_mask, unsigned long *value)
310 unsigned long regin;
312 if ((gpio_mask & connect_mask) != gpio_mask)
313 return -1;
315 regin = bcmgpio_ioctl (BCMGPIO_REG_IN, 0, 0);
317 *value = regin & gpio_mask;
319 return 0;
323 int
324 bcmgpio_out (unsigned long gpio_mask, unsigned long value)
327 if ((gpio_mask & connect_mask) != gpio_mask)
328 return -1;
330 bcmgpio_ioctl (BCMGPIO_REG_OUT, gpio_mask, value);
332 return 0;
336 int
337 bcmgpio_strobe_start (int gpio_pin, bcmgpio_strobe_t *strobe_info)
339 unsigned long regout;
340 int status;
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");
347 return -1;
350 if (! bcmgpio_info[gpio_pin].connected)
351 return -1;
353 if (bcmgpio_info[gpio_pin].dirn == BCMGPIO_DIRN_IN)
354 return -1;
356 if (bcmgpio_info[gpio_pin].strobing)
357 return 0;
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);
362 return -1;
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);
369 return -1;
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);
381 return -1;
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;
401 return 0;
406 bcmgpio_strobe_stop (int gpio_pin)
408 assert ((gpio_pin >= 0) && (gpio_pin <= BCMGPIO_MAXINDEX));
410 if (! bcmgpio_info[gpio_pin].connected)
411 return -1;
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;
422 return 0;
425 /* Search for token in comma separated token-string */
426 static inline int
427 findmatch(char *string, char *name)
429 uint len;
430 char *c;
432 len = strlen(name);
433 while ((c = strchr(string, ',')) != NULL) {
434 if (len == (uint)(c - string) && !strncmp(string, name, len))
435 return 1;
436 string = c + 1;
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";
459 char *val;
460 uint pin;
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))
467 return pin;
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);
473 if (nvram_get(name))
475 def_pin = BCMGPIO_UNDEFINED;
479 return def_pin;