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

Reply via email to