The subject was wrong

This adds a (platform) driver for the well known pca82c251.
Due to the requirement to attach transceivers directly to netdevs,
2 interfaces exists: platform_device && pca82c251_create()

The enable GPIO is optional (-1 = don't use). It takes a bit noise to
deal with the 'optional' case.

Signed-off-by: Kurt Van Dijck <[email protected]>
---
Index: include/socketcan/can/platform/pca82c251.h
===================================================================
--- include/socketcan/can/platform/pca82c251.h  (revision 0)
+++ include/socketcan/can/platform/pca82c251.h  (revision 0)
@@ -0,0 +1,22 @@
+#ifndef CAN_TRANSCEIVER_PCA82C251_H
+#define CAN_TRANSCEIVER_PCA82C251_H
+
+/*
+ * PCA82C251 interface
+ * to make it available from outside platform_driver ('pca82c251')
+ */
+struct pca82c251_platform_data {
+       int gpio;
+       unsigned long slope_resistor;
+       const char *match;
+};
+
+/*
+ * some kind of 'manual' interface
+ */
+extern struct can_transceiver *pca82c251_create(
+       struct device *parent, const struct pca82c251_platform_data *pdata);
+extern void pca82c251_destroy(struct can_transceiver *cantr);
+
+#endif /* CAN_TRANSCEIVER_PCA82C251_H */
+
Index: drivers/net/can/transceiver/pca82c251.c
===================================================================
--- drivers/net/can/transceiver/pca82c251.c     (revision 0)
+++ drivers/net/can/transceiver/pca82c251.c     (revision 0)
@@ -0,0 +1,220 @@
+/*
+ * NXP pca82c251 CAN transceiver
+ *
+ * Copyright (C) 2009 EIA Electronics
+ * Author: Kurt Van Dijck <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <socketcan/can/transceiver.h>
+#include <socketcan/can/platform/pca82c251.h>
+
+/*
+ * dynamic can_transceiver stuff
+ */
+struct pca82c251 {
+       struct can_transceiver cantr;
+       unsigned long slope_resistor;
+       int gpio;
+       int gpio_valid;
+};
+#define to_pca82c251(d)        container_of((d), struct pca82c251, cantr)
+
+static int pca82c251_validate_gpio(struct pca82c251 *pca)
+{
+       int ret;
+
+       if (pca->gpio_valid)
+               return 0;
+       ret = gpio_request(pca->gpio, dev_name(&pca->cantr.dev));
+       if (ret < 0)
+               return ret;
+       pca->gpio_valid = 1;
+       gpio_direction_output(pca->gpio, 0);
+       return 0;
+}
+
+static int pca82c251_get_max_bitrate(struct can_transceiver *cantr)
+{
+       /*
+        * physically, this depends on the used slope resistor
+        * but the actual computation, I don't know yet
+        */
+       return 1000000;
+}
+
+static int pca82c251_set_mode(struct can_transceiver *cantr, int mode)
+{
+       struct pca82c251 *pca = to_pca82c251(cantr);
+       int ret;
+
+       if (pca->gpio < 0)
+               /* always ok, no gpio control is provided */
+               return 0;
+       ret = pca82c251_validate_gpio(pca);
+       if (ret < 0)
+               return ret;
+
+       switch (mode) {
+       case CANTR_MODE_ON:
+               gpio_set_value(pca->gpio, 1);
+               return 0;
+       case CANTR_MODE_OFF:
+               gpio_set_value(pca->gpio, 0);
+               return 0;
+       }
+       return -ENOTSUPP;
+}
+
+/*
+ * DEVICE management
+ */
+struct can_transceiver *pca82c251_create(
+       struct device *parent, const struct pca82c251_platform_data *pdata)
+{
+       struct pca82c251 *pca;
+       int ret;
+
+       pca = kzalloc(sizeof(*pca), GFP_KERNEL);
+       if (!pca) {
+               ret = -ENOMEM;
+               goto fail_mem;
+       }
+       pca->cantr.dev.parent = parent;
+       pca->cantr.set_mode = pca82c251_set_mode;
+       pca->cantr.get_max_bitrate = pca82c251_get_max_bitrate;
+
+       pca->slope_resistor = pdata->slope_resistor;
+       pca->gpio = pdata->gpio;
+       pca->gpio_valid = 0;
+
+       ret = can_transceiver_register(&pca->cantr);
+       if (ret < 0)
+               goto fail_register;
+
+       /*
+        * initial request for gpio
+        * don't fail when it does not succeed, because the gpio may
+        * come from future probed gpio_chips.
+        */
+       pca82c251_validate_gpio(pca);
+       if (pdata->match)
+               can_transceiver_set_match_name(&pca->cantr, pdata->match);
+       return &pca->cantr;
+
+fail_register:
+       kfree(pca);
+fail_mem:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(pca82c251_create);
+
+void pca82c251_destroy(struct can_transceiver *cantr)
+{
+       struct pca82c251 *pca = to_pca82c251(cantr);
+
+       can_transceiver_unregister(&pca->cantr);
+       if (pca->gpio_valid) {
+               gpio_direction_input(pca->gpio);
+               gpio_free(pca->gpio);
+       }
+       kfree(pca);
+}
+EXPORT_SYMBOL_GPL(pca82c251_destroy);
+
+/*
+ * SYSFS
+ */
+static ssize_t show_resistor(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct pca82c251 *pca = platform_get_drvdata(to_platform_device(dev));
+
+       return sprintf(buf, "%lu\n", pca->slope_resistor);
+}
+
+static ssize_t show_gpio(struct device *dev,
+                       struct device_attribute *attr, char *buf)
+{
+       struct pca82c251 *pca = platform_get_drvdata(to_platform_device(dev));
+
+       return sprintf(buf, "%s%i\n",
+                       ((pca->gpio >= 0) && !pca->gpio_valid) ? "!" : "",
+                       pca->gpio);
+}
+static DEVICE_ATTR(gpio, S_IRUGO, show_gpio, 0);
+static DEVICE_ATTR(resistor, S_IRUGO, show_resistor, 0);
+static struct attribute *pca_attrs[] = {
+       &dev_attr_gpio.attr,
+       &dev_attr_resistor.attr,
+       NULL
+};
+
+static struct attribute_group pca_attr_group = {
+       .name = NULL,
+       .attrs = pca_attrs,
+};
+
+static int pca82c251_probe(struct platform_device *pdev)
+{
+       struct can_transceiver *cantr;
+       int ret;
+
+       cantr = pca82c251_create(&pdev->dev, pdev->dev.platform_data);
+       if (IS_ERR(cantr))
+               return PTR_ERR(cantr);
+       ret = sysfs_create_group(&pdev->dev.kobj, &pca_attr_group);
+       if (ret < 0)
+               goto fail_sysfs;
+
+       platform_set_drvdata(pdev, cantr);
+       return 0;
+fail_sysfs:
+       pca82c251_destroy(cantr);
+       return ret;
+
+}
+
+static int pca82c251_remove(struct platform_device *pdev)
+{
+       struct can_transceiver *cantr = platform_get_drvdata(pdev);
+
+       sysfs_remove_group(&pdev->dev.kobj, &pca_attr_group);
+       platform_set_drvdata(pdev, 0);
+       if (cantr)
+               pca82c251_destroy(cantr);
+       return 0;
+}
+
+static struct platform_driver pca82c251_driver = {
+       .driver.name = "pca82c251",
+       .probe = pca82c251_probe,
+       .remove = pca82c251_remove,
+};
+MODULE_ALIAS("platform:pca82c251");
+
+static int __init pca82c251_start_mod(void)
+{
+       return platform_driver_register(&pca82c251_driver);
+}
+
+static void __exit pca82c251_stop_mod(void)
+{
+       platform_driver_unregister(&pca82c251_driver);
+}
+
+module_init(pca82c251_start_mod);
+module_exit(pca82c251_stop_mod);
+
+MODULE_AUTHOR("Kurt Van Dijck <[email protected]>");
+MODULE_DESCRIPTION("PCA82C251 CAN transceiver support");
+MODULE_LICENSE("GPL");
+

_______________________________________________
Socketcan-core mailing list
[email protected]
https://lists.berlios.de/mailman/listinfo/socketcan-core

Reply via email to