+/*
+ * 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>
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,
#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) */
static void pwm_width(struct mpc52xx_gpt *gpt, u16 width)
{
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)
{
+ //while (((unsigned)gpt & 0xff) != 0x50) gpt++;
out_be32(&gpt->count, (1<<16) | PWM_PERIOD);
pwm_width(gpt, 0);
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)
static int __devinit motorek_init(struct motorek *m)
{
- pwm_init(m->pwmf);
- pwm_init(m->pwmb);
- //motorek_action(m, +0);
+ init_pwn(m->pwmf);
+ init_pwn(m->pwmb);
+ init_input(m->irca);
+ init_input(m->ircb);
return 0;
}
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 of_device* dev,
+ const struct of_device_id *match)
+{
+ struct device_node *dn = dev->node;
struct motorek *m;
int err;
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 2:
- m->pwmf = of_iomap(np, 0);
- break;
- case 3:
- 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)
- return 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 of_device* dev)
{
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 platform_driver motorek_driver = {
+static struct of_device_id motorek_match[] = {
+ { .type = "motorek", },
+ {},
+};
+static struct of_platform_driver motorek_driver = {
+ .owner = THIS_MODULE,
+ .name = "motorek",
+ .match_table = motorek_match,
.probe = motorek_probe,
.remove = __devexit_p(motorek_remove),
- .driver = {
- .name = "motorek",
- .owner = THIS_MODULE,
- },
};
-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 of_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;
+ ret = of_register_platform_driver(&motorek_driver);
+ return ret;
}
-module_init(motorek_init_module);
static void __exit motorek_exit_module(void)
{
- platform_device_unregister(motorek_pdev);
- platform_driver_unregister(&motorek_driver);
+ struct of_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);
+ }
+ }
+ of_unregister_platform_driver(&motorek_driver);
}
+
+module_init(motorek_init_module);
module_exit(motorek_exit_module);