I've written an I2C driver. I want to make the GPIO which it uses configurable from the device tree.
My device tree entry is currently:
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
...
myi2c: myi2c#43 {
compatible = "fsl,myi2c";
reg = <0x43>;
};
I'd like to add this line into the myi2c stanza:
myi2c-gpios = <&gpio4 29 GPIO_ACTIVE_HIGH>;
I can see how to do this, if I was writing a platform driver:
static int gpio_init_probe(struct platform_device *pdev)
{
int i = 0;
printk("GPIO example init\n");
/* "greenled" label is matching the device tree declaration. OUT_LOW is the value at init */
green = devm_gpiod_get(&pdev->dev, "greenled", GPIOD_OUT_LOW);
but in my driver's i2c_probe(), I have no access to a struct platform_device *:
static int myi2c_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
How can I read the value of myi2c-gpios from the device tree and use it in my i2c driver?
I found this driver to use as an example:
static int sn65dsi84_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
...
struct gpio_desc *enable_gpio;
enable_gpio = devm_gpiod_get_optional(&client->dev, "enable", GPIOD_OUT_HIGH);
if (enable_gpio)
gpiod_set_value_cansleep(enable_gpio, 1);
and its device tree is:
&i2c2 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
dsi_lvds_bridge: sn65dsi84#2c {
status = "disabled";
reg = <0x2c>;
compatible = "ti,sn65dsi84";
enable-gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>;
So devm_gpiod_get_optional() seems to be the answer.
I'm trying to find a way to accesses, from node_1, a variable in node_0 (see code below) in this device-tree:
/ {
amba_pl: amba_pl#0 {
node_0: node#a8000000 {
node_id = <0x0>;
node0State = <0x0>;
};
};
node_1: node#a9000000 {
node1State = <node_0's node0State>;
};
};
};
The primary goal is to be able to share a state between kernel modules. I'm aware that I can EXPORT_SYMBOL(variable) in the writing node and then extern *variable in the reading node, but wanted to see if I could accomplish this in the device-tree itself. node_0 would always be the only node to set the nodeState, and node_1 would only read. Is this possible?
You can store a phandle referring to the node containing node0state:
/ {
amba_pl: amba_pl#0 {
node_0: node#a8000000 {
node_id = <0x0>;
node0State = <0x0>;
};
};
node_1: node#a9000000 {
stateNode = <&node_0>;
};
};
};
In the driver code, if struct device_node *mynode; refers to the node_1 node, the node0state property of the other node referred to by the stateNode phandle property can be accessed as follows:
int rc = 0;
struct device_node *np;
u32 node0state;
np = of_parse_phandle(mynode, "stateNode", 0);
if (!np) {
// error node not found
rc = -ENXIO;
goto error1;
}
// Do whatever is needed to read "node0state". Here is an example.
rc = of_property_read_u32(np, "node0state", &node0state);
if (rc) {
// error
of_node_put(np); // decrement reference to the node
goto error2;
}
// Finished with the node.
of_node_put(np); // decrement reference to the node
I am currently refactoring a driver for the AMD Sensor Fusion Hub.
The original driver can be found here.
When sending commands to the device, the chip takes some time to process the request. I wasusing a hack using msleep to wait for the device to respond. However I'd like to cleanly implement IRQ handling. As a template I looked into the implementation of drivers/i2c/busses/i2c-amd-mp2-pci.c and came up with the following stub interrupt handling.
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
* AMD Sensor Fusion Hub (SFH) PCIe driver
*
* Authors: Shyam Sundar S K <Shyam-sundar.S-k#amd.com>
* Nehal Bakulchandra Shah <Nehal-bakulchandra.Shah#amd.com>
* Richard Neumann <mail#richard-neumann.de>
*/
#include <linux/bitops.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/pm_runtime.h>
#include <linux/types.h>
#include "amd-sfh-pci.h"
/**
* amd_sfh_get_sensor_mask - Returns the sensors mask.
* #pci_dev: The Sensor Fusion Hub PCI device
*
* Returns an unsigned integer representing the bitmask to match
* the sensors connected to the Sensor Fusion Hub.
*/
int amd_sfh_get_sensor_mask(struct pci_dev *pci_dev)
{
int sensor_mask;
struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
sensor_mask = readl(sfh_dev->mmio + AMD_SFH_SENSOR_MASK);
/* Correct bit shift in firmware register */
sensor_mask = sensor_mask >> 4;
if (!sensor_mask)
dev_err(&pci_dev->dev, "no sensors marked active on device\n");
return sensor_mask;
}
EXPORT_SYMBOL_GPL(amd_sfh_get_sensor_mask);
/**
* amd_sfh_start_sensor- Starts the respective sensor.
* #pci_dev: The Sensor Fusion Hub PCI device
* #sensor_idx: The sensor's index
* #dma_handle: The DMA handle
*/
void amd_sfh_start_sensor(struct pci_dev *pci_dev, enum sensor_idx sensor_idx,
dma_addr_t dma_handle)
{
struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
union amd_sfh_cmd command;
union amd_sfh_parm parameter;
command.ul = 0;
command.s.cmd_id = enable_sensor;
command.s.period = PERIOD;
command.s.sensor_id = sensor_idx;
parameter.ul = 0;
parameter.s.buffer_layout = 1;
parameter.s.buffer_length = 16;
writeq(dma_handle, sfh_dev->mmio + AMD_SFH_ADDR);
writel(parameter.ul, sfh_dev->mmio + AMD_SFH_PARM);
writel(command.ul, sfh_dev->mmio + AMD_SFH_CMD);
}
EXPORT_SYMBOL_GPL(amd_sfh_start_sensor);
/**
* amd_sfh_stop_sensor- Stops the respective sensor.
* #pci_dev: The Sensor Fusion Hub PCI device
* #sensor_idx: The sensor's index
*/
void amd_sfh_stop_sensor(struct pci_dev *pci_dev, enum sensor_idx sensor_idx)
{
struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
union amd_sfh_cmd command;
command.ul = 0;
command.s.cmd_id = disable_sensor;
command.s.period = 0;
command.s.sensor_id = sensor_idx;
writeq(0x0, sfh_dev->mmio + AMD_SFH_ADDR);
writel(command.ul, sfh_dev->mmio + AMD_SFH_CMD);
}
EXPORT_SYMBOL_GPL(amd_sfh_stop_sensor);
/**
* amd_sfh_stop_all_sensors- Stops all sensors on the SFH.
* #pci_dev: The Sensor Fusion Hub PCI device
*/
static void amd_sfh_stop_all_sensors(struct pci_dev *pci_dev)
{
struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
union amd_sfh_cmd command;
command.ul = 0;
command.s.cmd_id = stop_all_sensors;
command.s.period = 0;
command.s.sensor_id = 0;
writel(command.ul, sfh_dev->mmio + AMD_SFH_CMD);
}
/**
* amd_sfh_irq_isr - IRQ handler
* #irq: The IRQ number received
* #dev: The underlying device
*/
static irqreturn_t amd_sfh_irq_isr(int irq, void *dev)
{
struct amd_sfh_dev *sfh_dev = dev;
dev_info(&sfh_dev->pci_dev->dev, "got IRQ: %d\n", irq);
return IRQ_NONE;
}
/**
* amd_sfh_pci_init - Initializes the PCI device
* #sfh_dev: The device data
* #pci_dev: The PCI device
*/
static int amd_sfh_pci_init(struct amd_sfh_dev *sfh_dev,
struct pci_dev *pci_dev)
{
int rc;
pci_set_drvdata(pci_dev, sfh_dev);
rc = pcim_enable_device(pci_dev);
if (rc)
goto err_pci_enable;
rc = pcim_iomap_regions(pci_dev, BIT(2), pci_name(pci_dev));
if (rc)
goto err_pci_enable;
sfh_dev->pci_dev = pci_dev;
sfh_dev->mmio = pcim_iomap_table(pci_dev)[2];
pci_set_master(pci_dev);
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
if (rc) {
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
if (rc)
goto err_dma_mask;
}
/* Set up intx irq */
writel(0, sfh_dev->mmio + AMD_P2C_MSG_INTEN);
pci_intx(pci_dev, 1);
dev_info(&pci_dev->dev, "available interrupt: %d\n", pci_dev->irq);
rc = devm_request_irq(&pci_dev->dev, pci_dev->irq, amd_sfh_irq_isr,
0, dev_name(&pci_dev->dev), sfh_dev);
if (rc)
dev_err(&pci_dev->dev, "Failure requesting irq %i: %d\n",
pci_dev->irq, rc);
return rc;
err_dma_mask:
pci_clear_master(pci_dev);
err_pci_enable:
return rc;
}
static int amd_sfh_pci_probe(struct pci_dev *pci_dev,
const struct pci_device_id *id)
{
int rc;
struct amd_sfh_dev *sfh_dev;
sfh_dev = devm_kzalloc(&pci_dev->dev, sizeof(*sfh_dev), GFP_KERNEL);
if (!sfh_dev)
return -ENOMEM;
rc = amd_sfh_pci_init(sfh_dev, pci_dev);
if (rc)
return rc;
pm_runtime_set_autosuspend_delay(&pci_dev->dev, 1000);
pm_runtime_use_autosuspend(&pci_dev->dev);
pm_runtime_put_autosuspend(&pci_dev->dev);
pm_runtime_allow(&pci_dev->dev);
dev_info(&pci_dev->dev, "SFH device registered.\n");
return 0;
}
static void amd_sfh_pci_remove(struct pci_dev *pci_dev)
{
struct amd_sfh_dev *sfh_dev = pci_get_drvdata(pci_dev);
amd_sfh_stop_all_sensors(sfh_dev->pci_dev);
pm_runtime_forbid(&pci_dev->dev);
pm_runtime_get_noresume(&pci_dev->dev);
pci_intx(pci_dev, 0);
pci_clear_master(pci_dev);
}
static const struct pci_device_id amd_sfh_pci_tbl[] = {
{PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_SFH)},
{0}
};
MODULE_DEVICE_TABLE(pci, amd_sfh_pci_tbl);
static struct pci_driver amd_sfh_pci_driver = {
.name = "amd-sfh-pci",
.id_table = amd_sfh_pci_tbl,
.probe = amd_sfh_pci_probe,
.remove = amd_sfh_pci_remove,
};
module_pci_driver(amd_sfh_pci_driver);
/**
* amd_sfh_find_device - Returns the first best AMD SFH device.
*/
struct amd_sfh_dev *amd_sfh_find_device(void)
{
struct device *dev;
struct pci_dev *pci_dev;
dev = driver_find_next_device(&amd_sfh_pci_driver.driver, NULL);
if (!dev)
return NULL;
pci_dev = to_pci_dev(dev);
return pci_get_drvdata(pci_dev);
}
EXPORT_SYMBOL_GPL(amd_sfh_find_device);
MODULE_DESCRIPTION("AMD(R) Sensor Fusion Hub PCI driver");
MODULE_AUTHOR("Shyam Sundar S K <Shyam-sundar.S-k#amd.com>");
MODULE_AUTHOR("Nehal Bakulchandra Shah <Nehal-bakulchandra.Shah#amd.com>");
MODULE_AUTHOR("Richard Neumann <mail#richard-neumann.de>");
MODULE_LICENSE("Dual BSD/GPL");
However the dev_info from amd_sfh_irq_isr is never logged, so I suspect that the IRQ is never triggered when writing to the device registers. What can be the reason for that and what can I do to correctly implement the IRQ handling?
PS
dmesg output:
[ 2272.642762] amd-sfh-pci 0000:04:00.7: available interrupt: 56
[ 2272.642840] amd-sfh-pci 0000:04:00.7: SFH device registered.
Update
The device seems to support MSI:
04:00.7 Non-VGA unclassified device [0000]: Advanced Micro Devices, Inc. [AMD] Raven/Raven2/Renoir Sensor Fusion Hub [1022:15e4]
Subsystem: Hewlett-Packard Company Raven/Raven2/Renoir Sensor Fusion Hub [103c:8496]
Flags: bus master, fast devsel, latency 0, IRQ 56
Memory at fc800000 (32-bit, non-prefetchable) [size=1M]
Memory at fcc8c000 (32-bit, non-prefetchable) [size=8K]
Capabilities: [48] Vendor Specific Information: Len=08 <?>
Capabilities: [50] Power Management version 3
Capabilities: [64] Express Endpoint, MSI 00
Capabilities: [a0] MSI: Enable- Count=1/2 Maskable- 64bit+
Capabilities: [c0] MSI-X: Enable- Count=2 Masked-
Capabilities: [100] Vendor Specific Information: ID=0001 Rev=1 Len=010 <?>
Kernel modules: amd_sfh_pci
But the interrupts are still not received / handled:
static void amd_sfh_debug(struct pci_dev *pci_dev)
{
bool msi;
msi = pci_dev_msi_enabled(pci_dev);
pci_info(pci_dev, "MSI: %s\n", msi ? "true" : "false");
pci_info(pci_dev, "No MSI: %s\n", pci_dev->no_msi ? "true" : "false");
}
/**
* amd_sfh_handle_irq - Handles IRQs.
* #irq: The interrupt request to be handled
* #dev: The driver data
*
* Returns an appropriate IRQ return type.
*/
static irqreturn_t amd_sfh_handle_irq(int irq, void *dev)
{
struct amd_sfh_dev *privdata = dev;
pci_info(privdata->pci_dev, "got IRQ: %d\n", irq);
return IRQ_NONE;
}
static int amd_sfh_setup_irq(struct amd_sfh_dev *privdata)
{
int rc, vecs, irq;
struct pci_dev *pci_dev = privdata->pci_dev;
vecs = pci_alloc_irq_vectors(pci_dev, 1, 3, PCI_IRQ_ALL_TYPES);
if (vecs < 0)
return vecs;
pci_info(pci_dev, "allocated %d IRQ vectors\n", vecs);
for (irq = 0; irq < vecs; irq++) {
pci_info(pci_dev, "requesting IRQ: %d\n", irq);
rc = devm_request_irq(&pci_dev->dev,
pci_irq_vector(pci_dev, irq),
amd_sfh_handle_irq, 0, "sfh-irq",
privdata);
if (rc) {
pci_err(pci_dev, "failed to get IRQ\n");
goto free_irq_vectors;
}
}
return 0;
free_irq_vectors:
pci_free_irq_vectors(pci_dev);
return rc;
}
static int amd_sfh_pci_init(struct amd_sfh_dev *privdata,
struct pci_dev *pci_dev)
{
int rc;
pci_set_drvdata(pci_dev, privdata);
rc = pcim_enable_device(pci_dev);
if (rc)
return rc;
rc = pcim_iomap_regions(pci_dev, BIT(2), pci_name(pci_dev));
if (rc)
return rc;
privdata->pci_dev = pci_dev;
privdata->mmio = pcim_iomap_table(pci_dev)[2];
pci_set_master(pci_dev);
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(64));
if (rc) {
rc = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32));
if (rc)
goto clear_master;
}
/* Setup IRQ */
amd_sfh_debug(pci_dev);
rc = amd_sfh_setup_irq(privdata);
if (rc)
goto clear_master;
amd_sfh_debug(pci_dev);
/* End of IRQ setup */
pci_info(pci_dev, "AMD Sensor Fusion Hub device initialized\n");
return 0;
clear_master:
pci_clear_master(pci_dev);
return rc;
}
dmesg:
[ 6.954524] amd-sfh-pci 0000:04:00.7: enabling device (0000 -> 0002)
[ 6.954641] amd-sfh-pci 0000:04:00.7: MSI: false
[ 6.954642] amd-sfh-pci 0000:04:00.7: No MSI: false
[ 6.954791] amd-sfh-pci 0000:04:00.7: allocated 2 IRQ vectors
[ 6.954792] amd-sfh-pci 0000:04:00.7: requesting IRQ: 0
[ 6.954825] amd-sfh-pci 0000:04:00.7: requesting IRQ: 1
[ 6.954860] amd-sfh-pci 0000:04:00.7: MSI: true
[ 6.954861] amd-sfh-pci 0000:04:00.7: No MSI: false
[ 6.954861] amd-sfh-pci 0000:04:00.7: AMD Sensor Fusion Hub device initialized
[ 6.969691] amd-sfh-pci 0000:04:00.7: [Firmware Bug]: No sensors marked active!
[ 6.971265] amd-sfh-pci 0000:04:00.7: sensor mask: 0x000001
[ 7.548189] hid-generic 0018:03FE:0001.0001: hidraw0: I2C HID v0.01 Device [amd-sfh-accel] on
The original documentation from AMD mentiones interrupts but is not too specific as to how they are being generated.
By try-and-error I found that I needed to set the P2C register 0x10690 to 1 in order to enable interrupts on the device. Whith this set, the device is flooding the driver with interrupts. I'm still figuring out how to make the device generate interrupts only on actual write events to the C2P registers.
Okay, I found out, how:
https://github.com/conqp/linux/blob/5ba797452a794100d65d103e8eb53f64ae14d1d0/drivers/hid/amd-sfh-hid/amd-sfh-pci.c#L301
I have a Ti's AM335x custom board. I want to define some pins as gpios and be able to set them as input and output in its Linux userspace.
I defined pins in am33xx_pinmux and then referenced it with the bone-pinmux-helper. The problem is I can set it with these commands on the terminal but it doesn't work.
echo 4 > /sys/class/gpios/export
echo out > /sys/class/gpios/gpio4/direction
echo 1 > /sys/class/gpios/value
here is a brief of my code for just two pins gpio0_4 gpio0_5, each pin must become available to set as input or output in userspace.
#include "am33xx.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
&am33xx_pinmux {
pinctrl-names = "default";
DATA_OUT_A00:DATA_OUT_A00 {
pinctrl-single,pins = <0x158 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) >; /*gpio0_4*/
};
DATA_OUT_A01:DATA_OUT_A01 {
pinctrl-single,pins = <0x15C (PIN_OUTPUT_PULLDOWN | MUX_MODE7) >; /*gpio0_5*/
};
...
}
...
&ocp {
test_helper: helper {
compatible = "bone-pinmux-helper";
status = "okay";
pinctrl-names = "DATA_OUT_A00", "DATA_OUT_A01";
pinctrl-0 = <&DATA_OUT_A00>;
pinctrl-1 = <&DATA_OUT_A01>;
}
}
EDIT:-------------------
I'm using main Ti kernel 4.9 version. If I want to define these two pins as output this DTS works fine but ONLY for output mode. I can export pins as input but when I read its value it always returns zero.
#include "am33xx.dtsi"
#include <dt-bindings/interrupt-controller/irq.h>
/ {
model = "My Custom board C335x";
compatible = "ti,am33xx";
cpus {
cpu#0 {
cpu0-supply = <&vdd_core>;
};
};
...
gpio-leds {
compatible = "gpio-leds";
pinctrl-names = "default";
pinctrl-0 = <&led_pins>;
// the following two blocks make the pins available in /sys/class/leds
// if removed can be accessd with /sys/class/gpios
test_led1 {
label = "test";
gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
test_led2 {
label = "test";
gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
default-state = "off";
};
};
}
&am33xx_pinmux {
pinctrl-names = "default";
led_pins: pinmux_led_pins {
pinctrl-single,pins = <
0x158 (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /*gpio0_4*/
0x15C (PIN_OUTPUT_PULLDOWN | MUX_MODE7) /*gpio0_5*/
>;
};
...
}
If I leave these pins in the DTS file, I still can export them but I can't get any input or set a value as output to them.
Hello I am looking at this documentation.
https://www.kernel.org/doc/html/v4.11/driver-api/i2c.html
My goal is simply to write some data to an EEPROM using an I2C bus. I am a bit confused on which functions to use and how to populate the structures that are needed for those functions.
My guess is that I will need to create an i2c_client to represent the EEPROM. I have the location of the EEPROM from this device tree.
&i2c0 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
i2cswitch#74 {
compatible = "nxp,pca9548";
#address-cells = <1>;
#size-cells = <0>;
reg = <0x74>;
i2c#2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
eeprom#54 {
compatible = "at,24c08";
reg = <0x54>;
};
};
};
};
How would I go about filling the i2c_client struct with this data?
Then I am guessing I would use this function
int i2c_master_send(const struct i2c_client * client, const char * buf, int count)
And provid it the client struct and a string that I want to write as well as the length of that string with the stipulation it is less than 64k. In this case the CPU is the master?
What header files will I need to included to use the functions and structs that are provided by the documentation?
Thanks.