This adds a (platform) driver for the tja1041 can transceiver. The 'en' & '/err' gpio's are required, the '/stb' is optional, and it's functionality is _not_ tested by me yet (no actual device). I did test the /err input as proof of concept. Oliver, I assume you have such devices?
Signed-off-by: Kurt Van Dijck <[email protected]> --- Index: include/socketcan/can/platform/tja1041.h =================================================================== --- include/socketcan/can/platform/tja1041.h (revision 0) +++ include/socketcan/can/platform/tja1041.h (revision 0) @@ -0,0 +1,18 @@ +#ifndef CAN_TRANSCEIVER_TJA1041_H +#define CAN_TRANSCEIVER_TJA1041_H + +/* + * TJA1041 interface + * only available as platform device (name 'tja1041') + */ +struct tja1041_platform_data { + /* gpio's */ + int stb; + int en; + int err; + + const char *match; +}; + +#endif /* CAN_TRANSCEIVER_TJA1041_H */ + Index: drivers/net/can/transceiver/tja1041.c =================================================================== --- drivers/net/can/transceiver/tja1041.c (revision 0) +++ drivers/net/can/transceiver/tja1041.c (revision 0) @@ -0,0 +1,303 @@ +/* + * NXP TJA1041 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 <linux/interrupt.h> +#include <socketcan/can/error.h> +#include <socketcan/can/transceiver.h> +#include <socketcan/can/platform/tja1041.h> + +/* + * dynamic can_transceiver stuff + */ +struct tja1041 { + struct can_transceiver cantr; + int stb; + int en; + int err; + int err_irq; + int gpio_valid; + struct work_struct work; +}; +#define to_tja1041(d) container_of((d), struct tja1041, cantr) + +static int tja1041_validate_gpio(struct tja1041 *tja, int mask) +{ + int ret; + + if ((tja->gpio_valid & mask) == mask) + return 0; + + if (!(mask & 1)) + goto no_stb; + if (tja->stb < 0) { + /* mark as requested */ + tja->gpio_valid |= 1; + goto no_stb; + } + if (tja->gpio_valid & 1) + goto stb_done; + ret = gpio_request(tja->stb, dev_name(&tja->cantr.dev)); + if (ret < 0) + return ret; + /* stb is active low */ + gpio_direction_output(tja->stb, 1); + tja->gpio_valid |= 1; +stb_done: +no_stb: + if (!(mask & 2)) + goto no_en; + if (tja->gpio_valid & 2) + goto no_en; + ret = gpio_request(tja->en, dev_name(&tja->cantr.dev)); + if (ret < 0) + return ret; + gpio_direction_output(tja->en, 0); + tja->gpio_valid |= 2; +no_en: + if (!(mask & 4)) + goto no_err; + if (tja->gpio_valid & 4) + goto no_err; + ret = gpio_request(tja->err, dev_name(&tja->cantr.dev)); + if (ret < 0) + return ret; + gpio_direction_input(tja->err); + tja->err_irq = gpio_to_irq(tja->err); + tja->gpio_valid |= 4; +no_err: + return 0; +} + +static irqreturn_t tja1041_irq(int irq, void *p) +{ + struct tja1041 *tja = p; + + disable_irq_nosync(tja->err_irq); + schedule_work(&tja->work); + return IRQ_HANDLED; +} + +static void tja1041_free_irq(struct can_transceiver *cantr) +{ + struct tja1041 *tja = to_tja1041(cantr); + + if (tja->err_irq < 0) + return; + free_irq(tja->err_irq, tja); +} + +static int tja1041_setup_irq(struct can_transceiver *cantr) +{ + struct tja1041 *tja = to_tja1041(cantr); + int ret; + + ret = tja1041_validate_gpio(tja, 4); + if (ret < 0) + return ret; + if (tja->err_irq < 0) + return 0; + /* set a low-level interrupt */ + ret = request_irq(tja->err_irq, tja1041_irq, + IRQF_TRIGGER_LOW | IRQF_DISABLED | IRQF_SAMPLE_RANDOM, + dev_name(&tja->cantr.dev), tja); + if (ret < 0) + dev_err(&tja->cantr.dev, + "request_irq(%i) failed\n", tja->err_irq); + return ret; +} + +/* WorkQueue to allow calling can_transceiver_alert() */ +static void tja1041_work(struct work_struct *work) +{ + struct tja1041 *tja = container_of(work, struct tja1041, work); + + can_transceiver_alert(&tja->cantr, CAN_ERR_TRX_UNKNOWN_ERROR); + /* + * we know the irq is disabled, but support that + * can_transceiver_alert() will eventually call set_mode(OFF) + * until this is true, it's a lot easier to + * call free_irq from there. + */ +} + +static int tja1041_get_max_bitrate(struct can_transceiver *cantr) +{ + return 1000000; +} + +static int tja1041_get_state(struct can_transceiver *cantr) +{ + struct tja1041 *tja = to_tja1041(cantr); + int ret; + + /* validate err input */ + ret = tja1041_validate_gpio(tja, 4); + if (ret < 0) + return ret; + if (gpio_get_value(tja->err)) + /* no error */ + return 0; + return CAN_ERR_TRX_UNKNOWN_ERROR; +} + +static int tja1041_set_mode(struct can_transceiver *cantr, int mode) +{ + struct tja1041 *tja = to_tja1041(cantr); + int ret; + + ret = tja1041_get_state(cantr); + if (ret < 0) + return ret; + if ((ret > 0) && (mode != CANTR_MODE_OFF)) + /* in error condition, cancel netdev_open() */ + return -ECANCELED; + + /* validate stb & en */ + ret = tja1041_validate_gpio(tja, 3); + if (ret < 0) + return ret; + + switch (mode) { + case CANTR_MODE_ON: + if (tja->stb >= 0) + /* to to pwon mode */ + gpio_set_value(tja->stb, 1); + /* goto normal mode */ + gpio_set_value(tja->en, 1); + ret = tja1041_setup_irq(&tja->cantr); + if (ret < 0) + goto fail_irq; + break; + case CANTR_MODE_OFF: + tja1041_free_irq(&tja->cantr); +fail_irq: + /* goto pwon mode */ + gpio_set_value(tja->en, 0); + if (tja->stb >= 0) + /* to to standby mode */ + gpio_set_value(tja->stb, 0); + break; + default: + return -ENOTSUPP; + } + return ret; +} + +static int tja1041_probe(struct platform_device *pdev) +{ + struct tja1041 *tja; + const struct tja1041_platform_data *pdata = pdev->dev.platform_data; + int ret; + + tja = kzalloc(sizeof(*tja), GFP_KERNEL); + if (!tja) { + ret = -ENOMEM; + goto fail_mem; + } + tja->cantr.dev.parent = &pdev->dev; + tja->cantr.set_mode = tja1041_set_mode; + tja->cantr.get_state = tja1041_get_state; + tja->cantr.get_max_bitrate = tja1041_get_max_bitrate; + + tja->stb = pdata->stb; + tja->en = pdata->en; + tja->err = pdata->err; + tja->err_irq = -1; + tja->gpio_valid = 0; + + ret = can_transceiver_register(&tja->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. + */ + tja1041_validate_gpio(tja, ~0); + platform_set_drvdata(pdev, &tja->cantr); + + INIT_WORK(&tja->work, tja1041_work); + + if (pdata->match) + can_transceiver_set_match_name(&tja->cantr, pdata->match); + return 0; +fail_register: + kfree(tja); +fail_mem: + return ret; +} + +static int tja1041_remove(struct platform_device *pdev) +{ + struct can_transceiver *cantr = platform_get_drvdata(pdev); + struct tja1041 *tja = to_tja1041(cantr); + + cancel_work_sync(&tja->work); + platform_set_drvdata(pdev, 0); + if (!cantr) + return 0; + + can_transceiver_unregister(&tja->cantr); + if (tja->stb >= 0) { + gpio_direction_input(tja->stb); + gpio_free(tja->stb); + } + gpio_direction_input(tja->en); + gpio_free(tja->en); + gpio_direction_input(tja->err); + gpio_free(tja->err); + + kfree(tja); + return 0; +} + +static void tja1041_shutdown(struct platform_device *pdev) +{ + struct can_transceiver *cantr = platform_get_drvdata(pdev); + struct tja1041 *tja = to_tja1041(cantr); + + /* when still active, enter sleep mode */ + if (tja->stb >= 0) + /* goto sleep mode */ + gpio_set_value(tja->stb, 0); +} + + +static struct platform_driver tja1041_driver = { + .driver.name = "tja1041", + .probe = tja1041_probe, + .remove = tja1041_remove, + .shutdown = tja1041_shutdown, +}; +MODULE_ALIAS("platform:tja1041"); + +static int __init tja1041_start_mod(void) +{ + return platform_driver_register(&tja1041_driver); +} + +static void __exit tja1041_stop_mod(void) +{ + platform_driver_unregister(&tja1041_driver); +} + +module_init(tja1041_start_mod); +module_exit(tja1041_stop_mod); + +MODULE_AUTHOR("Kurt Van Dijck <[email protected]>"); +MODULE_DESCRIPTION("TJA1041 CAN transceiver support"); +MODULE_LICENSE("GPL"); + _______________________________________________ Socketcan-core mailing list [email protected] https://lists.berlios.de/mailman/listinfo/socketcan-core
