Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
257 lines (212 sloc) 6.03 KB
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Amitesh Singh");
MODULE_DESCRIPTION("custom soc chip driver to create fake GPIOs");
MODULE_VERSION("0.1");
#define DRIVER_NAME "platform-gpio-device"
struct my_device_platform_data
{
struct gpio_chip chip;
int gpio_pin_val; // only 1 pin we have, for simplicity
struct irq_chip *irqchip;
void (*gpio_create)(struct my_device_platform_data *, struct platform_device *pdev);
void (*gpio_remove)(struct my_device_platform_data *, struct platform_device *pdev);
};
static int
_gpio_get(struct gpio_chip *chip,
unsigned offset)
{
struct my_device_platform_data *sd =
container_of(chip,
struct my_device_platform_data,
chip);
printk(KERN_INFO "gpio_get(): %d", sd->gpio_pin_val);
return sd->gpio_pin_val;
}
static void
_gpio_set(struct gpio_chip *chip,
unsigned offset, int value)
{
struct my_device_platform_data *sd =
container_of(chip,
struct my_device_platform_data,
chip);
sd->gpio_pin_val = value;
printk(KERN_INFO "gpio_set(): %d", value);
if (value == 1)
generic_handle_irq(irq_find_mapping(chip->irqdomain, offset));
}
static int
_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
printk(KERN_INFO "Setting pin to OUTPUT");
return 0;
}
//This is not required whenever you use gpiochip_irqchip_add
static int
_to_irq(struct gpio_chip *chip,
unsigned offset)
{
printk("to_irq():");
return 101;
}
static void
_irq_mask(struct irq_data *d)
{
printk("irq mask");
}
static void
_irq_unmask(struct irq_data *d)
{
printk("irq unmask");
}
static int
_irq_set_type(struct irq_data *d, unsigned type)
{
printk("irq set type: %d, type\n", type);
return 0;
}
static int
_irq_request_resources(struct irq_data *d)
{
return 0;
}
static struct irq_chip my_gpio_irq_chip = {
.name = DRIVER_NAME,
.irq_mask = _irq_mask,
.irq_unmask = _irq_unmask,
.irq_set_type = _irq_set_type,
.irq_request_resources = _irq_request_resources,
};
/*
static irqreturn_t
_irq_handler(int irq, void *dev_id)
{
printk(KERN_ALERT "........................ handled irq interrupt");
return IRQ_HANDLED;
}
*/
static void
_gpio_create(struct my_device_platform_data *sd, struct platform_device *pdev)
{
printk(KERN_INFO "GPIO created");
sd->chip.dev = &pdev->dev;
sd->chip.label = DRIVER_NAME;
sd->chip.owner = THIS_MODULE;
sd->chip.base = -1;
sd->chip.ngpio = 4;
sd->chip.can_sleep = false;
sd->chip.set = _gpio_set;
sd->chip.get = _gpio_get;
sd->chip.direction_output = _direction_output;
//sd->chip.to_irq = _to_irq;
sd->irqchip = &my_gpio_irq_chip;
if (gpiochip_add(&sd->chip) < 0)
{
printk(KERN_ALERT "Failed to add gpio chip");
}
else
printk (KERN_INFO "able to add gpiochip: %s",
sd->chip.label);
int ret;
/*
ret = devm_request_threaded_irq(&pdev->dev, 36,
NULL, _irq_handler, IRQF_TRIGGER_HIGH,
dev_name(&pdev->dev), &sd->chip);
if (ret)
{
printk("Failed to request IRQ:\n");
}
*/
ret = gpiochip_irqchip_add(&sd->chip,
sd->irqchip,
0,
handle_simple_irq,
IRQ_TYPE_NONE);
//IRQ_TYPE_LEVEL_HIGH);
if (ret)
{
printk(KERN_NOTICE "Failed to add irqchip");
}
gpiochip_set_chained_irqchip(&sd->chip,
sd->irqchip,
36,
NULL);
}
static void
_gpio_remove(struct my_device_platform_data *sd, struct platform_device *pdev)
{
printk(KERN_INFO "GPIO is removed");
free_irq(36, 0);
gpiochip_remove(&sd->chip);
}
static struct my_device_platform_data my_device_data = {
.gpio_create = _gpio_create,
.gpio_remove = _gpio_remove,
};
static void
_device_release(struct device *pdev)
{
//fixes kernel warnings:
/* +0.000010] WARNING: CPU: 3 PID: 11891 at /build/lin
ux-a2WvEb/linux-4.4.0/drivers/base/core.c:251 device_r
elease+0x89/0x90() [ +0.000010] Device 'platform-gpio-device' does not have a release() function, it is broken and must be fixed.
*/
printk(KERN_ALERT "Device is released");
}
static struct platform_device my_device = {
.name = DRIVER_NAME,
.id = -1, //let kernel decide
.dev.platform_data = &my_device_data,
.dev.release = _device_release,
};
static int
_sample_platform_driver_probe(struct platform_device *pdev)
{
struct my_device_platform_data *data;
printk(KERN_INFO "platfrom device connected/probed");
///struct my_driver_data *mdd;
data = dev_get_platdata(&pdev->dev);
if (data->gpio_create) data->gpio_create(data, pdev);
return 0;
}
static int
_sample_platform_driver_remove(struct platform_device *pdev)
{
struct my_device_platform_data *data;
data = dev_get_platdata(&pdev->dev);
if (data->gpio_remove) data->gpio_remove(data, pdev);
printk(KERN_INFO "platfrom device removed");
return 0;
}
static struct platform_driver sample_platform_driver = {
.probe = _sample_platform_driver_probe,
.remove = _sample_platform_driver_remove,
.driver = {
.name = DRIVER_NAME, //platform_device will also use same name
},
};
static int __init
_platform_driver_init(void)
{
printk(KERN_INFO "platform driver init");
platform_driver_register(&sample_platform_driver);
platform_device_register(&my_device);
return 0;
}
static void __exit
_platform_driver_exit(void)
{
printk(KERN_INFO "platform driver exit");
platform_driver_unregister(&sample_platform_driver);
platform_device_unregister(&my_device);
}
module_init(_platform_driver_init);
module_exit(_platform_driver_exit);