struct ucmb {
struct mutex mutex;
- unsigned int msg_delay_ms;
+ bool is_open;
+
+ unsigned int msg_delay_usec;
unsigned int gpio_reset;
bool reset_activelow;
struct platform_device spi_gpio_pdev;
};
+#define UCMB_MAX_MSG_DELAY (10 * 1000 * 1000) /* 10 seconds */
+
+
struct ucmb_message_hdr {
__le16 magic; /* UCMB_MAGIC */
__le16 len; /* Payload length (excluding header and footer) */
static int ucmb_spi_busnum_count = 1337;
-
-
-static struct ucmb_platform_data ucmb_list[] = {
- { //FIXME don't define it here.
- .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 */
- },
-};
+static int ucmb_pdev_id_count;
static int __devinit ucmb_spi_probe(struct spi_device *sdev)
ucmb_toggle_reset_line(ucmb, 1);
msleep(50);
ucmb_toggle_reset_line(ucmb, 0);
- msleep(10);
+ msleep(50);
return 0;
}
return container_of(filp->f_op, struct ucmb, mdev_fops);
}
+static int ucmb_open(struct inode *inode, struct file *filp)
+{
+ struct ucmb *ucmb = filp_to_ucmb(filp);
+ int err = 0;
+
+ mutex_lock(&ucmb->mutex);
+
+ if (ucmb->is_open) {
+ err = -EBUSY;
+ goto out_unlock;
+ }
+ ucmb->is_open = 1;
+ ucmb->msg_delay_usec = 0;
+
+out_unlock:
+ mutex_unlock(&ucmb->mutex);
+
+ return err;
+}
+
+static int ucmb_release(struct inode *inode, struct file *filp)
+{
+ struct ucmb *ucmb = filp_to_ucmb(filp);
+
+ mutex_lock(&ucmb->mutex);
+ WARN_ON(!ucmb->is_open);
+ ucmb->is_open = 0;
+ mutex_unlock(&ucmb->mutex);
+
+ return 0;
+}
+
static int ucmb_ioctl(struct inode *inode, struct file *filp,
unsigned int cmd, unsigned long arg)
{
case UCMB_IOCTL_RESETUC:
ret = ucmb_reset_microcontroller(ucmb);
break;
+ case UCMB_IOCTL_GMSGDELAY:
+ if (put_user(ucmb->msg_delay_usec, (unsigned int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ break;
+ case UCMB_IOCTL_SMSGDELAY: {
+ unsigned int msg_delay_usec;
+
+ if (get_user(msg_delay_usec, (unsigned int __user *)arg)) {
+ ret = -EFAULT;
+ break;
+ }
+ if (msg_delay_usec > UCMB_MAX_MSG_DELAY) {
+ ret = -E2BIG;
+ break;
+ }
+ ucmb->msg_delay_usec = msg_delay_usec;
+ break;
+ }
default:
ret = -EINVAL;
}
if (err)
goto out_free;
- /* The microcontroller deserves some time to process the message. */
- if (ucmb->msg_delay_ms)
- msleep(ucmb->msg_delay_ms);
+ if (ucmb->msg_delay_usec) {
+ /* The microcontroller deserves some time to process the message. */
+ if (ucmb->msg_delay_usec >= 1000000) {
+ ssleep(ucmb->msg_delay_usec / 1000000);
+ msleep(DIV_ROUND_UP(ucmb->msg_delay_usec, 1000));
+ } else if (ucmb->msg_delay_usec >= 1000) {
+ msleep(DIV_ROUND_UP(ucmb->msg_delay_usec, 1000));
+ } else
+ udelay(ucmb->msg_delay_usec);
+ }
/* Get the status code. */
err = spi_read(ucmb->sdev, (u8 *)&status, sizeof(status));
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;
ucmb->mdev.minor = MISC_DYNAMIC_MINOR;
ucmb->mdev.name = pdata->name;
ucmb->mdev.parent = &pdev->dev;
+ ucmb->mdev_fops.open = ucmb_open;
+ ucmb->mdev_fops.release = ucmb_release;
ucmb->mdev_fops.read = ucmb_read;
ucmb->mdev_fops.write = ucmb_write;
ucmb->mdev_fops.ioctl = ucmb_ioctl;
.remove = __devexit_p(ucmb_remove),
};
-static int ucmb_modinit(void)
+int ucmb_device_register(struct ucmb_platform_data *pdata)
{
- struct ucmb_platform_data *pdata;
struct platform_device *pdev;
- int err, i;
-
- printk(KERN_INFO "Microcontroller message bus driver\n");
+ int err;
- err = platform_driver_register(&ucmb_driver);
+ pdev = platform_device_alloc("ucmb", ucmb_pdev_id_count++);
+ if (!pdev) {
+ printk(KERN_ERR PFX "Failed to allocate platform device.\n");
+ return -ENOMEM;
+ }
+ err = platform_device_add_data(pdev, pdata, sizeof(*pdata));
if (err) {
- printk(KERN_ERR PFX "Failed to register platform driver\n");
+ printk(KERN_ERR PFX "Failed to add platform data.\n");
+ platform_device_put(pdev);
return err;
}
- err = spi_register_driver(&ucmb_spi_driver);
+ err = platform_device_add(pdev);
if (err) {
- printk(KERN_ERR PFX "Failed to register SPI driver\n");
- platform_driver_unregister(&ucmb_driver);
+ printk(KERN_ERR PFX "Failed to register platform device.\n");
+ platform_device_put(pdev);
return err;
}
+ pdata->pdev = pdev;
- for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
- pdata = &ucmb_list[i];
+ return 0;
+}
+EXPORT_SYMBOL(ucmb_device_register);
- pdev = platform_device_alloc("ucmb", i);
- if (!pdev) {
- printk(KERN_ERR PFX "Failed to allocate platform device.\n");
- break;
- }
- err = platform_device_add_data(pdev, pdata, sizeof(*pdata));
- if (err) {
- printk(KERN_ERR PFX "Failed to add platform data.\n");
- platform_device_put(pdev);
- break;
- }
- err = platform_device_add(pdev);
- if (err) {
- printk(KERN_ERR PFX "Failed to register platform device.\n");
- platform_device_put(pdev);
- break;
- }
- pdata->pdev = pdev;
+void ucmb_device_unregister(struct ucmb_platform_data *pdata)
+{
+ if (!pdata->pdev)
+ return;
+ platform_device_unregister(pdata->pdev);
+ platform_device_put(pdata->pdev);
+ pdata->pdev = NULL;
+}
+EXPORT_SYMBOL(ucmb_device_unregister);
+
+static int ucmb_modinit(void)
+{
+ int err;
+
+ printk(KERN_INFO "Microcontroller message bus driver\n");
+
+ err = spi_register_driver(&ucmb_spi_driver);
+ if (err) {
+ printk(KERN_ERR PFX "Failed to register SPI driver\n");
+ return err;
+ }
+ err = platform_driver_register(&ucmb_driver);
+ if (err) {
+ printk(KERN_ERR PFX "Failed to register platform driver\n");
+ spi_unregister_driver(&ucmb_spi_driver);
+ return err;
}
return 0;
}
-module_init(ucmb_modinit);
+subsys_initcall(ucmb_modinit);
static void ucmb_modexit(void)
{
- struct ucmb_platform_data *pdata;
- int i;
-
- for (i = 0; i < ARRAY_SIZE(ucmb_list); i++) {
- pdata = &ucmb_list[i];
-
- if (pdata->pdev) {
- platform_device_unregister(pdata->pdev);
- platform_device_put(pdata->pdev);
- }
- }
- spi_unregister_driver(&ucmb_spi_driver);
platform_driver_unregister(&ucmb_driver);
+ spi_unregister_driver(&ucmb_spi_driver);
}
module_exit(ucmb_modexit);
/** UCMB_IOCTL_RESETUC - Reset the microcontroller. */
#define UCMB_IOCTL_RESETUC _IO(__UCMB_IOCTL, 0)
+/** UCMB_IOCTL_GMSGDELAY - Get the delay to wait before fetching the status. */
+#define UCMB_IOCTL_GMSGDELAY _IOR(__UCMB_IOCTL, 1, unsigned int)
+/** UCMB_IOCTL_SMSGDELAY - Set the delay to wait before fetching the status. */
+#define UCMB_IOCTL_SMSGDELAY _IOW(__UCMB_IOCTL, 2, unsigned int)
#ifdef __KERNEL__
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/spi_gpio.h>
+
/**
* struct ucmb_platform_data - UCMB device descriptor
*
*
* @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.
- * This is the time the microcontroller takes to process
- * one message.
*/
struct ucmb_platform_data {
const char *name;
u8 mode;
u32 max_speed_hz;
- unsigned int msg_delay_ms;
struct platform_device *pdev; /* internal */
};
#define UCMB_NO_RESET ((unsigned int)-1)
+int ucmb_device_register(struct ucmb_platform_data *pdata);
+void ucmb_device_unregister(struct ucmb_platform_data *pdata);
+
#endif /* __KERNEL__ */
#endif /* LINUX_UCMB_H_ */