Raghu
Raghu

Reputation: 1

Custom Linux serdev driver for ST7580 PLC modem

I have a ST7580 Power line communication modem connected to a BeagleBone Black device running Debian Linux, kernel v4.19. I am trying to communicate with the modem using a custom serial driver (using the SERDEV model) which is based on some samples. I have compiled it as a kernel module named st7580km.ko and used modprobe to insert it into the kernel. Since I want to use a DeviceTree overlay to load the driver for this peripheral device, I have copied and modified the BONE-UART1.dts and named it as BONE-ST7580.dts with the following content:

/dts-v1/;
/plugin/;
#include <dt-bindings/pinctrl/am33xx.h>

&{/chosen} {
    overlays {
        BONE-ST7580 = __TIMESTAMP__;
    };
};

&ocp {
    P9_24_pinmux { pinctrl-0 = <&P9_24_uart_pin>; };    /* UART TX*/
    P9_26_pinmux { pinctrl-0 = <&P9_26_uart_pin>; };    /* UART RX*/
};

&bone_uart1 {
    //compatible = "st7580, st7580km, uart, omap3-uart";
    compatible = "st7580, st7580km";
    status = "okay";
};

I have compiled the dts file to dtbo and placed the dtbo in /lib/firmware and also in /boot/dtbs/4.19.94-ti-r73/

The kernel module source:

#include <asm/unaligned.h>
#include <linux/completion.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/jiffies.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/serdev.h>

#define ST7580_DRIVER_NAME "st7580"
#define ST7580_DRIVER_VERSION  "0.1.0"
#define ST7580_DEFAULT_BAUD_RATE 57600
#define ST7580_CHECKSUM_LENGTH 2
#define ST7580_MAX_DATA_LENGTH (3 + 255 + ST7580_CHECKSUM_LENGTH )
#define ST7580_TX_ACK_TIMEOUT msecs_to_jiffies(40)

static void st7580_write_wakeup(struct serdev_device *serdev)
{
    dev_info(&serdev->dev, "\nst7580_write_wakeup: pull down RTS\n");
    /* Pull down RTS */
    serdev_device_set_tiocm(serdev, TIOCMBIS, TIOCM_RTS);
}

static int st7580_receive_buf(struct serdev_device *serdev, 
       const unsigned char *buf, size_t size)
{
    dev_info(&serdev->dev, "\nst7580_receive_buf: Received %u bytes\n", size);
    return serdev_controller_receive_buf(serdev, buf, size);
}

static const struct serdev_device_ops st7580_serdev_ops = {
    .receive_buf = st7580_receive_buf, /* default */
    .write_wakeup = st7580_write_wakeup,
};

static int st7580_probe(struct serdev_device *serdev)
{
    int uart_flags;
    int ret;

    dev_info(&serdev->dev, "\nst7580_probe started...");
    serdev_device_set_client_ops(serdev, &st7580_serdev_ops);
    ret = devm_serdev_device_open(&serdev->dev, serdev);
    if (ret) {
        _dev_err(&serdev->dev, "unable to open the device\n");
        return ret;
    }
    serdev_device_set_baudrate(serdev, ST7580_DEFAULT_BAUD_RATE);
    serdev_device_set_flow_control(serdev, false);
    ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
    uart_flags = 0;
    uart_flags &= ~PARENB;
    uart_flags &= ~CSTOPB;
    uart_flags &= ~CSIZE;
    uart_flags |=  CS8;
    uart_flags &= ~CRTSCTS;
    uart_flags |= CREAD | CLOCAL;
    //uart_settings.c_iflag &= ~(IXON | IXOFF | IXANY);
    //uart_settings.c_iflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    //uart_settings.c_iflag &= ~IGNBRK;
    serdev_device_set_tiocm(serdev, TIOCMSET, uart_flags);
    serdev_device_set_rts(serdev, 0);
    serdev_device_set_tiocm(serdev, TIOCMSET, TIOCM_RTS);
    dev_info(&serdev->dev, "\nDriver name is %s, loaded successsfully\n", ST7580_DRIVER_NAME);
    return ret;
}

static const struct of_device_id st7580_of_match[] = {
    { .compatible = "st7580, st7580km" },
    { .compatible = "st, st7580" },
    { .compatible = "st, st7580km" },
    { }
};
MODULE_DEVICE_TABLE(of, st7580_of_match);

static struct serdev_device_driver st7580_driver = {
    .driver = {
        .name = ST7580_DRIVER_NAME,
        .of_match_table = st7580_of_match,
    },
    .probe = st7580_probe,
};
module_serdev_device_driver(st7580_driver);

I have also modified the /boot/uEnv.txt and added this line to load the DeviceTree overlay.

optargs=quiet drm.debug=7 capemgr.enable_partno=BB-ST7580

When I reboot I expect that my driver will be loaded using which I can communicate to the modem from a userspace app. I have placed some debug messages in the code which I expect to see in the dmesg output like below:

dev_info(&serdev->dev, "\nDriver name is %s, loaded successsfully\n", ST7580_DRIVER_NAME);

So far I do dont see any of the messages and am not sure if the kernel is using my driver for this peripheral. Can any one tell me what I have missed or any errors?

I have SD card enabled and the device boots from that not the eMMC. In the boot logs I dont see the kernel even trying to load the BONE-ST7580.dtbo that is specified in uEnv.txt. I also have CONFIG_SERIAL_DEV_BUS=y CONFIG_SERIAL_DEV_CTRL_TTYPORT=y enabled in the /usr/src/linux-headers-4.19.94-ti-r73/.config file that I used to build the driver as suggested in another post.

Upvotes: 0

Views: 486

Answers (1)

TBR
TBR

Reputation: 2890

Step one is to hook up to the debug UART on the Beaglebone. It gives you the necessary low level information to figure out what is happening during the U-Boot phase and early Kernel boot.

For embedded (Linux) work there is no excuse from hooking up to a UART. It's an essential tool.

Things to check for in this case is if you have more than one U-Boot in play (eMMC vs SD) and if you're working with the right one. Also if there are any error messages from U-Boot, etc. It should be quite easy to tell from the UART output.

Upvotes: 0

Related Questions