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
