|
| 1 | +From 50ad0fc3083890661113180aa3a849958d0c0787 Mon Sep 17 00:00:00 2001 |
| 2 | +From: deebot <deepankar.maithani@stud.th-deg.de> |
| 3 | +Date: Sun, 23 Aug 2020 13:40:44 +0200 |
| 4 | +Subject: [PATCH] - Added driver to use rpmsg with gpiochip interface.Requires |
| 5 | + PRU firmware Please follow instructions to load PRU firmware |
| 6 | + https://github.com/deebot/Beaglebone-BidirectionBus/blob/dev/bidirec_299/README.md |
| 7 | + - Added rule to compile rpmsg-gpio driver in Make and Kconfig |
| 8 | + |
| 9 | +--- |
| 10 | + drivers/rpmsg/Kconfig | 12 ++ |
| 11 | + drivers/rpmsg/Makefile | 2 + |
| 12 | + drivers/rpmsg/rpmsg_gpio.c | 222 +++++++++++++++++++++++++++++++++++++ |
| 13 | + 3 files changed, 236 insertions(+) |
| 14 | + create mode 100644 drivers/rpmsg/rpmsg_gpio.c |
| 15 | + |
| 16 | +diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig |
| 17 | +index c41d8157b079..ef2c47c017bb 100644 |
| 18 | +--- a/drivers/rpmsg/Kconfig |
| 19 | ++++ b/drivers/rpmsg/Kconfig |
| 20 | +@@ -81,4 +81,16 @@ config RPMSG_PRU |
| 21 | + |
| 22 | + If unsure, say N. |
| 23 | + |
| 24 | ++config CONFIG_RPMSG_GPIOHIP_INTERFACE |
| 25 | ++ tristate "A rpmsg driver with gpiochip interface" |
| 26 | ++ default m |
| 27 | ++ depends on RPMSG_VIRTIO |
| 28 | ++ depends on REMOTEPROC |
| 29 | ++ help |
| 30 | ++ This driver provides provides gpiochip inteface which can be accessed |
| 31 | ++ in userspace using libgpiod. You would see a char dev interface in |
| 32 | ++ "/dev" using this driver. Follow the README from the following |
| 33 | ++ repo for more info. |
| 34 | ++ https://github.com/deebot/Beaglebone-BidirectionBus |
| 35 | ++ |
| 36 | + endmenu |
| 37 | +diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile |
| 38 | +index 6666c8d6327e..3b93ea58ae36 100644 |
| 39 | +--- a/drivers/rpmsg/Makefile |
| 40 | ++++ b/drivers/rpmsg/Makefile |
| 41 | +@@ -9,4 +9,6 @@ obj-$(CONFIG_RPMSG_QCOM_SMD) += qcom_smd.o |
| 42 | + obj-$(CONFIG_RPMSG_VIRTIO) += virtio_rpmsg_bus.o |
| 43 | + |
| 44 | + obj-$(CONFIG_RPMSG_RPC) += rpmsg-rpc.o |
| 45 | ++obj-$(CONFIG_RPMSG_GPIOCHIP_INTERFACE) += rpmsg_gpio.o |
| 46 | + rpmsg-rpc-y := rpmsg_rpc.o rpmsg_rpc_sysfs.o rpmsg_rpc_dmabuf.o |
| 47 | ++ |
| 48 | +diff --git a/drivers/rpmsg/rpmsg_gpio.c b/drivers/rpmsg/rpmsg_gpio.c |
| 49 | +new file mode 100644 |
| 50 | +index 000000000000..92fd7e517c3c |
| 51 | +--- /dev/null |
| 52 | ++++ b/drivers/rpmsg/rpmsg_gpio.c |
| 53 | +@@ -0,0 +1,222 @@ |
| 54 | ++/* |
| 55 | ++ * PRU Remote Processor Messaging Driver with gpiochip interface |
| 56 | ++ * copyright (c) 2020 Deepankar Maithani |
| 57 | ++ * Codes examples from Lab no 5 from TI and has been used as boiler plate for rpmsg communication |
| 58 | ++ * https://processors.wiki.ti.com/index.php/PRU_Training:_Hands-on_Labs |
| 59 | ++ * |
| 60 | ++ * For learn more about the complete project visit: |
| 61 | ++ * https://github.com/deebot/Beaglebone-BidirectionBus/tree/dev |
| 62 | ++ * Steps to test the driver. |
| 63 | ++ * https://github.com/deebot/Beaglebone-BidirectionBus/blob/dev/bidirec_299/README.md |
| 64 | ++ * |
| 65 | ++ * This software is licensed under the terms of the GNU General Public |
| 66 | ++ * License version 2, as published by the Free Software Foundation, and |
| 67 | ++ * may be copied, distributed, and modified under those terms. |
| 68 | ++ * |
| 69 | ++ * This program is distributed in the hope that it will be useful, |
| 70 | ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 71 | ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 72 | ++ * GNU General Public License for more details. |
| 73 | ++ */ |
| 74 | ++#include <linux/kernel.h> |
| 75 | ++#include <linux/module.h> |
| 76 | ++#include <linux/slab.h> |
| 77 | ++#include <linux/gpio.h> |
| 78 | ++#include <linux/of.h> |
| 79 | ++#include <linux/rpmsg.h> |
| 80 | ++#include <linux/fs.h> |
| 81 | ++#include <linux/init.h> |
| 82 | ++#include <linux/kfifo.h> |
| 83 | ++#include <linux/uaccess.h> |
| 84 | ++#include <linux/poll.h> |
| 85 | ++#include <linux/rpmsg/virtio_rpmsg.h> |
| 86 | ++#include <linux/stat.h> |
| 87 | ++#include <linux/bitops.h> |
| 88 | ++#define PRU_MAX_DEVICES (16) |
| 89 | ++/* Matches the definition in virtio_rpmsg_bus.c */ |
| 90 | ++#define RPMSG_BUF_SIZE (512) |
| 91 | ++#define MAX_FIFO_MSG (32) |
| 92 | ++#define FIFO_MSG_SIZE RPMSG_BUF_SIZE |
| 93 | ++#define GPIO_NUM 9 |
| 94 | ++static DEFINE_MUTEX(rpmsg_pru_lock); |
| 95 | ++static char rpmsg_pru_buf[RPMSG_BUF_SIZE]; |
| 96 | ++static struct gpio_chip chip; |
| 97 | ++struct rpmsg_pru_dev { |
| 98 | ++ struct rpmsg_device *rpdev; |
| 99 | ++ struct device *dev; |
| 100 | ++ bool locked; |
| 101 | ++ dev_t devt; |
| 102 | ++ struct kfifo msg_fifo; |
| 103 | ++ u32 msg_len[MAX_FIFO_MSG]; |
| 104 | ++ int msg_idx_rd; |
| 105 | ++ int msg_idx_wr; |
| 106 | ++ wait_queue_head_t wait_list; |
| 107 | ++ uint32_t gpio_state; |
| 108 | ++ long input_state; |
| 109 | ++ |
| 110 | ++}; |
| 111 | ++ |
| 112 | ++struct rpmsg_pru_dev *prudev; |
| 113 | ++/*Reads the lines which are set as input*/ |
| 114 | ++static int mygpio_get_value(struct gpio_chip *gc, unsigned offset) |
| 115 | ++{ |
| 116 | ++ uint32_t value; |
| 117 | ++ struct rpmsg_device *rpdev = container_of(gc->parent, |
| 118 | ++ struct rpmsg_device, dev); |
| 119 | ++ unsigned int mask = BIT(offset % 8); |
| 120 | ++ prudev = dev_get_drvdata(&rpdev->dev); |
| 121 | ++ |
| 122 | ++ value = (prudev->input_state & mask)>>offset; |
| 123 | ++ return value; |
| 124 | ++} |
| 125 | ++/* Writes to the lines and creates the gpio_state which is then sent as RPmsg */ |
| 126 | ++static void mygpio_set_value(struct gpio_chip *gc, unsigned offset, int val) |
| 127 | ++{ |
| 128 | ++ int ret; |
| 129 | ++ struct rpmsg_device *rpdev = container_of(gc->parent, struct rpmsg_device, dev); |
| 130 | ++ pr_info("set_value function triggered"); |
| 131 | ++ pr_info("The bit number %d is set to value: %d", offset, val); |
| 132 | ++ |
| 133 | ++ prudev = dev_get_drvdata(&rpdev->dev); |
| 134 | ++ if (val == 0) { |
| 135 | ++ prudev->gpio_state &= ~(1<<offset); |
| 136 | ++ } else { |
| 137 | ++ prudev->gpio_state |= (1<<offset); |
| 138 | ++ } |
| 139 | ++ if (offset == GPIO_NUM-1) { |
| 140 | ++ /* copy the gpiostate in rpmsg buffer which will be sent over to PRU*/ |
| 141 | ++ memcpy((void *)&rpmsg_pru_buf, (void *)&(prudev->gpio_state), |
| 142 | ++ sizeof(&(prudev->gpio_state))); |
| 143 | ++ pr_info("A check for checking gpio_state: %d", |
| 144 | ++ prudev->gpio_state); |
| 145 | ++ /* This line actually sends the data to the other side*/ |
| 146 | ++ ret = rpmsg_send(rpdev->ept, (void *)rpmsg_pru_buf, 2); |
| 147 | ++ if (ret) |
| 148 | ++ dev_err(gc->parent, "rpmsg_send failed: %d\n", ret); |
| 149 | ++ } |
| 150 | ++ |
| 151 | ++} |
| 152 | ++ |
| 153 | ++/*sets the pin to output. Will be called when user sets one |
| 154 | ++ * of the gpiochip line as output which can be done manually |
| 155 | ++ * in sysfs or using libgpiod */ |
| 156 | ++ |
| 157 | ++static int mygpio_direction_output(struct gpio_chip *gc, |
| 158 | ++ unsigned offset, int val) |
| 159 | ++{ |
| 160 | ++ pr_info("Direction of GPIO set to: out\n"); |
| 161 | ++ return 0; |
| 162 | ++} |
| 163 | ++/*Runs When direction of a line is set as output*/ |
| 164 | ++static int mygpio_direction_input(struct gpio_chip *gc, |
| 165 | ++ unsigned offset) |
| 166 | ++{ |
| 167 | ++ |
| 168 | ++ pr_info("Direction of GPIO set to: in \n"); |
| 169 | ++ return 0; |
| 170 | ++} |
| 171 | ++/*This function gets called every time |
| 172 | ++ *an rpmsg_channel is created with a name that matches the .name |
| 173 | ++ *attribute of the rpmsg_driver_sample_id_table. It sets up suitable memory |
| 174 | ++ *and the gpiochip interface that can be seen in /sys/class/gpio and /dev. |
| 175 | ++ */ |
| 176 | ++static int mygpio_rpmsg_pru_probe (struct rpmsg_device *rpdev) |
| 177 | ++{ |
| 178 | ++ int ret; |
| 179 | ++ struct rpmsg_pru_dev *prudev; |
| 180 | ++ prudev = devm_kzalloc(&rpdev->dev, sizeof(*prudev), GFP_KERNEL); |
| 181 | ++ if (!prudev) |
| 182 | ++ return -ENOMEM; |
| 183 | ++ prudev->rpdev = rpdev; |
| 184 | ++ ret = kfifo_alloc(&prudev->msg_fifo, MAX_FIFO_MSG * FIFO_MSG_SIZE, |
| 185 | ++ GFP_KERNEL); |
| 186 | ++ if (ret) { |
| 187 | ++ dev_err(&rpdev->dev, "Unable to allocate fifo for the rpmsg_pru device\n"); |
| 188 | ++ return -ENOMEM; |
| 189 | ++ } |
| 190 | ++ init_waitqueue_head(&prudev->wait_list); |
| 191 | ++ dev_set_drvdata(&rpdev->dev, prudev); |
| 192 | ++ chip.label = rpdev->desc; |
| 193 | ++ chip.base = -1; |
| 194 | ++ chip.parent = &rpdev->dev; |
| 195 | ++ chip.owner = THIS_MODULE; |
| 196 | ++ chip.ngpio = GPIO_NUM; |
| 197 | ++ chip.can_sleep = 1; |
| 198 | ++ chip.get = mygpio_get_value; |
| 199 | ++ chip.set = mygpio_set_value; |
| 200 | ++ chip.direction_output = mygpio_direction_output; |
| 201 | ++ chip.direction_input = mygpio_direction_input; |
| 202 | ++ return gpiochip_add(&chip); |
| 203 | ++} |
| 204 | ++/* Callback function which gets called whenever a new rpmsg is received |
| 205 | ++ * The data received from the PRU is converted into long and then assigned to |
| 206 | ++ * input_state |
| 207 | ++ * @msg_fifo: kernel fifo used to buffer the messages between userspace and PRU |
| 208 | ++ * @msg_len: array storing the lengths of each message in the kernel fifo |
| 209 | ++ * @msg_idx_rd: kernel fifo read index |
| 210 | ++ * @msg_idx_wr: kernel fifo write index |
| 211 | ++ * */ |
| 212 | ++static int mygpio_rpmsg_pru_cb(struct rpmsg_device *rpdev, void *data, int len, |
| 213 | ++ void *priv, u32 src) |
| 214 | ++{ int ret; |
| 215 | ++ u32 length; |
| 216 | ++ struct rpmsg_pru_dev *prudev; |
| 217 | ++ |
| 218 | ++ prudev = dev_get_drvdata(&rpdev->dev); |
| 219 | ++ |
| 220 | ++ if (kfifo_avail(&prudev->msg_fifo) < len) { |
| 221 | ++ dev_err(&rpdev->dev, "Not enough space on the FIFO\n"); |
| 222 | ++ return -ENOSPC; |
| 223 | ++ } |
| 224 | ++ |
| 225 | ++ if ((prudev->msg_idx_wr + 1) % MAX_FIFO_MSG == |
| 226 | ++ prudev->msg_idx_rd) { |
| 227 | ++ dev_err(&rpdev->dev, "Message length table is full\n"); |
| 228 | ++ return -ENOSPC; |
| 229 | ++ } |
| 230 | ++ /* adds the data received into a fifo*/ |
| 231 | ++ length = kfifo_in(&prudev->msg_fifo, data, len); |
| 232 | ++ prudev->msg_len[prudev->msg_idx_wr] = length; |
| 233 | ++ prudev->msg_idx_wr = (prudev->msg_idx_wr + 1) % MAX_FIFO_MSG; |
| 234 | ++ |
| 235 | ++ wake_up_interruptible(&prudev->wait_list); |
| 236 | ++ ret = kstrtol((char *) data, 10, &prudev->input_state); |
| 237 | ++ if (ret) { |
| 238 | ++ return ret; |
| 239 | ++ } |
| 240 | ++ pr_info("The shift register port state is: %ld", prudev->input_state); |
| 241 | ++ return 0; |
| 242 | ++} |
| 243 | ++static void mygpio_rpmsg_pru_remove(struct rpmsg_device *rpdev) |
| 244 | ++{ |
| 245 | ++ struct rpmsg_pru_dev *prudev; |
| 246 | ++ prudev = dev_get_drvdata(&rpdev->dev); |
| 247 | ++ |
| 248 | ++ kfifo_free(&prudev->msg_fifo); |
| 249 | ++ gpiochip_remove(&chip); |
| 250 | ++ |
| 251 | ++} |
| 252 | ++/* |
| 253 | ++ * Matches this tag:If you change .name |
| 254 | ++ * PRU firmware should also be updated with same channel name |
| 255 | ++ */ |
| 256 | ++static const struct rpmsg_device_id rpmsg_driver_pru_id_table[] = { |
| 257 | ++ { .name = "rpmsg-pru-gpio" }, |
| 258 | ++ { }, |
| 259 | ++}; |
| 260 | ++MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_pru_id_table); |
| 261 | ++ |
| 262 | ++static struct rpmsg_driver rpmsg_pru_driver = { |
| 263 | ++ .drv.name = KBUILD_MODNAME, |
| 264 | ++ .id_table = rpmsg_driver_pru_id_table, |
| 265 | ++ .probe = mygpio_rpmsg_pru_probe, |
| 266 | ++ .callback = mygpio_rpmsg_pru_cb, |
| 267 | ++ .remove = mygpio_rpmsg_pru_remove, |
| 268 | ++}; |
| 269 | ++ |
| 270 | ++module_rpmsg_driver(rpmsg_pru_driver); |
| 271 | ++MODULE_AUTHOR("DeepankarMaithani <deepankar19910@gmail.com>"); |
| 272 | ++MODULE_DESCRIPTION("A driver to send rpmsg data using sysfs and chardev interface"); |
| 273 | ++MODULE_VERSION("0.1"); |
| 274 | ++MODULE_LICENSE("GPL"); |
| 275 | ++ |
| 276 | +-- |
| 277 | +2.28.0 |
| 278 | + |
0 commit comments