Basic Kernel HWMon Driver Module for LPC Chip? - linux-kernel

I'm working on writing a kernel hwmon driver module for a chip that communicates over LPC (ISA style bus). I have the following code so far
umode_t qnap_ec_is_visible(const void* data, enum hwmon_sensor_types type, u32 attr, int channel)
{
}
int qnap_ec_read(struct device* dev, enum hwmon_sensor_types type, u32 attr, int channel, long* val)
{
}
int qnap_ec_write(struct device* dev, enum hwmon_sensor_types type, u32 attr, int channel, long val)
{
}
static const struct hwmon_ops qnap_ec_ops = {
.is_visible = qnap_ec_is_visible,
.read = qnap_ec_read,
.write = qnap_ec_write
};
static const struct hwmon_channel_info *qnap_ec_channel_info[] = {
HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT),
HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT),
HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
NULL
};
static const struct hwmon_chip_info qnap_ec_chip_info = {
.ops = &qnap_ec_ops,
.info = qnap_ec_channel_info
};
static int qnap_ec_probe(struct platform_device* platform_dev)
{
struct device* dev;
dev = devm_hwmon_device_register_with_info(&platform_dev->dev, "qnap_ec_hwmon", NULL,
&qnap_ec_chip_info, NULL);
return PTR_ERR_OR_ZERO(dev);
}
static const struct of_device_id qnap_ec_of_match[] = {
{ .compatible = "???" },
{}
};
MODULE_DEVICE_TABLE(of, qnap_ec_of_match);
static struct platform_driver qnap_ec_driver = {
.driver = {
.name = "qnap_ec_hwmon",
.of_match_table = qnap_ec_of_match
},
.probe = qnap_ec_probe
};
module_platform_driver(qnap_ec_driver);
however I'm pretty sure this approach (using a device ID and having the kernel call the probe function when it finds that device ID on the system) won't work for something on the LPC bus. The IT87 driver which also communicates over the LPC bus uses __init/__exit functions to enter the driver, however, that driver is very large and probably not an ideal example of a simple driver module. Are there any examples available of how to write a basic (ie: no real functionality just the skeleton) kernel hwmon driver for a LPC device? Some of the things I can't find answers for, for example, is if I use the __init/__exit functions, can I still register the driver using the devm_hwmon_device_register_with_info function or do I need to use another approach (the it87 driver for example uses the platform_driver_register function, but I'm not sure why since there doesn't seem to be any documentation on the correct approach for LPC devices).

Related

How to add device tree support and DMA assignement to character device module?

Hello fellow developers,
I am developing a Linux kernel module that uses a DMA channel to transfer memory
(on STM32MP157F).
This works but additional tuning should be made. The STM mdma kernel module makes this possible by using this private configuration struct:
struct stm32_mdma_chan_config {
u32 request;
u32 priority_level;
u32 transfer_config;
u32 mask_addr;
u32 mask_data;
bool m2m_hw;
};
I would like set the priority from 0x0 to 0x3 which is its maximum allowed value.
The variable priority_level is set in stm32_mdma_of_xlate function:
static struct dma_chan *stm32_mdma_of_xlate(struct of_phandle_args *dma_spec,
struct of_dma *ofdma)
{
struct stm32_mdma_chan_config config;
config.request = dma_spec->args[0];
config.priority_level = dma_spec->args[1];
....
}
Other modules/drivers in the system use a device tree setting like this for its and the used DMA channel configuration.
spi#44004000 {
#address-cells = <0x01>;
#size-cells = <0x00>;
dmas = <0x0e 0x25 0x400 0x01 0x0e 0x26 0x400 0x01>;
dma-names = "rx\0tx";
};
spi-stm32.c calls of_match_device in its stm32_spi_probe function. I believe the dma configuration is done during its execution.
I would like something similar for my character device driver:
mydriver#0 {
compatible = "mydriver";
dmas = <&mdma1 36 0x0 0x40008 0x0 0x0>,
<&mdma1 37 0x0 0x40002 0x0 0x0>;
dma-names = "rx", "tx";
};
But this is currently ignored because I do not have a platform device I could use to call
of_match_device. I seem to be blinded by to much source code to look at ...
Any tips for me?
Update:
Currently studying http://xillybus.com/tutorials/device-tree-zynq-3
Best regards Gunther
I solved my issues after doing some extra research.
These are the steps:
I had to add a custom entry to the device tree:
mydriver_0: mydriver#0 {
compatible = "mydriver";
dmas = <&mdma1 22 0x3 0x1200000a 0x48001008 0x00000020 1>;
dma-names = "dma0";
};
Make the driver loadable as a platform driver. Create an of_device_id match table matching the device tree entry. Change module_init() to call platform_driver_register() to register the matching table. Then implement a probe function calling the previous init function. Store pdev.
static int mydriver_drv_probe(struct platform_device *pdev)
{
// store pdev for later call to: dma_channel_req(&pdev->dev, "dma0");
// TODO init driver
return 0;
}
/* Connection to device tree */
static struct of_device_id mydriver_of_match[] =
{
{ .compatible = "mydriver" },
{}
};
MODULE_DEVICE_TABLE(of, mydriver_of_match);
static struct platform_driver mydriver_platform_driver = {
.probe = mydriver_drv_probe,
.remove = mydriver_drv_remove,
.driver = {
.name = "mydriver",
.owner = THIS_MODULE,
.of_match_table = mydriver_of_match,
},
};
static int __init _mydriver_driver_init(void)
{
return platform_driver_register(&mydriver_platform_driver);
}
module_init(_mydriver_driver_init);
Get the correctly configured DMA channel:
chan = dma_request_chan(&pdev->dev, "dma0");
I tested this and it worked. The requested DMA channel is configured using the my device tree "dmas" entry.
I hope this of use for somebody else!

How is the 'data' field provided by the device tree (for platform devce case)?

I can't understand how the device tree information is used in a specific driver.
This is a code snippet from linux-5.15.68 drivers/pci/controller/dwc/pcie-designware-plat.c.
static int dw_plat_pcie_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct dw_plat_pcie *dw_plat_pcie;
struct dw_pcie *pci;
int ret;
const struct of_device_id *match;
const struct dw_plat_pcie_of_data *data;
enum dw_pcie_device_mode mode;
match = of_match_device(dw_plat_pcie_of_match, dev);
if (!match)
return -EINVAL;
data = (struct dw_plat_pcie_of_data *)match->data;
mode = (enum dw_pcie_device_mode)data->mode;
... (skip)
return 0;
}
static const struct dw_plat_pcie_of_data dw_plat_pcie_rc_of_data = {
.mode = DW_PCIE_RC_TYPE,
};
static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = {
.mode = DW_PCIE_EP_TYPE,
};
static const struct of_device_id dw_plat_pcie_of_match[] = {
{
.compatible = "snps,dw-pcie",
.data = &dw_plat_pcie_rc_of_data,
},
{
.compatible = "snps,dw-pcie-ep",
.data = &dw_plat_pcie_ep_of_data,
},
{},
};
So the kernel parses the device tree (connected to the struct dev) while running this probe function for a platform device. It compares the 'compatible' field of the device tree's node with the match data of this driver (=dw_plat_pcie_of_match) and extracts the of_device_id data from the device node of the device tree. Then, shouldn't the device tree have this 'data' field in the of_device_id information somewhere?
But this is an example device tree node with 'snps,dw-pcie-ep' in the compatible field (from arch/arm/boot/dts/uniphier-pro5.dtsi).
pcie_ep: pcie-ep#66000000 {
compatible = "socionext,uniphier-pro5-pcie-ep",
"snps,dw-pcie-ep";
status = "disabled";
reg-names = "dbi", "dbi2", "link", "addr_space";
reg = <0x66000000 0x1000>, <0x66001000 0x1000>,
<0x66010000 0x10000>, <0x67000000 0x400000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_pcie>;
clock-names = "gio", "link";
clocks = <&sys_clk 12>, <&sys_clk 24>;
reset-names = "gio", "link";
resets = <&sys_rst 12>, <&sys_rst 24>;
num-ib-windows = <16>;
num-ob-windows = <16>;
num-lanes = <4>;
phy-names = "pcie-phy";
phys = <&pcie_phy>;
};
It doesn't have any data field with specific 'RC' or 'EP' mode indication. Where is this 'data' field kept in the device tree??
It doesn't have any data field with specific 'RC' or 'EP' mode indication. Where is this 'data' field kept in the device tree??
The DT node doesn't have to supply such a "data" field because the "'RC' or 'EP' mode indication" is already conveyed to the device driver using the compatible property.
In the driver, there is more than one acceptable compatible string (i.e. there is more than one struct of_device_id element). Each compatible string (specified by the .compatible = ...) in the driver is also associated with a data structure (specified by the .data = ...) that specifies attributes (i.e. the mode in question) of the particular device that has to be supported.
static const struct of_device_id dw_plat_pcie_of_match[] = {
{
.compatible = "snps,dw-pcie",
.data = &dw_plat_pcie_rc_of_data,
},
{
.compatible = "snps,dw-pcie-ep",
.data = &dw_plat_pcie_ep_of_data,
},
{},
};
Since your DT node uses
compatible = "socionext,uniphier-pro5-pcie-ep",
"snps,dw-pcie-ep";
the second string in that property should be a match for the compatible string in the 2nd element of the dw_plat_pcie_of_match[] array.
So when execution returns from the
match = of_match_device(dw_plat_pcie_of_match, dev);
match should be pointing to the following structure:
{
.compatible = "snps,dw-pcie-ep",
.data = &dw_plat_pcie_ep_of_data,
},
Then the assignment
data = (struct dw_plat_pcie_of_data *)match->data;
will refer to
static const struct dw_plat_pcie_of_data dw_plat_pcie_ep_of_data = {
.mode = DW_PCIE_EP_TYPE,
};
So the assignment
mode = (enum dw_pcie_device_mode)data->mode;
will fetch the value DW_PCIE_EP_TYPE from that structure, and set the mode.
Associating a data structure with the compatible string allows a device driver to covertly support more than a single version of a device. The DT is relieved from having to specify (any and all) the distinguishing aspects of that particular version of the device. The DT node will only have properties that describe configurable or board-specific attributes.

Linux device driver for a Smart Card IC module

I have a smart card IC module, and I want to create a Linux device driver for it. This module is using SPI as the controlling line and has an interrupt line to indicate whether a card is ready. I know how to create a SPI device in Linux kernel and how to read data in the kernel when the interruption happens. But I have no idea on how to transfer the data to the user space (maybe need to create a device node for it), and how to give the user space a interruption to notify it. Does anyone have some suggestion?
One way you can go about this is by creating a devfs entry and then having the interested process open that device and receive asynchronous notification from the device driver using fasync.
Once you have the notification in user space you can notify other interested processes by any means you deem fit.
I am writing a small trimmed down example illustrating this feature.
On the driver side
/* Appropriate headers */
static int myfasync(int fd, struct file *fp, int on);
static struct fasync_struct *fasyncQueue;
static struct file_operations fops =
{
.open = charDriverOpen,
.release = charDriverClose,
.read = charDriverRead,
.write = charDriverWrite,
.unlocked_ioctl = charDriverCtrl,
// This will be called when the FASYNC flag is set
.fasync = myfasync,
};
static int __init charDriverEntry()
{
// Appropriate init for the driver
// Nothing specific needs to be done here with respect to
// fasync feature.
}
static int myfasync(int fd, struct file *fp, int on)
{
// Register the process pointed to by fp to the list
// of processes to be notified when any event occurs
return fasync_helper(fd, fp, 1, &fasyncQueue);
}
// Now to the part where we want to notify the processes listed
// in fasyncQueue when something happens. Here in this example I had
// implemented the timer. Not getting in to the details of timer func
// here
static void send_signal_timerfn(unsigned long data)
{
...
printk(KERN_INFO "timer expired \n");
kill_fasync(&fasyncQueue, SIGIO, POLL_OUT);
...
}
On the user land process side
void my_notifier(int signo, siginfo_t *sigInfo, void *data)
{
printf("Signal received from the driver expected %d got %d \n",SIGIO,signo);
}
int main()
{
struct sigaction signalInfo;
int flagInfo;
signalInfo.sa_sigaction = my_notifier;
signalInfo.sa_flags = SA_SIGINFO;
sigemptyset(&signalInfo.sa_mask);
sigaction(SIGIO, &signalInfo, NULL);
int fp,i;
fp = open("/dev/myCharDevice",O_RDWR);
if (fp<0)
printf("Failed to open\n");
/*New we will own the device so that we can get the signal from the device*/
// Own the process
fcntl(fp, F_SETOWN, getpid());
flagInfo = fcntl(fp, F_GETFL);
// Set the FASYNC flag this triggers the fasync fops
fcntl(fp, F_SETFL, flagInfo|FASYNC);
...
}
Hope this clears things up.
For more detailed reading I suggest you read this

writing an acpi driver in Linux

I need to write a kernel module that uses an acpi method to communicate to a hardware device.
At this point I just want to load the driver and enumerate the devices on the bus.
I found a fairly old but reasonable example on line, below is the basic outline. I pretty much took the example verbatim just changing names, I used acpidump to find the dsdt table get the correct device ID etc.
The driver loads fine, but my add functions are not being called. My suspicion is that I am missing a step to stimulate scanning the bus after I register it. The example assumes the driver is loaded on boot. Is there a way to request the bus be scanned after registering it such that any devices attached to a registered bus will be added? Understand that my suspicion may be wrong so if my assumptions are wrong please correct me.
below is the source:
static int viking_acpi_add(struct acpi_device *device);
static int viking_acpi_remove(struct acpi_device *device);
static void viking_acpi_notify(struct acpi_device *adev, u32 event);
static const struct acpi_device_id nv_device_ids[] = {
{ "ACPI0012", 0},
{ "", 0},
};
MODULE_DEVICE_TABLE(acpi, nv_device_ids);
static struct acpi_driver nv_acpi_driver = {
.name = "NV NVDR",
.class = "NV",
.ids = nv_device_ids,
.ops = {
.add = nv_acpi_add,
.remove = nv_acpi_remove,
.notify = nv_acpi_notify,
},
.owner = THIS_MODULE,
};
//static struct acpi_device acpi_dev;
static int nv_acpi_add(struct acpi_device *device)
{
printk("NV: acpi bus add\n");
return 0;
}
static int nv_remove(struct acpi_device *device)
{
printk("NV: acpi bus remove\n");
return 0;
}
static void nv_acpi_notify(struct acpi_device *adev, u32 event)
{
device_lock(&adev->dev);
printk("notification detected\n");
device_unlock(&adev->dev);
}
static int __init nv_init(void)
{
int result = 0;
result = acpi_bus_register_driver(&nvt_driver);
if (result < 0) {
printk("Error registering driver\n");
return -ENODEV;
}
return 0;
}
static void __exit nv_exit(void)
{
acpi_bus_unregister_driver(&nv_driver);
}
module_init(nv_init);
module_exit(nv_exit);
Well it turns out that another acpi bus driver was registered for the acpi device ID I was using and the kernel did not call my add routine as a consequence. When I ran it with a different kernel, my add routine was called as expected.

probe is not getting called in i2c driver

I am trying to learn to write a i2c driver on raspberry pi board and i have taken groove LCD back-light.Here driver.probe is not getting called whereas driver's inserted in system as i can see in dmesg.
Init code of driver is getting called, and code =>
static int lcd_probe(struct i2c_client *i2c_client, const struct i2c_device_id *i2c_id)
{
int ret = 0;
//struct lcd_data *lcd_data;
// struct device *dev = &i2c_client->dev;
// lcd_data->client = i2c_client;
pr_debug("lcd_probe : calling the probe\n");
pr_debug("lcd_probe : i2c_client->addr = %d, i2c_client_name = %s\n",
i2c_client->addr, i2c_client->name);
return ret;
}
static struct i2c_device_id lcd_id[] = {
{"lcd", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, lcd_id);
static struct i2c_driver lcd_driver = {
.driver = {
.name = "lcd",
.owner = THIS_MODULE,
},
.probe = lcd_probe,
// .remove = lcd_remove,
// .attach_adapter = lcd_attach,
.detect = lcd_detect,
.id_table = lcd_id,
};
static int __init lcd_init(void)
{
pr_debug("lcd_init : calling init\n");
return (i2c_add_driver(&lcd_driver));
}
and dmesg =>
[ 1.971009] lcd_init : calling init
But driver.probe is not registering in i2c subsystem.
board file initialization =>
Board init code =>
/** start aartyaa lcd i2c driver */
printk(KERN_INFO "board file registering i2c lcd device\n");
i2c_register_board_info_dt(1, lcd_i2c_devices, ARRAY_SIZE(lcd_i2c_devices));
i2c_board_info code =>
/** aaryaa i2c lcd struct */
static struct i2c_board_info __initdata lcd_i2c_devices[] = {
{
.type = "lcd",
.addr = 0x62,
},
};
i added debugs in i2c_register_device
and i found driver prove device is not getting called. dmesg i have linked
dmesg link
It seems that i need to register in platform also ..
How probe gets called in i2c ... ?
Any help will be appreciated.
Thank you...!!!

Resources