#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;
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 {
#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>
struct ucmb {
+ struct mutex mutex;
+
unsigned int msg_delay_ms;
+ unsigned int gpio_reset;
+ bool reset_activelow;
/* Misc character device driver */
struct miscdevice mdev;
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 {
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 */
},
};
.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) {
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)
{
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;
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)) {
out_free:
free_page((unsigned long)buf);
out:
+ mutex_unlock(&ucmb->mutex);
+
return err ? err : size;
}
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)
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;
out_free:
free_page((unsigned long)buf);
out:
+ mutex_unlock(&ucmb->mutex);
+
return err ? err : size;
}
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. */
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;
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);
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:
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);
.owner = THIS_MODULE,
},
.probe = ucmb_probe,
- .remove = __devexit_p(ucmb_probe),
+ .remove = __devexit_p(ucmb_remove),
};
static int ucmb_modinit(void)
#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.
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;
struct platform_device *pdev; /* internal */
};
+#define UCMB_NO_RESET ((unsigned int)-1)
+
+
+#endif /* __KERNEL__ */
#endif /* LINUX_UCMB_H_ */