blob: 7b4641a2cb84b67e55ad5dbe6fefd05f138f2c69 [file] [log] [blame]
Ben Collinsfaa4fd22010-06-17 13:27:26 -04001/*
Hans Verkuildcae5da2013-03-25 05:35:17 -03002 * Copyright (C) 2010-2013 Bluecherry, LLC <http://www.bluecherrydvr.com>
3 *
4 * Original author:
5 * Ben Collins <bcollins@ubuntu.com>
6 *
7 * Additional work by:
8 * John Brooks <john.brooks@bluecherry.net>
Ben Collinsfaa4fd22010-06-17 13:27:26 -04009 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
Ben Collinsfaa4fd22010-06-17 13:27:26 -040019 */
20
21#include <linux/kernel.h>
22#include <linux/fs.h>
Hans Verkuildcae5da2013-03-25 05:35:17 -030023#include <linux/delay.h>
24#include <linux/uaccess.h>
25
Krzysztof Hałasaae69b222011-02-11 13:36:27 +010026#include "solo6x10.h"
Ben Collinsfaa4fd22010-06-17 13:27:26 -040027
Krzysztof Hałasadecebab2011-02-11 13:38:20 +010028static void solo_gpio_mode(struct solo_dev *solo_dev,
Ben Collinsfaa4fd22010-06-17 13:27:26 -040029 unsigned int port_mask, unsigned int mode)
30{
31 int port;
32 unsigned int ret;
33
34 ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0);
35
36 /* To set gpio */
37 for (port = 0; port < 16; port++) {
38 if (!((1 << port) & port_mask))
39 continue;
40
41 ret &= (~(3 << (port << 1)));
42 ret |= ((mode & 3) << (port << 1));
43 }
44
45 solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret);
46
47 /* To set extended gpio - sensor */
48 ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1);
49
50 for (port = 0; port < 16; port++) {
51 if (!((1 << (port + 16)) & port_mask))
52 continue;
53
54 if (!mode)
55 ret &= ~(1 << port);
56 else
57 ret |= 1 << port;
58 }
59
Anton Sviridenkod3202d12017-08-02 10:17:02 -040060 /* Enable GPIO[31:16] */
61 ret |= 0xffff0000;
62
Ben Collinsfaa4fd22010-06-17 13:27:26 -040063 solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret);
64}
65
Krzysztof Hałasadecebab2011-02-11 13:38:20 +010066static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value)
Ben Collinsfaa4fd22010-06-17 13:27:26 -040067{
68 solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT,
69 solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value);
70}
71
Krzysztof Hałasadecebab2011-02-11 13:38:20 +010072static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value)
Ben Collinsfaa4fd22010-06-17 13:27:26 -040073{
74 solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT,
75 solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value);
76}
77
Krzysztof Hałasadecebab2011-02-11 13:38:20 +010078static void solo_gpio_config(struct solo_dev *solo_dev)
Ben Collinsfaa4fd22010-06-17 13:27:26 -040079{
80 /* Video reset */
81 solo_gpio_mode(solo_dev, 0x30, 1);
82 solo_gpio_clear(solo_dev, 0x30);
83 udelay(100);
84 solo_gpio_set(solo_dev, 0x30);
85 udelay(100);
86
87 /* Warning: Don't touch the next line unless you're sure of what
88 * you're doing: first four gpio [0-3] are used for video. */
89 solo_gpio_mode(solo_dev, 0x0f, 2);
90
91 /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */
92 solo_gpio_mode(solo_dev, 0xff00, 1);
93
94 /* Initially set relay status to 0 */
95 solo_gpio_clear(solo_dev, 0xff00);
Anton Sviridenkod3202d12017-08-02 10:17:02 -040096
97 /* Set input pins direction */
98 solo_gpio_mode(solo_dev, 0xffff0000, 0);
Ben Collinsfaa4fd22010-06-17 13:27:26 -040099}
100
Anton Sviridenkod3202d12017-08-02 10:17:02 -0400101#ifdef CONFIG_GPIOLIB
102/* Pins 0-7 are not exported, because it seems from code above they are
103 * used for internal purposes. So offset 0 corresponds to pin 8, therefore
104 * offsets 0-7 are relay GPIOs, 8-23 - input GPIOs.
105 */
106static int solo_gpiochip_get_direction(struct gpio_chip *chip,
107 unsigned int offset)
108{
109 int ret, mode;
110 struct solo_dev *solo_dev = gpiochip_get_data(chip);
111
112 if (offset < 8) {
113 ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0);
114 mode = 3 & (ret >> ((offset + 8) * 2));
115 } else {
116 ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1);
117 mode = 1 & (ret >> (offset - 8));
118 }
119
120 if (!mode)
121 return 1;
122 else if (mode == 1)
123 return 0;
124
125 return -1;
126}
127
128static int solo_gpiochip_direction_input(struct gpio_chip *chip,
129 unsigned int offset)
130{
131 return -1;
132}
133
134static int solo_gpiochip_direction_output(struct gpio_chip *chip,
135 unsigned int offset, int value)
136{
137 return -1;
138}
139
140static int solo_gpiochip_get(struct gpio_chip *chip,
141 unsigned int offset)
142{
143 int ret;
144 struct solo_dev *solo_dev = gpiochip_get_data(chip);
145
146 ret = solo_reg_read(solo_dev, SOLO_GPIO_DATA_IN);
147
148 return 1 & (ret >> (offset + 8));
149}
150
151static void solo_gpiochip_set(struct gpio_chip *chip,
152 unsigned int offset, int value)
153{
154 struct solo_dev *solo_dev = gpiochip_get_data(chip);
155
156 if (value)
157 solo_gpio_set(solo_dev, 1 << (offset + 8));
158 else
159 solo_gpio_clear(solo_dev, 1 << (offset + 8));
160}
161#endif
162
Krzysztof Hałasadecebab2011-02-11 13:38:20 +0100163int solo_gpio_init(struct solo_dev *solo_dev)
Ben Collinsfaa4fd22010-06-17 13:27:26 -0400164{
Arnd Bergmannd259f912017-09-15 15:52:04 -0400165#ifdef CONFIG_GPIOLIB
Anton Sviridenkod3202d12017-08-02 10:17:02 -0400166 int ret;
Arnd Bergmannd259f912017-09-15 15:52:04 -0400167#endif
Anton Sviridenkod3202d12017-08-02 10:17:02 -0400168
Ben Collinsf62de9b2010-11-04 22:51:17 -0400169 solo_gpio_config(solo_dev);
Anton Sviridenkod3202d12017-08-02 10:17:02 -0400170#ifdef CONFIG_GPIOLIB
171 solo_dev->gpio_dev.label = SOLO6X10_NAME"_gpio";
172 solo_dev->gpio_dev.parent = &solo_dev->pdev->dev;
173 solo_dev->gpio_dev.owner = THIS_MODULE;
174 solo_dev->gpio_dev.base = -1;
175 solo_dev->gpio_dev.ngpio = 24;
176 solo_dev->gpio_dev.can_sleep = 0;
177
178 solo_dev->gpio_dev.get_direction = solo_gpiochip_get_direction;
179 solo_dev->gpio_dev.direction_input = solo_gpiochip_direction_input;
180 solo_dev->gpio_dev.direction_output = solo_gpiochip_direction_output;
181 solo_dev->gpio_dev.get = solo_gpiochip_get;
182 solo_dev->gpio_dev.set = solo_gpiochip_set;
183
184 ret = gpiochip_add_data(&solo_dev->gpio_dev, solo_dev);
185
186 if (ret) {
187 solo_dev->gpio_dev.label = NULL;
188 return -1;
189 }
190#endif
Ben Collinsf62de9b2010-11-04 22:51:17 -0400191 return 0;
Ben Collinsfaa4fd22010-06-17 13:27:26 -0400192}
193
Krzysztof Hałasadecebab2011-02-11 13:38:20 +0100194void solo_gpio_exit(struct solo_dev *solo_dev)
Ben Collinsfaa4fd22010-06-17 13:27:26 -0400195{
Anton Sviridenkod3202d12017-08-02 10:17:02 -0400196#ifdef CONFIG_GPIOLIB
197 if (solo_dev->gpio_dev.label) {
198 gpiochip_remove(&solo_dev->gpio_dev);
199 solo_dev->gpio_dev.label = NULL;
200 }
201#endif
Ben Collinsfaa4fd22010-06-17 13:27:26 -0400202 solo_gpio_clear(solo_dev, 0x30);
203 solo_gpio_config(solo_dev);
204}