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
This commit is contained in:
mb 2009-02-20 23:24:55 +00:00
parent 5329011836
commit 0d2ac176e0
3 changed files with 144 additions and 19 deletions

View File

@ -13,32 +13,39 @@
#include <unistd.h> #include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <errno.h> #include <errno.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#define UCMB_DEV "/dev/ucmb" #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) 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) int main(int argc, char **argv)
{ {
const char *command; const char *command, *devpath = UCMB_DEV;
int errcode = 0; int res, errcode = 0;
int ucmb_fd; int ucmb_fd;
char *buf; char *buf;
size_t count, buflen; size_t count, buflen;
ssize_t nrbytes; ssize_t nrbytes;
if (argc != 2) { if (argc != 2 && argc != 3) {
usage(argc, argv); usage(argc, argv);
return 1; return 1;
} }
if (argc == 3)
devpath = argv[2];
command = argv[1]; command = argv[1];
ucmb_fd = open(UCMB_DEV, O_RDWR); ucmb_fd = open(devpath, O_RDWR);
if (ucmb_fd == -1) { if (ucmb_fd == -1) {
fprintf(stderr, "Failed to open %s\n", UCMB_DEV); fprintf(stderr, "Failed to open %s\n", UCMB_DEV);
errcode = 1; errcode = 1;
@ -58,22 +65,34 @@ int main(int argc, char **argv)
if (nrbytes < 0) { if (nrbytes < 0) {
fprintf(stderr, "Failed to read UCMB: %s (%d)\n", fprintf(stderr, "Failed to read UCMB: %s (%d)\n",
strerror(errno), errno); strerror(errno), errno);
errcode = 1;
goto out_free; goto out_free;
} }
if (fwrite(buf, nrbytes, 1, stdout) != 1) { if (fwrite(buf, nrbytes, 1, stdout) != 1) {
fprintf(stderr, "Failed to write stdout\n"); fprintf(stderr, "Failed to write stdout\n");
errcode = 1;
goto out_free; goto out_free;
} }
} else if (strcasecmp(command, "write") == 0) { } else if (strcasecmp(command, "write") == 0) {
count = fread(buf, 1, buflen, stdin); count = fread(buf, 1, buflen, stdin);
if (!count) { if (!count) {
fprintf(stderr, "Failed to read stdin\n"); fprintf(stderr, "Failed to read stdin\n");
errcode = 1;
goto out_free; goto out_free;
} }
nrbytes = write(ucmb_fd, buf, count); nrbytes = write(ucmb_fd, buf, count);
if (nrbytes != count) { if (nrbytes != count) {
fprintf(stderr, "Failed to write UCMB: %s (%d)\n", fprintf(stderr, "Failed to write UCMB: %s (%d)\n",
strerror(errno), errno); 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; goto out_free;
} }
} else { } else {

View File

@ -15,6 +15,7 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/spi/spi_gpio.h> #include <linux/spi/spi_gpio.h>
#include <linux/spi/spi_bitbang.h> #include <linux/spi/spi_bitbang.h>
#include <linux/gpio.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/crc16.h> #include <linux/crc16.h>
@ -31,7 +32,11 @@ MODULE_AUTHOR("Michael Buesch");
struct ucmb { struct ucmb {
struct mutex mutex;
unsigned int msg_delay_ms; unsigned int msg_delay_ms;
unsigned int gpio_reset;
bool reset_activelow;
/* Misc character device driver */ /* Misc character device driver */
struct miscdevice mdev; struct miscdevice mdev;
@ -47,7 +52,7 @@ struct ucmb {
struct ucmb_message_hdr { struct ucmb_message_hdr {
__le16 magic; /* UCMB_MAGIC */ __le16 magic; /* UCMB_MAGIC */
__le16 len; /* Payload length (excluding header) */ __le16 len; /* Payload length (excluding header and footer) */
} __attribute__((packed)); } __attribute__((packed));
struct ucmb_message_footer { struct ucmb_message_footer {
@ -75,14 +80,16 @@ static int ucmb_spi_busnum_count = 1337;
static struct ucmb_platform_data ucmb_list[] = { static struct ucmb_platform_data ucmb_list[] = {
{ //FIXME don't define it here. { //FIXME don't define it here.
.name = "ucmb", .name = "ucmb",
.gpio_cs = 3, .gpio_cs = SPI_GPIO_NO_CHIPSELECT,
.gpio_sck = 0, .gpio_sck = 0,
.gpio_miso = 1, .gpio_miso = 1,
.gpio_mosi = 2, .gpio_mosi = 2,
.mode = SPI_MODE_0, .gpio_reset = 3,
.max_speed_hz = 128000, /* Hz */ .reset_activelow = 0,
.msg_delay_ms = 1, /* mS */ .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), .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) static int ucmb_status_code_to_errno(enum ucmb_status_code code)
{ {
switch (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); 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, static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
size_t size, loff_t *offp) 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), }; struct ucmb_status status = { .magic = cpu_to_le16(UCMB_MAGIC), };
u16 crc = 0xFFFF; u16 crc = 0xFFFF;
mutex_lock(&ucmb->mutex);
size = min_t(size_t, size, PAGE_SIZE); size = min_t(size_t, size, PAGE_SIZE);
err = -ENOMEM; err = -ENOMEM;
@ -168,7 +216,7 @@ static ssize_t ucmb_read(struct file *filp, char __user *user_buf,
if (err) if (err)
goto out_free; goto out_free;
crc = crc16(crc, &hdr, sizeof(hdr)); crc = crc16(crc, (u8 *)&hdr, sizeof(hdr));
crc = crc16(crc, buf, size); crc = crc16(crc, buf, size);
crc ^= 0xFFFF; crc ^= 0xFFFF;
if (crc != le16_to_cpu(footer.crc)) { if (crc != le16_to_cpu(footer.crc)) {
@ -193,6 +241,8 @@ out_send_status:
out_free: out_free:
free_page((unsigned long)buf); free_page((unsigned long)buf);
out: out:
mutex_unlock(&ucmb->mutex);
return err ? err : size; 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_transfer spi_data_xfer;
struct spi_message spi_msg; struct spi_message spi_msg;
mutex_lock(&ucmb->mutex);
err = -ENOMEM; err = -ENOMEM;
buf = (char *)__get_free_page(GFP_KERNEL); buf = (char *)__get_free_page(GFP_KERNEL);
if (!buf) if (!buf)
@ -221,7 +273,7 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
goto out_free; goto out_free;
hdr.len = cpu_to_le16(size); 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 = crc16(footer.crc, buf, size);
footer.crc ^= 0xFFFF; footer.crc ^= 0xFFFF;
@ -269,6 +321,8 @@ static ssize_t ucmb_write(struct file *filp, const char __user *user_buf,
out_free: out_free:
free_page((unsigned long)buf); free_page((unsigned long)buf);
out: out:
mutex_unlock(&ucmb->mutex);
return err ? err : size; return err ? err : size;
} }
@ -287,7 +341,10 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
ucmb = kzalloc(sizeof(struct ucmb), GFP_KERNEL); ucmb = kzalloc(sizeof(struct ucmb), GFP_KERNEL);
if (!ucmb) if (!ucmb)
return -ENOMEM; return -ENOMEM;
mutex_init(&ucmb->mutex);
ucmb->msg_delay_ms = pdata->msg_delay_ms; 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. */ /* Create the SPI GPIO bus master. */
@ -336,6 +393,25 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
goto err_free_spi_device; 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. */ /* Create the Misc char device. */
ucmb->mdev.minor = MISC_DYNAMIC_MINOR; 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.parent = &pdev->dev;
ucmb->mdev_fops.read = ucmb_read; ucmb->mdev_fops.read = ucmb_read;
ucmb->mdev_fops.write = ucmb_write; ucmb->mdev_fops.write = ucmb_write;
ucmb->mdev_fops.ioctl = ucmb_ioctl;
ucmb->mdev.fops = &ucmb->mdev_fops; ucmb->mdev.fops = &ucmb->mdev_fops;
err = misc_register(&ucmb->mdev); err = misc_register(&ucmb->mdev);
if (err) { if (err) {
printk(KERN_ERR PFX "Failed to register miscdev %s\n", printk(KERN_ERR PFX "Failed to register miscdev %s\n",
ucmb->mdev.name); ucmb->mdev.name);
goto err_unreg_spi_device; goto err_free_reset_gpio;
} }
platform_set_drvdata(pdev, ucmb); platform_set_drvdata(pdev, ucmb);
@ -358,6 +435,9 @@ static int __devinit ucmb_probe(struct platform_device *pdev)
return 0; return 0;
err_free_reset_gpio:
if (pdata->gpio_reset != UCMB_NO_RESET)
gpio_free(pdata->gpio_reset);
err_unreg_spi_device: err_unreg_spi_device:
spi_unregister_device(ucmb->sdev); spi_unregister_device(ucmb->sdev);
err_free_spi_device: 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", printk(KERN_ERR PFX "Failed to unregister miscdev %s\n",
ucmb->mdev.name); ucmb->mdev.name);
} }
if (ucmb->gpio_reset != UCMB_NO_RESET)
gpio_free(ucmb->gpio_reset);
spi_unregister_device(ucmb->sdev); spi_unregister_device(ucmb->sdev);
spi_dev_put(ucmb->sdev); spi_dev_put(ucmb->sdev);
platform_device_unregister(&ucmb->spi_gpio_pdev); platform_device_unregister(&ucmb->spi_gpio_pdev);
@ -396,7 +478,7 @@ static struct platform_driver ucmb_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = ucmb_probe, .probe = ucmb_probe,
.remove = __devexit_p(ucmb_probe), .remove = __devexit_p(ucmb_remove),
}; };
static int ucmb_modinit(void) static int ucmb_modinit(void)

View File

@ -2,6 +2,17 @@
#define LINUX_UCMB_H_ #define LINUX_UCMB_H_
#include <linux/types.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 * struct ucmb_platform_data - UCMB device descriptor
@ -9,11 +20,17 @@
* @name: The name of the device. This will also be the name of * @name: The name of the device. This will also be the name of
* the misc char device. * 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_sck: The clock GPIO pin.
* @gpio_miso: The master-in slave-out GPIO pin. * @gpio_miso: The master-in slave-out GPIO pin.
* @gpio_mosi: The master-out slave-in 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_* * @mode: The SPI bus mode. SPI_MODE_*
* @max_speed_hz: The bus speed, in Hz. If zero the speed is not limited. * @max_speed_hz: The bus speed, in Hz. If zero the speed is not limited.
* @msg_delay_ms: The message delay time, in milliseconds. * @msg_delay_ms: The message delay time, in milliseconds.
@ -28,6 +45,9 @@ struct ucmb_platform_data {
unsigned int gpio_miso; unsigned int gpio_miso;
unsigned int gpio_mosi; unsigned int gpio_mosi;
unsigned int gpio_reset;
bool reset_activelow;
u8 mode; u8 mode;
u32 max_speed_hz; u32 max_speed_hz;
unsigned int msg_delay_ms; unsigned int msg_delay_ms;
@ -35,4 +55,8 @@ struct ucmb_platform_data {
struct platform_device *pdev; /* internal */ struct platform_device *pdev; /* internal */
}; };
#define UCMB_NO_RESET ((unsigned int)-1)
#endif /* __KERNEL__ */
#endif /* LINUX_UCMB_H_ */ #endif /* LINUX_UCMB_H_ */