Skip to content

Commit 66cbf64

Browse files
committed
Added driver to use rpmsg with gpiochip interface.Requires PRU firmware
Please follow instructions to load PRU firmware https://github.com/deebot/Beaglebone-BidirectionBus/blob/dev/bidirec_299/README.md
1 parent 2f2471e commit 66cbf64

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

drivers/rpmsg/rpmsg_gpio.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* PRU Remote Processor Messaging Driver with gpiochip interface
3+
* copyright (c) 2020 Deepankar Maithani
4+
* Codes examples from Lab no 5 from TI and has been used as boiler plate for rpmsg communication
5+
* https://processors.wiki.ti.com/index.php/PRU_Training:_Hands-on_Labs
6+
*
7+
* For learn more about the complete project visit:
8+
* https://github.com/deebot/Beaglebone-BidirectionBus/tree/dev
9+
* Steps to test the driver.
10+
* https://github.com/deebot/Beaglebone-BidirectionBus/blob/dev/bidirec_299/README.md
11+
*
12+
* This software is licensed under the terms of the GNU General Public
13+
* License version 2, as published by the Free Software Foundation, and
14+
* may be copied, distributed, and modified under those terms.
15+
*
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU General Public License for more details.
20+
*/
21+
#include <linux/kernel.h>
22+
#include <linux/module.h>
23+
#include <linux/slab.h>
24+
#include <linux/gpio.h>
25+
#include <linux/of.h>
26+
#include <linux/rpmsg.h>
27+
#include <linux/fs.h>
28+
#include <linux/init.h>
29+
#include <linux/kfifo.h>
30+
#include <linux/uaccess.h>
31+
#include <linux/poll.h>
32+
#include <linux/rpmsg/virtio_rpmsg.h>
33+
#include <linux/stat.h>
34+
#include <linux/bitops.h>
35+
#define PRU_MAX_DEVICES (16)
36+
/* Matches the definition in virtio_rpmsg_bus.c */
37+
#define RPMSG_BUF_SIZE (512)
38+
#define MAX_FIFO_MSG (32)
39+
#define FIFO_MSG_SIZE RPMSG_BUF_SIZE
40+
#define GPIO_NUM 9
41+
static DEFINE_MUTEX(rpmsg_pru_lock);
42+
static char rpmsg_pru_buf[RPMSG_BUF_SIZE];
43+
static struct gpio_chip chip;
44+
struct rpmsg_pru_dev {
45+
struct rpmsg_device *rpdev;
46+
struct device *dev;
47+
bool locked;
48+
dev_t devt;
49+
struct kfifo msg_fifo;
50+
u32 msg_len[MAX_FIFO_MSG];
51+
int msg_idx_rd;
52+
int msg_idx_wr;
53+
wait_queue_head_t wait_list;
54+
uint32_t gpio_state;
55+
long input_state;
56+
57+
};
58+
59+
struct rpmsg_pru_dev *prudev;
60+
/*Reads the lines which are set as input*/
61+
static int mygpio_get_value(struct gpio_chip *gc, unsigned offset)
62+
{
63+
uint32_t value;
64+
struct rpmsg_device *rpdev = container_of(gc->parent,
65+
struct rpmsg_device, dev);
66+
unsigned int mask = BIT(offset % 8);
67+
prudev = dev_get_drvdata(&rpdev->dev);
68+
69+
value = (prudev->input_state & mask)>>offset;
70+
return value;
71+
}
72+
/* Writes to the lines and creates the gpio_state which is then sent as RPmsg */
73+
static void mygpio_set_value(struct gpio_chip *gc, unsigned offset, int val)
74+
{
75+
int ret;
76+
struct rpmsg_device *rpdev = container_of(gc->parent, struct rpmsg_device, dev);
77+
pr_info("set_value function triggered");
78+
pr_info("The bit number %d is set to value: %d", offset, val);
79+
80+
prudev = dev_get_drvdata(&rpdev->dev);
81+
if (val == 0) {
82+
prudev->gpio_state &= ~(1<<offset);
83+
} else {
84+
prudev->gpio_state |= (1<<offset);
85+
}
86+
if (offset == GPIO_NUM-1) {
87+
/* copy the gpiostate in rpmsg buffer which will be sent over to PRU*/
88+
memcpy((void *)&rpmsg_pru_buf, (void *)&(prudev->gpio_state),
89+
sizeof(&(prudev->gpio_state)));
90+
pr_info("A check for checking gpio_state: %d",
91+
prudev->gpio_state);
92+
/* This line actually sends the data to the other side*/
93+
ret = rpmsg_send(rpdev->ept, (void *)rpmsg_pru_buf, 2);
94+
if (ret)
95+
dev_err(gc->parent, "rpmsg_send failed: %d\n", ret);
96+
}
97+
98+
}
99+
100+
/*sets the pin to output. Will be called when user sets one
101+
* of the gpiochip line as output which can be done manually
102+
* in sysfs or using libgpiod */
103+
104+
static int mygpio_direction_output(struct gpio_chip *gc,
105+
unsigned offset, int val)
106+
{
107+
pr_info("Direction of GPIO set to: out\n");
108+
return 0;
109+
}
110+
/*Runs When direction of a line is set as output*/
111+
static int mygpio_direction_input(struct gpio_chip *gc,
112+
unsigned offset)
113+
{
114+
115+
pr_info("Direction of GPIO set to: in \n");
116+
return 0;
117+
}
118+
/*This function gets called every time
119+
*an rpmsg_channel is created with a name that matches the .name
120+
*attribute of the rpmsg_driver_sample_id_table. It sets up suitable memory
121+
*and the gpiochip interface that can be seen in /sys/class/gpio and /dev.
122+
*/
123+
static int mygpio_rpmsg_pru_probe (struct rpmsg_device *rpdev)
124+
{
125+
int ret;
126+
struct rpmsg_pru_dev *prudev;
127+
prudev = devm_kzalloc(&rpdev->dev, sizeof(*prudev), GFP_KERNEL);
128+
if (!prudev)
129+
return -ENOMEM;
130+
prudev->rpdev = rpdev;
131+
ret = kfifo_alloc(&prudev->msg_fifo, MAX_FIFO_MSG * FIFO_MSG_SIZE,
132+
GFP_KERNEL);
133+
if (ret) {
134+
dev_err(&rpdev->dev, "Unable to allocate fifo for the rpmsg_pru device\n");
135+
return -ENOMEM;
136+
}
137+
init_waitqueue_head(&prudev->wait_list);
138+
dev_set_drvdata(&rpdev->dev, prudev);
139+
chip.label = rpdev->desc;
140+
chip.base = -1;
141+
chip.parent = &rpdev->dev;
142+
chip.owner = THIS_MODULE;
143+
chip.ngpio = GPIO_NUM;
144+
chip.can_sleep = 1;
145+
chip.get = mygpio_get_value;
146+
chip.set = mygpio_set_value;
147+
chip.direction_output = mygpio_direction_output;
148+
chip.direction_input = mygpio_direction_input;
149+
return gpiochip_add(&chip);
150+
}
151+
/* Callback function which gets called whenever a new rpmsg is received
152+
* The data received from the PRU is converted into long and then assigned to
153+
* input_state
154+
* @msg_fifo: kernel fifo used to buffer the messages between userspace and PRU
155+
* @msg_len: array storing the lengths of each message in the kernel fifo
156+
* @msg_idx_rd: kernel fifo read index
157+
* @msg_idx_wr: kernel fifo write index
158+
* */
159+
static int mygpio_rpmsg_pru_cb(struct rpmsg_device *rpdev, void *data, int len,
160+
void *priv, u32 src)
161+
{ int ret;
162+
u32 length;
163+
struct rpmsg_pru_dev *prudev;
164+
165+
prudev = dev_get_drvdata(&rpdev->dev);
166+
167+
if (kfifo_avail(&prudev->msg_fifo) < len) {
168+
dev_err(&rpdev->dev, "Not enough space on the FIFO\n");
169+
return -ENOSPC;
170+
}
171+
172+
if ((prudev->msg_idx_wr + 1) % MAX_FIFO_MSG ==
173+
prudev->msg_idx_rd) {
174+
dev_err(&rpdev->dev, "Message length table is full\n");
175+
return -ENOSPC;
176+
}
177+
/* adds the data received into a fifo*/
178+
length = kfifo_in(&prudev->msg_fifo, data, len);
179+
prudev->msg_len[prudev->msg_idx_wr] = length;
180+
prudev->msg_idx_wr = (prudev->msg_idx_wr + 1) % MAX_FIFO_MSG;
181+
182+
wake_up_interruptible(&prudev->wait_list);
183+
ret = kstrtol((char *) data, 10, &prudev->input_state);
184+
if (ret) {
185+
return ret;
186+
}
187+
pr_info("The shift register port state is: %ld", prudev->input_state);
188+
return 0;
189+
}
190+
static void mygpio_rpmsg_pru_remove(struct rpmsg_device *rpdev)
191+
{
192+
struct rpmsg_pru_dev *prudev;
193+
prudev = dev_get_drvdata(&rpdev->dev);
194+
195+
kfifo_free(&prudev->msg_fifo);
196+
gpiochip_remove(&chip);
197+
198+
}
199+
/*
200+
* Matches this tag:If you change .name
201+
* PRU firmware should also be updated with same channel name
202+
*/
203+
static const struct rpmsg_device_id rpmsg_driver_pru_id_table[] = {
204+
{ .name = "rpmsg-pru-gpio" },
205+
{ },
206+
};
207+
MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_pru_id_table);
208+
209+
static struct rpmsg_driver rpmsg_pru_driver = {
210+
.drv.name = KBUILD_MODNAME,
211+
.id_table = rpmsg_driver_pru_id_table,
212+
.probe = mygpio_rpmsg_pru_probe,
213+
.callback = mygpio_rpmsg_pru_cb,
214+
.remove = mygpio_rpmsg_pru_remove,
215+
};
216+
217+
module_rpmsg_driver(rpmsg_pru_driver);
218+
MODULE_AUTHOR("DeepankarMaithani <[email protected]>");
219+
MODULE_DESCRIPTION("A driver to send rpmsg data using sysfs and chardev interface");
220+
MODULE_VERSION("0.1");
221+
MODULE_LICENSE("GPL");
222+

0 commit comments

Comments
 (0)