From 0d2ac176e02e34d524c6dd59dccdc2da7c851987 Mon Sep 17 00:00:00 2001 From: mb Date: Fri, 20 Feb 2009 23:24:55 +0000 Subject: [PATCH] ucmb: Add support for a reset line to the microcontroller. git-svn-id: svn://svn.openwrt.org/openwrt/packages@14586 3c298f89-4303-0410-b956-a3cf2f4a3e73 --- utils/ucmb-tools/tools/ucmb.c | 29 +++++++-- utils/ucmb/driver/ucmb.c | 108 ++++++++++++++++++++++++++++++---- utils/ucmb/driver/ucmb.h | 26 +++++++- 3 files changed, 144 insertions(+), 19 deletions(-) diff --git a/utils/ucmb-tools/tools/ucmb.c b/utils/ucmb-tools/tools/ucmb.c index 85ca8ed12..0bf82e42a 100644 --- a/utils/ucmb-tools/tools/ucmb.c +++ b/utils/ucmb-tools/tools/ucmb.c @@ -13,32 +13,39 @@ #include #include #include +#include +#include #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 { diff --git a/utils/ucmb/driver/ucmb.c b/utils/ucmb/driver/ucmb.c index 2b7232c40..37fb889a3 100644 --- a/utils/ucmb/driver/ucmb.c +++ b/utils/ucmb/driver/ucmb.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -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) diff --git a/utils/ucmb/driver/ucmb.h b/utils/ucmb/driver/ucmb.h index 94fdc6420..8455dd002 100644 --- a/utils/ucmb/driver/ucmb.h +++ b/utils/ucmb/driver/ucmb.h @@ -2,6 +2,17 @@ #define LINUX_UCMB_H_ #include +#include + + +/* 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 @@ -9,11 +20,17 @@ * @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_ */