| 
 | 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