]> rtime.felk.cvut.cz Git - shark/motorek-5200.git/blobdiff - motorek.c
Add load command to the boot script required for newer novaboot version.
[shark/motorek-5200.git] / motorek.c
index 98a2db1fcdadf92a9dae4e4825d9736dff361e77..1ab1bf8a6307af56363cc6061849e2c07b1a430d 100644 (file)
--- a/motorek.c
+++ b/motorek.c
@@ -1,14 +1,33 @@
+/*
+ *     Driver for DC motor connected to MPC5200 based boards.
+ *
+ *     The motor is used for education at Czech Technical University
+ *     in Prague.
+ *
+ *     Authors:
+ *     Michal Sojka            <sojkam1@fel.cvut.cz>
+ *
+ *     This program is free software; you can redistribute it and/or
+ *     modify it under the terms of the GNU General Public License
+ *     as published by the Free Software Foundation; either version
+ *     2 of the License, or (at your option) any later version.
+ */
+
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/mod_devicetable.h>
 #include <linux/of.h>
-#include <asm-powerpc/mpc52xx.h>
+#include <linux/of_platform.h>
+#include <asm/mpc52xx.h>
 #include <linux/interrupt.h>
+#include <linux/slab.h>
 
 struct motorek {
-       struct mpc52xx_gpt *pwmf, *pwmb;
+       struct mpc52xx_gpt *pwmf, *pwmb, *irca, *ircb;
        atomic_t pos;
        int irq;
+       int action;
+       int last_irc_state;
 };
 
 static ssize_t show_position(struct device *dev,
@@ -21,7 +40,31 @@ static ssize_t show_position(struct device *dev,
        return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
 }
 
+static ssize_t show_action(struct device *dev,
+                          struct device_attribute *attr, char *buf)
+{
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct motorek          *m = platform_get_drvdata(pdev);
+
+       int len = snprintf(buf, PAGE_SIZE, "%d\n", m->action);
+       return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+
+static void motorek_action(struct motorek *m, int action_permile);
+static ssize_t store_action(struct device *dev, struct device_attribute *attr,
+                           const char *buf, size_t count)
+{
+       struct platform_device  *pdev = to_platform_device(dev);
+       struct motorek          *m = platform_get_drvdata(pdev);
+
+       int a;
+       sscanf(buf, "%d", &a);
+       motorek_action(m, a);
+       return strnlen(buf, PAGE_SIZE);
+}
+
 DEVICE_ATTR(position,0444,show_position,NULL);
+DEVICE_ATTR(action,0644,show_action,store_action);
 
 #define MPC52xx_GPT_MODE_DISABLED 0
 #define MPC52xx_GPT_MODE_INCAPT   1
@@ -29,7 +72,13 @@ DEVICE_ATTR(position,0444,show_position,NULL);
 #define MPC52xx_GPT_MODE_PWM     3
 #define MPC52xx_GPT_MODE_GPIO    4
 
-#define MPC52xx_GPT_PWM_OP       (1<<8)
+#define MPC52xx_GPT_MODE_GPIO_IN   (0<<4)
+#define MPC52xx_GPT_MODE_GPIO_OUT0 (2<<4)
+#define MPC52xx_GPT_MODE_GPIO_OUT1 (1<<4)
+
+#define MPC52xx_GPT_STATUS_PIN (1<<8)
+
+#define MPC52xx_GPT_PWM_OP       (1<<8) /* PWM polarity */
 
 /* Base clock 132 MHz
  * (/proc/device-tree/cpus/PowerPC,5200@0/bus-frequency) */
@@ -38,25 +87,41 @@ DEVICE_ATTR(position,0444,show_position,NULL);
 
 static void pwm_width(struct mpc52xx_gpt *gpt, u16 width)
 {
-       gpt->pwm = (width<<16) | MPC52xx_GPT_PWM_OP;
+       out_be32(&gpt->pwm, (width<<16) | MPC52xx_GPT_PWM_OP);
+       //printk("pwm: %p=0x%x\n", gpt, width);
+
 }
 
-static void __devinit pwm_init(struct mpc52xx_gpt *gpt)
+static void __devinit init_pwn(struct mpc52xx_gpt *gpt)
 {
-       gpt->count = (1<<16) | PWM_PERIOD;
+       //while (((unsigned)gpt & 0xff) != 0x50) gpt++;
+       out_be32(&gpt->count, (1<<16) | PWM_PERIOD);
        pwm_width(gpt, 0);
-       gpt->mode = MPC52xx_GPT_MODE_PWM;
+       out_be32(&gpt->mode, MPC52xx_GPT_MODE_PWM);
+
+       //out_be32(&gpt->pwm, (500<<16) | MPC52xx_GPT_PWM_OP); /* REMOVE ME */
+
+       //out_be32(&gpt->mode, MPC52xx_GPT_MODE_GPIO | (2<<4));
+       //printk("pwm: %p\n", gpt);
+}
+
+static void __devinit init_input(struct mpc52xx_gpt *gpt)
+{
+       out_be32(&gpt->mode,
+                MPC52xx_GPT_MODE_GPIO |
+                MPC52xx_GPT_MODE_GPIO_IN);
 }
 
 static void  pwm_done(struct mpc52xx_gpt *gpt)
 {
-       gpt->mode = 0;
-       gpt->count = 0;
+       out_be32(&gpt->mode, 0);
+       out_be32(&gpt->count, 0);
        pwm_width(gpt, 0);
 }
 
 static void motorek_action(struct motorek *m, int action_permile)
 {
+       m->action = action_permile;
        if (action_permile >= 0) {
                pwm_width(m->pwmb, 0);
                pwm_width(m->pwmf, +action_permile*PWM_PERIOD/1000);
@@ -68,9 +133,10 @@ static void motorek_action(struct motorek *m, int action_permile)
 
 static int __devinit motorek_init(struct motorek *m)
 {
-       pwm_init(m->pwmf);
-       pwm_init(m->pwmb);
-       motorek_action(m, +45);
+       init_pwn(m->pwmf);
+       init_pwn(m->pwmb);
+       init_input(m->irca);
+       init_input(m->ircb);
        return 0;
 }
 
@@ -80,24 +146,64 @@ static int motorek_done(struct motorek *m)
        pwm_done(m->pwmb);
        return 0;
 }
+#define TRANSITION(old, new) (((old) << 2) | new)
+static const int table[16] = {
+       [TRANSITION(0,1)] = +1,
+       [TRANSITION(1,3)] = +1,
+       [TRANSITION(3,2)] = +1,
+       [TRANSITION(2,0)] = +1,
+
+       [TRANSITION(1,0)] = -1,
+       [TRANSITION(3,1)] = -1,
+       [TRANSITION(2,3)] = -1,
+       [TRANSITION(0,2)] = -1,
+};
 
 static irqreturn_t motorek_irq(int irq, void *dev_id)
 {
        struct motorek *m = dev_id;
-       atomic_inc(&m->pos);
+       int irc_state =
+               ((in_be32(&m->irca->status) & MPC52xx_GPT_STATUS_PIN) ? 1 : 0) | \
+               ((in_be32(&m->ircb->status) & MPC52xx_GPT_STATUS_PIN) ? 2 : 0);
+       atomic_add(table[TRANSITION(m->last_irc_state, irc_state)], &m->pos);
+       m->last_irc_state = irc_state;
        return IRQ_HANDLED;
 }
 
-/* mpc5200 device tree match tables */
-static struct of_device_id mpc5200_gpt_ids[] __initdata = {
-       { .compatible = "fsl,mpc5200-gpt", },
+static struct of_device_id mpc5200_gpt_match[] = {
        { .compatible = "mpc5200-gpt", },
-       {}
+       { .compatible = "fsl,mpc5200-gpt", },
+       {},
 };
 
-static int __devinit motorek_probe(struct platform_device *dev)
+struct mpc52xx_gpt __iomem *iomap_gpt_by_phandle_prop(struct device_node *dn, const char *prop)
 {
        struct device_node *np;
+       struct mpc52xx_gpt __iomem *gpt = NULL;
+       const phandle *phandle;
+
+       phandle = of_get_property(dn, prop, NULL);
+       if (!phandle)
+               return NULL;
+
+       np = of_find_node_by_phandle(*phandle);
+       if (!np)
+               return NULL;
+
+       if (!of_match_node(mpc5200_gpt_match, np)) {
+               printk(KERN_ERR "property %s doesn't refer to GPT\n", prop);
+               goto out;
+       }
+
+       gpt = of_iomap(np, 0);
+out:
+       of_node_put(np);
+       return gpt;
+}
+
+static int __devinit motorek_probe(struct platform_device* dev)
+{
+       struct device_node *dn = dev->dev.of_node;
        struct motorek *m;
        int err;
 
@@ -105,91 +211,116 @@ static int __devinit motorek_probe(struct platform_device *dev)
        if (!m)
                return -ENOMEM;
 
-       for_each_matching_node(np, mpc5200_gpt_ids) {
-               const void *prop;
-               int i;
-               
-               prop = of_get_property(np, "cell-index", NULL);
-               if (prop) {
-                       i = *(u32 *)prop;
-                       switch (i) {
-                       case 1:
-                               m->pwmf = of_iomap(np, 0);
-                               break;
-                       case 2:
-                               m->pwmb = of_iomap(np, 0);
-                               break;
-                       }
-               }
-       }
-       if (!m->pwmf || !m->pwmb) {
-               printk(KERN_ERR "%s() mmap failed\n", __func__);
-               return -ENXIO;
-       }
-
-/*     /\* FIXME: This should be specified in device-tree *\/ */
-/*     m->irq = irq_create_of_mapping( */
-
-/*     err = request_irq(m->irq, motorek_irq, 0, "motorek", m); */
-/*     if (err) */
-/*             return err; */
+       err = -ENODEV;
+
+       m->pwmf = iomap_gpt_by_phandle_prop(dn, "pwmf");
+       if (!m->pwmf)
+               goto err_free;
+       m->pwmb = iomap_gpt_by_phandle_prop(dn, "pwmb");
+       if (!m->pwmb)
+               goto err_unmap_pf;
+       m->irca = iomap_gpt_by_phandle_prop(dn, "irca");
+       if (!m->irca)
+               goto err_unmap_pb;
+       m->ircb = iomap_gpt_by_phandle_prop(dn, "ircb");
+       if (!m->ircb)
+               goto err_unmap_ia;
+
+       m->irq = irq_of_parse_and_map(dn, 0);
+       if (m->irq == NO_IRQ)
+               goto err_unmap_ib;
+       err = request_irq(m->irq, motorek_irq, 0, "motorek", m);
+       if (err)
+               goto err_unmap_ib;
 
        motorek_init(m);
 
        platform_set_drvdata(dev, m);
-
        err = device_create_file(&dev->dev,&dev_attr_position);
        if (err)
-               return err;
+               goto err_irq;
+       err = device_create_file(&dev->dev,&dev_attr_action);
+       if (err)
+               goto err_irq;
+
+       printk(KERN_NOTICE "Motorek initialized\n");
 
        return 0;
+
+err_irq:
+       free_irq(m->irq, m);
+err_unmap_ib:
+       iounmap(m->pwmb);
+err_unmap_ia:
+       iounmap(m->pwmf);
+err_unmap_pb:
+       iounmap(m->pwmb);
+err_unmap_pf:
+       iounmap(m->pwmf);
+err_free:
+       kfree(m);
+       return err;
 }
 
-static int __devexit motorek_remove(struct platform_device *dev)
+static int __devexit motorek_remove(struct platform_devicedev)
 {
        struct motorek *m;
-       m = platform_get_drvdata(dev);
 
+       printk(KERN_NOTICE "Removing motorek\n");
+       m = platform_get_drvdata(dev);
+       free_irq(m->irq, m);
        motorek_done(m);
+       kfree(m);
        return 0;
 }
 
-
-
+static struct of_device_id motorek_match[] = {
+       { .type = "motorek", },
+       {},
+};
 static struct platform_driver motorek_driver = {
        .probe          = motorek_probe,
        .remove         = __devexit_p(motorek_remove),
-       .driver         = {
-               .name   = "motorek",
-               .owner  = THIS_MODULE,
+       .driver         = {
+               .name           = "motorek",
+               .owner          = THIS_MODULE,
+               .of_match_table = motorek_match,
        },
 };
 
 
-static struct platform_device *motorek_pdev;
-
 static int __init motorek_init_module(void)
 {
        int ret;
-       motorek_pdev = platform_device_alloc("motorek", 0);
-       if (!motorek_pdev)
-               return -ENOMEM;
-       ret = platform_device_add(motorek_pdev);
-       if (ret) {
-               platform_device_put(motorek_pdev);
-               return ret;
-       }
+       struct platform_device *dev;
+       struct device_node *dn;
+
+       for_each_node_by_type(dn, "motorek") {
+               if (!of_find_device_by_node(dn)) {
+                       dev = of_platform_device_create(dn, NULL, NULL);
+                       if (!dev)
+                               return -ENOMEM;
+               }
+       };
 
        ret = platform_driver_register(&motorek_driver);
-       return 0;
+       return ret;
 }
-module_init(motorek_init_module);
 
 static void __exit motorek_exit_module(void)
 {
-       platform_device_unregister(motorek_pdev);
+       struct platform_device *dev;
+       struct device_node *dn;
+
+       for_each_node_by_type(dn, "motorek") {
+               while ((dev = of_find_device_by_node(dn))) {
+                       of_device_unregister(dev);
+               }
+       }
        platform_driver_unregister(&motorek_driver);
 }
+
+module_init(motorek_init_module);
 module_exit(motorek_exit_module);