/*
 * wnr2200-keys.c
 *
 *  Created on: 2014年1月20日
 *      Author: tao
 */
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kmod.h>

#include <linux/workqueue.h>
#include <linux/skbuff.h>
#include <linux/netlink.h>
#include <linux/kobject.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/of_gpio.h>
#include <linux/gpio_keys.h>
#include <asm/mach-ath79/ar71xx_regs.h>


#define DRV_NAME    "wnr2200-keys"
#undef BH_DEBUG
#define BH_SKB_SIZE        2048

#ifdef BH_DEBUG
#define BH_DBG(fmt, args...) printk(KERN_DEBUG "%s: " fmt, DRV_NAME, ##args )
#else
#define BH_DBG(fmt, args...) do {} while (0)
#endif

#define BH_ERR(fmt, args...) printk(KERN_ERR "%s: " fmt, DRV_NAME, ##args )

#define WNR2200_GPIO_BUTTON_WIFI            3
#define WNR2200_GPIO_BUTTON_WPS                5
#define WNR2200_GPIO_BUTTON_RESET            6

#define WNR2200_PCI_GPIO_BASE             0x20
#define WNR2200_PCI_GPIO_COUNT            10
#define WNR2200_KEYS_POLL_INTERVAL    20 /* msecs */
#define WNR2200_KEYS_DEBOUNCE_INTERVAL    (3 * WNR2200_KEYS_POLL_INTERVAL)

#define AR9287_BASE
                 AR71XX_PCI_MEM_BASE
#define AR9287_GPIO_IN_OUT                           0x4048 // GPIO
input / output register
#define AR9287_GPIO_IN_VAL                           0x001FF800
#define AR9287_GPIO_IN_VAL_S                         11

#define AR9287_GPIO_OE_OUT                           0x404c // GPIO
output enable register
#define AR9287_GPIO_OE_OUT_DRV                       0x3    // 2 bit
field mask, shifted by 2*bitpos
#define AR9287_GPIO_OE_OUT_DRV_NO                    0x0    // tristate
#define AR9287_GPIO_OE_OUT_DRV_LOW                   0x1    // drive if low
#define AR9287_GPIO_OE_OUT_DRV_HI                    0x2    // drive if high
#define AR9287_GPIO_OE_OUT_DRV_ALL                   0x3    // drive always

#define AR9287_GPIO_OUTPUT_MUX_AS_OUTPUT             0
#define AR9287_GPIO_OUTPUT_MUX_AS_PCIE_ATTENTION_LED 1
#define AR9287_GPIO_OUTPUT_MUX_AS_PCIE_POWER_LED     2
#define AR9287_GPIO_OUTPUT_MUX_AS_TX_FRAME           3
#define AR9287_GPIO_OUTPUT_MUX_AS_RX_CLEAR_EXTERNAL  4
#define AR9287_GPIO_OUTPUT_MUX_AS_MAC_NETWORK_LED    5
#define AR9287_GPIO_OUTPUT_MUX_AS_MAC_POWER_LED      6

#define AR9287_GPIO_OUTPUT_MUX1                      0x4060
#define AR9287_GPIO_OUTPUT_MUX2                      0x4064
#define AR9287_GPIO_IE_VALUE                         0x4054 // GPIO
input enable and value register

#define AR9287_GPIO_BIT(_gpio) (1 << (_gpio))

#define MS(_v, _f)  (((_v) & _f) >> _f##_S)

struct bh_priv {
    unsigned long        seen;
};

struct bh_event {
    const char        *name;
    unsigned int        type;
    char            *action;
    unsigned long        seen;

    struct sk_buff        *skb;
    struct work_struct    work;
};

struct bh_map {
    unsigned int    code;
    const char    *name;
};

struct gpio_keys_button_data {
    struct delayed_work work;
    struct bh_priv bh;
    int last_state;
    int count;
    int threshold;
    int can_sleep;
    struct gpio_keys_button *b;
};

struct gpio_keys_button_dev {
    int polled;
    struct delayed_work work;

    struct device *dev;
    struct gpio_keys_platform_data *pdata;
    struct gpio_keys_button_data data[0];
};
void __iomem *ar9287_gpio_base;

extern u64 uevent_next_seqnum(void);

#define BH_MAP(_code, _name)        \
    {                \
        .code = (_code),    \
        .name = (_name),    \
    }

static struct bh_map button_map[] = {
    BH_MAP(KEY_RESTART,    "reset"),
    BH_MAP(KEY_RFKILL,    "rfkill"),
    BH_MAP(KEY_WPS_BUTTON,    "wps"),
};

static struct platform_device *wnr2200_key_devs;

static int ar9287_gpio_get_value(struct gpio_chip *chip, unsigned offset)
{
    return (MS(__raw_readl(ar9287_gpio_base + AR9287_GPIO_IN_OUT),
AR9287_GPIO_IN_VAL) & AR9287_GPIO_BIT(offset)) != 0;
}
static void ar9287_gpio_set_value(struct gpio_chip *chip,
                  unsigned offset, int value)
{
    unsigned long reg, mask;
    reg = __raw_readl(ar9287_gpio_base + AR9287_GPIO_IN_OUT);
    mask = ~(1 << offset);
    reg &= mask | ((value&1) << offset);
    __raw_writel(reg, ar9287_gpio_base + AR9287_GPIO_IN_OUT);
}

static int ar9287_gpio_direction_input(struct gpio_chip *chip,
                       unsigned offset)
{
    /*FIXME*/
    return 0;
}

static int ar9287_gpio_direction_output(struct gpio_chip *chip,
                    unsigned offset, int value)
{
    /*FIXME*/
    return 0;
}

static struct gpio_chip ar9287_gpio_chip = {
    .label        = "ar9287-gpio",
    .get            = ar9287_gpio_get_value,
    .set            = ar9287_gpio_set_value,
    .direction_input    = ar9287_gpio_direction_input,
    .direction_output    = ar9287_gpio_direction_output,
    .ngpio        = WNR2200_PCI_GPIO_COUNT,
    .base            =    WNR2200_PCI_GPIO_BASE
};

static struct gpio_keys_button wnr2200_gpio_keys[] = {
    {
        .desc        = "rfkill",
        .type        = EV_KEY,
        .code        = KEY_RFKILL,
        .debounce_interval = WNR2200_KEYS_DEBOUNCE_INTERVAL,
        .gpio        = 0x20 + WNR2200_GPIO_BUTTON_WIFI,
        .active_low    = 1,
    },
    {
        .desc        = "wps",
        .type        = EV_KEY,
        .code        = KEY_WPS_BUTTON,
        .debounce_interval = WNR2200_KEYS_DEBOUNCE_INTERVAL,
        .gpio        = 0x20 + WNR2200_GPIO_BUTTON_WPS,
        .active_low    = 1,
    },
    {
        .desc        = "reset",
        .type        = EV_KEY,
        .code        = KEY_RESTART,
        .debounce_interval = WNR2200_KEYS_DEBOUNCE_INTERVAL,
        .gpio        = 0x20 + WNR2200_GPIO_BUTTON_RESET,
        .active_low    = 1,
    }
};

static int bh_event_add_var(struct bh_event *event, int argv,
        const char *format, ...)
{
    static char buf[128];
    char *s;
    va_list args;
    int len;

    if (argv)
        return 0;

    va_start(args, format);
    len = vsnprintf(buf, sizeof(buf), format, args);
    va_end(args);

    if (len >= sizeof(buf)) {
        BH_ERR("buffer size too small\n");
        WARN_ON(1);
        return -ENOMEM;
    }

    s = skb_put(event->skb, len + 1);
    strcpy(s, buf);

    BH_DBG("added variable '%s'\n", s);

    return 0;
}

static int button_hotplug_fill_event(struct bh_event *event)
{
    int ret;

    ret = bh_event_add_var(event, 0, "HOME=%s", "/");
    if (ret)
        return ret;

    ret = bh_event_add_var(event, 0, "PATH=%s",
                    "/sbin:/bin:/usr/sbin:/usr/bin");
    if (ret)
        return ret;

    ret = bh_event_add_var(event, 0, "SUBSYSTEM=%s", "button");
    if (ret)
        return ret;

    ret = bh_event_add_var(event, 0, "ACTION=%s", event->action);
    if (ret)
        return ret;

    ret = bh_event_add_var(event, 0, "BUTTON=%s", event->name);
    if (ret)
        return ret;

    if (event->type == EV_SW) {
        ret = bh_event_add_var(event, 0, "TYPE=%s", "switch");
        if (ret)
            return ret;
    }

    ret = bh_event_add_var(event, 0, "SEEN=%ld", event->seen);
    if (ret)
        return ret;

    ret = bh_event_add_var(event, 0, "SEQNUM=%llu", uevent_next_seqnum());

    return ret;
}

static void button_hotplug_work(struct work_struct *work)
{
    struct bh_event *event = container_of(work, struct bh_event, work);
    int ret = 0;

    event->skb = alloc_skb(BH_SKB_SIZE, GFP_KERNEL);
    if (!event->skb)
        goto out_free_event;

    ret = bh_event_add_var(event, 0, "%s@", event->action);
    if (ret)
        goto out_free_skb;

    ret = button_hotplug_fill_event(event);
    if (ret)
        goto out_free_skb;

    NETLINK_CB(event->skb).dst_group = 1;
    broadcast_uevent(event->skb, 0, 1, GFP_KERNEL);

 out_free_skb:
    if (ret) {
        BH_ERR("work error %d\n", ret);
        kfree_skb(event->skb);
    }
 out_free_event:
    kfree(event);
}

static int button_hotplug_create_event(const char *name, unsigned int type,
        unsigned long seen, int pressed)
{
    struct bh_event *event;

    BH_DBG("create event, name=%s, seen=%lu, pressed=%d\n",
        name, seen, pressed);

    event = kzalloc(sizeof(*event), GFP_KERNEL);
    if (!event)
        return -ENOMEM;

    event->name = name;
    event->type = type;
    event->seen = seen;
    event->action = pressed ? "pressed" : "released";

    INIT_WORK(&event->work, (void *)(void *)button_hotplug_work);
    schedule_work(&event->work);

    return 0;
}
#ifdef    CONFIG_HOTPLUG
static int button_get_index(unsigned int code)
{
    int i;

    for (i = 0; i < ARRAY_SIZE(button_map); i++)
        if (button_map[i].code == code)
            return i;

    return -1;
}

static void button_hotplug_event(struct gpio_keys_button_data *data,
               unsigned int type, int value)
{
    struct bh_priv *priv = &data->bh;
    unsigned long seen = jiffies;
    int btn;

    BH_DBG("event type=%u, code=%u, value=%d\n", type, data->b->code, value);

    if ((type != EV_KEY) && (type != EV_SW))
        return;

    btn = button_get_index(data->b->code);
    if (btn < 0)
        return;

    button_hotplug_create_event(button_map[btn].name, type,
            (seen - priv->seen) / HZ, value);
    priv->seen = seen;
}
#else
static void button_hotplug_event(struct gpio_keys_button_data *data,
               unsigned int type, int value)
{
}
#endif    /* CONFIG_HOTPLUG */

static int gpio_button_get_value(struct gpio_keys_button_data *bdata)
{
    int val;

    if (bdata->can_sleep)
        val = !!gpio_get_value_cansleep(bdata->b->gpio);
    else
        val = !!gpio_get_value(bdata->b->gpio);

    return val ^ bdata->b->active_low;
}

static void gpio_keys_polled_check_state(struct gpio_keys_button_data *bdata)
{
    int state = gpio_button_get_value(bdata);

    if (state != bdata->last_state) {
        unsigned int type = bdata->b->type ?: EV_KEY;

        if (bdata->count < bdata->threshold) {
            bdata->count++;
            return;
        }

        if ((bdata->last_state != -1) || (type == EV_SW))
            button_hotplug_event(bdata, type, state);

        bdata->last_state = state;
    }

    bdata->count = 0;
}

static void gpio_keys_polled_queue_work(struct gpio_keys_button_dev *bdev)
{
    struct gpio_keys_platform_data *pdata = bdev->pdata;
    unsigned long delay = msecs_to_jiffies(pdata->poll_interval);

    if (delay >= HZ)
        delay = round_jiffies_relative(delay);
    schedule_delayed_work(&bdev->work, delay);
}


static void gpio_keys_polled_poll(struct work_struct *work)
{
    struct gpio_keys_button_dev *bdev =
        container_of(work, struct gpio_keys_button_dev, work.work);
    int i;

    for (i = 0; i < bdev->pdata->nbuttons; i++) {
        struct gpio_keys_button_data *bdata = &bdev->data[i];
        gpio_keys_polled_check_state(bdata);
    }
    gpio_keys_polled_queue_work(bdev);
}

static void gpio_keys_polled_close(struct gpio_keys_button_dev *bdev)
{
    struct gpio_keys_platform_data *pdata = bdev->pdata;

    cancel_delayed_work_sync(&bdev->work);

    if (pdata->disable)
        pdata->disable(bdev->dev);
}

static int gpio_keys_button_probe(struct platform_device *pdev,
        struct gpio_keys_button_dev **_bdev, int polled)
{
    struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
    struct device *dev = &pdev->dev;
    struct gpio_keys_button_dev *bdev;
    struct gpio_keys_button *buttons;
    int error;
    int i;

    if (polled && !pdata->poll_interval) {
        dev_err(dev, "missing poll_interval value\n");
        return -EINVAL;
    }

    buttons = devm_kzalloc(dev, pdata->nbuttons * sizeof(struct
gpio_keys_button),
               GFP_KERNEL);
    if (!buttons) {
        dev_err(dev, "no memory for button data\n");
        return -ENOMEM;
    }
    memcpy(buttons, pdata->buttons, pdata->nbuttons * sizeof(struct
gpio_keys_button));

    bdev = devm_kzalloc(dev, sizeof(struct gpio_keys_button_dev) +
               pdata->nbuttons * sizeof(struct gpio_keys_button_data),
               GFP_KERNEL);
    if (!bdev) {
        dev_err(dev, "no memory for private data\n");
        return -ENOMEM;
    }

    bdev->polled = polled;

    for (i = 0; i < pdata->nbuttons; i++) {
        struct gpio_keys_button *button = &buttons[i];
        struct gpio_keys_button_data *bdata = &bdev->data[i];
        unsigned int gpio = button->gpio;

        if (button->wakeup) {
            dev_err(dev, DRV_NAME "does not support wakeup\n");
            return -EINVAL;
        }

        error = devm_gpio_request(dev, gpio,
                     button->desc ? button->desc : DRV_NAME);
        if (error) {
            dev_err(dev, "unable to claim gpio %u, err=%d\n",
                gpio, error);
            return error;
        }

        error = gpio_direction_input(gpio);
        if (error) {
            dev_err(dev,
                "unable to set direction on gpio %u, err=%d\n",
                gpio, error);
            return error;
        }

        bdata->can_sleep = gpio_cansleep(gpio);
        bdata->last_state = -1;

        if (bdev->polled)
            bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
                        pdata->poll_interval);
        else
            bdata->threshold = 1;

        bdata->b = &pdata->buttons[i];
    }

    bdev->dev = &pdev->dev;
    bdev->pdata = pdata;
    platform_set_drvdata(pdev, bdev);

    *_bdev = bdev;

    return 0;
}
static int wnr2200_key_probe(struct platform_device *pdev)
{
    struct gpio_keys_platform_data *pdata;
    struct gpio_keys_button_dev *bdev;
    struct device *dev;
    int ret = -1;
    int i;

    dev = &pdev->dev;

    ret = gpio_keys_button_probe(pdev, &bdev, 1);

    if (ret) {
        dev_err(dev, "gpio_keys_button_probe failed\n");
        goto exit;
    }

    INIT_DELAYED_WORK(&bdev->work, gpio_keys_polled_poll);

    pdata = bdev->pdata;

    if (pdata->enable)
        pdata->enable(bdev->dev);

    for (i = 0; i < pdata->nbuttons; i++)
        gpio_keys_polled_check_state(&bdev->data[i]);

    gpio_keys_polled_queue_work(bdev);

exit:
    return ret;
}

static int wnr2200_key_remove(struct platform_device *pdev)
{
    struct gpio_keys_button_dev *bdev = platform_get_drvdata(pdev);

    platform_set_drvdata(pdev, NULL);

    if (bdev->polled)
    gpio_keys_polled_close(bdev);

    return 0;
}



static struct platform_driver wnr2200_key_driver = {
    .probe        = wnr2200_key_probe,
    .remove        = wnr2200_key_remove,
    .driver        = {
        .name    = DRV_NAME,
        .owner    = THIS_MODULE,
        .of_match_table = of_match_ptr(wnr2200_key_of_match),
    },
};

static int __init wnr2200_key_init(void)
{
    int ret = -1;
    struct gpio_keys_platform_data pdata;

    ar9287_gpio_base = ioremap_nocache(AR71XX_PCI_MEM_BASE,
AR71XX_PCI_MEM_SIZE);
    if (ar9287_gpio_base == NULL) {
        printk("ar9287_gpio_base ioremap failed\n");
        goto exit1;
    }
    ret = gpiochip_add(&ar9287_gpio_chip);
    if (ret) {
        printk("gpiochip_add failed\n");
        goto exit2;
    }

    wnr2200_key_devs = platform_device_alloc(DRV_NAME, -1);
    if (!wnr2200_key_devs) {
        printk("platform_device_alloc failed\n");
        ret = -ENOMEM;
        goto exit3;
    }
    memset(&pdata, 0, sizeof(pdata));
    pdata.poll_interval = WNR2200_KEYS_POLL_INTERVAL;
    pdata.nbuttons = ARRAY_SIZE(wnr2200_gpio_keys);
    pdata.buttons = wnr2200_gpio_keys;

    ret = platform_device_add_data(wnr2200_key_devs, &pdata, sizeof(pdata));
    if (ret) {
        printk("platform_device_add_data failed\n");
        goto exit4;
    }

    ret = platform_device_add(wnr2200_key_devs);
    if (ret) {
        printk("platform_device_add failed\n");
        goto exit4;
    }
    ret = platform_driver_register(&wnr2200_key_driver);
    if (ret == 0) {
        return 0;
    }
    printk("platform_driver_register failed\n");
    platform_device_del(wnr2200_key_devs);
exit4:
    platform_device_put(wnr2200_key_devs);
exit3:
    gpiochip_remove(&ar9287_gpio_chip);
exit2:
    iounmap(ar9287_gpio_base);
exit1:
    wnr2200_key_devs = NULL;
    ar9287_gpio_base = NULL;
    return ret;
}

static void __exit wnr2200_key_exit(void)
{
    struct platform_device *dev = wnr2200_key_devs;


    platform_driver_unregister(&wnr2200_key_driver);
    platform_device_unregister(dev);

    gpiochip_remove(&ar9287_gpio_chip);
    iounmap(ar9287_gpio_base);
    ar9287_gpio_base = NULL;
    wnr2200_key_devs = NULL;

}
module_init(wnr2200_key_init);
module_exit(wnr2200_key_exit);


MODULE_AUTHOR("Tao Guo <[email protected]>");
MODULE_AUTHOR("Gabor Juhos <[email protected]>");
MODULE_AUTHOR("Felix Fietkau <[email protected]>");
MODULE_DESCRIPTION("WNR2200 keys driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform: " DRV_NAME);

2014-01-25 John Crispin <[email protected]>:
> Hi,
>
> can you post the driver ?
>
> i have a board with AR9287 where one of the gpios is used, would love to
> test your driver
>
> John
>
>
> On 25/01/2014 10:06, 郭涛 wrote:
>> Dear All
>>
>> I wrote a driver for AR9287 GPIO.  My module always load before ath9k
>> and cause bus error. The read/write PCI bus operate should after ath9k
>> init the PCI bus.
>>
>> I know I can use modules.dep or modules.softdep, but I can't find it
>> in /lib/modules/xx/ dir.
>>
>> I run `modinfo ath9k`, it show
>> "ath9k_hw,mac80211,ath9k_common,compat,cfg80211,ath", but my module
>> show noting.
>>
>> So, I want to know how to make ath9k as my module's depends ?
>>
>> Thanks
>>
>> BRS
>> _______________________________________________
>> openwrt-devel mailing list
>> [email protected]
>> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
>>
> _______________________________________________
> openwrt-devel mailing list
> [email protected]
> https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel
_______________________________________________
openwrt-devel mailing list
[email protected]
https://lists.openwrt.org/cgi-bin/mailman/listinfo/openwrt-devel

Reply via email to