ucmb: Add support for a reset line to the microcontroller.
authorMichael Büsch <mb@bu3sch.de>
Fri, 20 Feb 2009 23:24:55 +0000 (23:24 +0000)
committerMichael Büsch <mb@bu3sch.de>
Fri, 20 Feb 2009 23:24:55 +0000 (23:24 +0000)
SVN-Revision: 14586

utils/ucmb-tools/tools/ucmb.c
utils/ucmb/driver/ucmb.c
utils/ucmb/driver/ucmb.h

index 85ca8ed120ef8a219afd49369bd6187fd140f188..0bf82e42aa949d8e57a12bed9471989972008206 100644 (file)
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/ioctl.h>
 
 
 #define UCMB_DEV       "/dev/ucmb"
 
+#define __UCMB_IOCTL           ('U'|'C'|'M'|'B')
+#define UCMB_IOCTL_RESETUC     _IO(__UCMB_IOCTL, 0)
+
 
 static void usage(int argc, char **argv)
 {
-       fprintf(stderr, "Usage: %s read|write\n", argv[0]);
+       fprintf(stderr, "Usage: %s read|write|reset [" UCMB_DEV "]\n", argv[0]);
 }
 
 int main(int argc, char **argv)
 {
-       const char *command;
-       int errcode = 0;
+       const char *command, *devpath = UCMB_DEV;
+       int res, errcode = 0;
        int ucmb_fd;
        char *buf;
        size_t count, buflen;
        ssize_t nrbytes;
 
-       if (argc != 2) {
+       if (argc != 2 && argc != 3) {
                usage(argc, argv);
                return 1;
        }
+       if (argc == 3)
+               devpath = argv[2];
        command = argv[1];
 
-       ucmb_fd = open(UCMB_DEV, O_RDWR);
+       ucmb_fd = open(devpath, O_RDWR);
        if (ucmb_fd == -1) {
                fprintf(stderr, "Failed to open %s\n", UCMB_DEV);
                errcode = 1;
@@ -58,22 +65,34 @@ int main(int argc, char **argv)
                if (nrbytes < 0) {
                        fprintf(stderr, "Failed to read UCMB: %s (%d)\n",
                                strerror(errno), errno);
+                       errcode = 1;
                        goto out_free;
                }
                if (fwrite(buf, nrbytes, 1, stdout) != 1) {
                        fprintf(stderr, "Failed to write stdout\n");
+                       errcode = 1;
                        goto out_free;
                }
        } else if (strcasecmp(command, "write") == 0) {
                count = fread(buf, 1, buflen, stdin);
                if (!count) {
                        fprintf(stderr, "Failed to read stdin\n");
+                       errcode = 1;
                        goto out_free;
                }
                nrbytes = write(ucmb_fd, buf, count);
                if (nrbytes != count) {
                        fprintf(stderr, "Failed to write UCMB: %s (%d)\n",
                                strerror(errno), errno);
+                       errcode = 1;
+                       goto out_free;
+               }
+       } else if (strcasecmp(command, "reset") == 0) {
+               res = ioctl(ucmb_fd, UCMB_IOCTL_RESETUC);
+               if (res) {
+                       fprintf(stderr, "RESET ioctl failed: %s (%d)\n",
+                               strerror(res < 0 ? -res : res), res);
+                       errcode = 1;
                        goto out_free;
                }
        } else {
index 2b7232c40d83464ab25820e2d23dcb65d403e20b..37fb889a33e9209eeab8db9b35619a40b1dcbc9f 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/spi_gpio.h>
 #include <linux/spi/spi_bitbang.h>
+#include <linux/gpio.h>
 #include <linux/gfp.h>
 #include <linux/delay.h>
 #include <linux/crc16.h>
@@ -31,7 +32,11 @@ MODULE_AUTHOR("Michael Buesch");
 
 
 struct ucmb {
+       struct mutex mutex;
+
        unsigned int msg_delay_ms;
+       unsigned int gpio_reset;
+       bool reset_activelow;
 
        /* Misc character device driver */
        struct miscdevice mdev;
@@ -47,7 +52,7 @@ struct ucmb {
 
 struct ucmb_message_hdr {
        __le16 magic;           /* UCMB_MAGIC */
-       __le16 len;             /* Payload length (excluding header) */
+       __le16 len;             /* Payload length (excluding header and footer) */
 } __attribute__((packed));
 
 struct ucmb_message_footer {
@@ -75,14 +80,16 @@ static int ucmb_spi_busnum_count = 1337;
 
 static struct ucmb_platform_data ucmb_list[] = {
        { //FIXME don't define it here.
-               .name           = "ucmb",
-               .gpio_cs        = 3,
-               .gpio_sck       = 0,
-               .gpio_miso      = 1,
-               .gpio_mosi      = 2,
-               .mode           = SPI_MODE_0,
-               .max_speed_hz   = 128000, /* Hz */
-               .msg_delay_ms   = 1, /* mS */
+               .name                   = "ucmb",
+               .gpio_cs                = SPI_GPIO_NO_CHIPSELECT,
+               .gpio_sck               = 0,
+               .gpio_miso              = 1,
+               .gpio_mosi              = 2,
+               .gpio_reset             = 3,
+               .reset_activelow        = 0,
+               .mode                   = SPI_MODE_0,
+               .max_speed_hz           = 128000, /* Hz */
+               .msg_delay_ms           = 1, /* mS */
        },
 };
 
@@ -107,6 +114,26 @@ static struct spi_driver ucmb_spi_driver = {
        .remove         = __devexit_p(ucmb_spi_remove),
 };
 
+static void ucmb_toggle_reset_line(struct ucmb *ucmb, bool active)
+{
+       if (ucmb->reset_activelow)
+               active = !active;
+       gpio_set_value(ucmb->gpio_reset, active);
+}
+
+static int ucmb_reset_microcontroller(struct ucmb *ucmb)
+{
+       if (ucmb->gpio_reset == UCMB_NO_RESET)
+               return -ENODEV;
+
+       ucmb_toggle_reset_line(ucmb, 1);
+       msleep(50);
+       ucmb_toggle_reset_line(ucmb, 0);
+       msleep(10);
+
+       return 0;
+}
+
 static int ucmb_status_code_to_errno(enum ucmb_status_code code)
 {
        switch (code) {
@@ -129,6 +156,25 @@ static inline struct ucmb * filp_to_ucmb(struct file *filp)
        return container_of(filp->f_op, struct ucmb, mdev_fops);
 }
 
+static int ucmb_ioctl(struct inode *inode, struct file *filp,
+                     unsigned int cmd, unsigned long arg)
+{
+       struct ucmb *ucmb = filp_to_ucmb(filp);
+       int ret = 0;
+
+       mutex_lock(&ucmb->mutex);
+       switch (cmd) {
+       case UCMB_IOCTL_RESETUC:
+               ret = ucmb_reset_microcontroller(ucmb);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+       mutex_unlock(&ucmb->mutex);
+
+       return ret;
+}
+
 static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
                         size_t size, loff_t *offp)
 {
@@ -140,6 +186,8 @@ static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
        struct ucmb_status status = { .magic = cpu_to_le16(UCMB_MAGIC), };
        u16 crc = 0xFFFF;
 
+       mutex_lock(&ucmb->mutex);
+
        size = min_t(size_t, size, PAGE_SIZE);
 
        err = -ENOMEM;
@@ -168,7 +216,7 @@ static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
        if (err)
                goto out_free;
 
-       crc = crc16(crc, &hdr, sizeof(hdr));
+       crc = crc16(crc, (u8 *)&hdr, sizeof(hdr));
        crc = crc16(crc, buf, size);
        crc ^= 0xFFFF;
        if (crc != le16_to_cpu(footer.crc)) {
@@ -193,6 +241,8 @@ out_send_status:
 out_free:
        free_page((unsigned long)buf);
 out:
+       mutex_unlock(&ucmb->mutex);
+
        return err ? err : size;
 }
 
@@ -210,6 +260,8 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
        struct spi_transfer spi_data_xfer;
        struct spi_message spi_msg;
 
+       mutex_lock(&ucmb->mutex);
+
        err = -ENOMEM;
        buf = (char *)__get_free_page(GFP_KERNEL);
        if (!buf)
@@ -221,7 +273,7 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
                goto out_free;
        hdr.len = cpu_to_le16(size);
 
-       footer.crc = crc16(footer.crc, &hdr, sizeof(hdr));
+       footer.crc = crc16(footer.crc, (u8 *)&hdr, sizeof(hdr));
        footer.crc = crc16(footer.crc, buf, size);
        footer.crc ^= 0xFFFF;
 
@@ -269,6 +321,8 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
 out_free:
        free_page((unsigned long)buf);
 out:
+       mutex_unlock(&ucmb->mutex);
+
        return err ? err : size;
 }
 
@@ -287,7 +341,10 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
        ucmb = kzalloc(sizeof(struct ucmb), GFP_KERNEL);
        if (!ucmb)
                return -ENOMEM;
+       mutex_init(&ucmb->mutex);
        ucmb->msg_delay_ms = pdata->msg_delay_ms;
+       ucmb->gpio_reset = pdata->gpio_reset;
+       ucmb->reset_activelow = pdata->reset_activelow;
 
        /* Create the SPI GPIO bus master. */
 
@@ -336,6 +393,25 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
                goto err_free_spi_device;
        }
 
+       /* Initialize the RESET line. */
+
+       if (pdata->gpio_reset != UCMB_NO_RESET) {
+               err = gpio_request(pdata->gpio_reset, pdata->name);
+               if (err) {
+                       printk(KERN_ERR PFX
+                              "Failed to request RESET GPIO line\n");
+                       goto err_unreg_spi_device;
+               }
+               err = gpio_direction_output(pdata->gpio_reset,
+                                           pdata->reset_activelow);
+               if (err) {
+                       printk(KERN_ERR PFX
+                              "Failed to set RESET GPIO direction\n");
+                       goto err_free_reset_gpio;
+               }
+               ucmb_reset_microcontroller(ucmb);
+       }
+
        /* Create the Misc char device. */
 
        ucmb->mdev.minor = MISC_DYNAMIC_MINOR;
@@ -343,13 +419,14 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
        ucmb->mdev.parent = &pdev->dev;
        ucmb->mdev_fops.read = ucmb_read;
        ucmb->mdev_fops.write = ucmb_write;
+       ucmb->mdev_fops.ioctl = ucmb_ioctl;
        ucmb->mdev.fops = &ucmb->mdev_fops;
 
        err = misc_register(&ucmb->mdev);
        if (err) {
                printk(KERN_ERR PFX "Failed to register miscdev %s\n",
                       ucmb->mdev.name);
-               goto err_unreg_spi_device;
+               goto err_free_reset_gpio;
        }
 
        platform_set_drvdata(pdev, ucmb);
@@ -358,6 +435,9 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
 
        return 0;
 
+err_free_reset_gpio:
+       if (pdata->gpio_reset != UCMB_NO_RESET)
+               gpio_free(pdata->gpio_reset);
 err_unreg_spi_device:
        spi_unregister_device(ucmb->sdev);
 err_free_spi_device:
@@ -380,6 +460,8 @@ static int __devexit ucmb_remove(struct platform_device *pdev)
                printk(KERN_ERR PFX "Failed to unregister miscdev %s\n",
                       ucmb->mdev.name);
        }
+       if (ucmb->gpio_reset != UCMB_NO_RESET)
+               gpio_free(ucmb->gpio_reset);
        spi_unregister_device(ucmb->sdev);
        spi_dev_put(ucmb->sdev);
        platform_device_unregister(&ucmb->spi_gpio_pdev);
@@ -396,7 +478,7 @@ static struct platform_driver ucmb_driver = {
                .owner  = THIS_MODULE,
        },
        .probe          = ucmb_probe,
-       .remove         = __devexit_p(ucmb_probe),
+       .remove         = __devexit_p(ucmb_remove),
 };
 
 static int ucmb_modinit(void)
index 94fdc6420e75abaf42a0ac38d95814ff5e49377e..8455dd00257b168ab5ba78f760063264a667e7e4 100644 (file)
@@ -2,6 +2,17 @@
 #define LINUX_UCMB_H_
 
 #include <linux/types.h>
+#include <linux/ioctl.h>
+
+
+/* IOCTLs */
+#define __UCMB_IOCTL           ('U'|'C'|'M'|'B')
+
+/** UCMB_IOCTL_RESETUC - Reset the microcontroller. */
+#define UCMB_IOCTL_RESETUC     _IO(__UCMB_IOCTL, 0)
+
+
+#ifdef __KERNEL__
 
 /**
  * struct ucmb_platform_data - UCMB device descriptor
  * @name:              The name of the device. This will also be the name of
  *                     the misc char device.
  *
- * @gpio_cs:           The chipselect GPIO pin. Can be SPI_GPIO_NO_CHIPSELECT.
+ * @gpio_cs:           The chipselect GPIO pin. Can be SPI_GPIO_NO_CHIPSELECT,
+ *                     if chipselect is not used.
  * @gpio_sck:          The clock GPIO pin.
  * @gpio_miso:         The master-in slave-out GPIO pin.
  * @gpio_mosi:         The master-out slave-in GPIO pin.
  *
+ * @gpio_reset:                The GPIO pin to the microcontroller reset line.
+ *                     Can be UCMB_NO_RESET, if reset GPIO is not used.
+ * @reset_activelow:   If true, @gpio_reset is considered to be active
+ *                     on logical 0 (inverted).
+ *
  * @mode:              The SPI bus mode. SPI_MODE_*
  * @max_speed_hz:      The bus speed, in Hz. If zero the speed is not limited.
  * @msg_delay_ms:      The message delay time, in milliseconds.
@@ -28,6 +45,9 @@ struct ucmb_platform_data {
        unsigned int gpio_miso;
        unsigned int gpio_mosi;
 
+       unsigned int gpio_reset;
+       bool reset_activelow;
+
        u8 mode;
        u32 max_speed_hz;
        unsigned int msg_delay_ms;
@@ -35,4 +55,8 @@ struct ucmb_platform_data {
        struct platform_device *pdev; /* internal */
 };
 
+#define UCMB_NO_RESET          ((unsigned int)-1)
+
+
+#endif /* __KERNEL__ */
 #endif /* LINUX_UCMB_H_ */