Sharing PCI device with 2 different PCI drivers - linux-kernel

I am writing pci driver to access the Intel device with ID PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0., which is at ff:12.0 (device ID 0x6fa0).
But, turned out sbridge_edac driver was already installed and associated with that PCI device. This causes my pci_register_driver() to return error.
Is there a way to make my PCI driver use that PCI device if another driver already associated with it?
#define PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0 0x6fa0
static const struct pci_device_id sbridge_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SBRIDGE_IMC_HA0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_IBRIDGE_IMC_HA0_TA)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_HASWELL_IMC_HA0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BROADWELL_IMC_HA0)},
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_KNL_IMC_SAD0)},
{0,} /* 0 terminated list. */
};
MODULE_DEVICE_TABLE(pci, sbridge_pci_tbl);
static struct pci_driver discovery_pci_driver ={
.name = DRIVER_NAME,
.probe = discovery_probe,
.remove = discovery_remove,
.id_table = sbridge_pci_tbl,
};
static int __init discovery_init(void)
{
int res;
res = pci_register_driver(&discovery_pci_driver);
if(res<0) {
PERR("Adding driver to pci core failed\n");
return res;
}
return 0;
}
$lspci -x
ff:12.0 System peripheral: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0
Subsystem: Intel Corporation Xeon E7 v4/Xeon E5 v4/Xeon E3 v4/Xeon D Home Agent 0
Kernel driver in use: sbridge_edac

Since you want to create your own driver and associate it with the Intel hardware you can do one of the following:
Remove the sbridge_edac driver from the kernel config and recompile. You could also add your driver at this time.
Create a startup script that unloads the sbridge_edac driver and then loads in your driver.
Extend the sbridge_edac driver with your functionality (edit their code).
Create a function in your user space code that determines what driver is running and call it accordingly. You would have to put your driver functionality in your user space code and work with the sbridge_edac. But other user programs could access this one program instead of a driver.

Related

Access Raspberry Pi 4 system timer

I'm having trouble reading the Raspberry Pi 4 system timer.
My understanding is that the LO 32 bits should be at address 0x7e003004.
My reads always return -1.
Here's how I am trying:
int fd;
unsigned char* start;
uint32_t* t4lo;
fd = open("/dev/mem", O_RDONLY);
if (fd == -1)
{
perror("open /dev/mem");
exit(1);
}
start = (unsigned char*)mmap(0, getpagesize(), PROT_READ, MAP_SHARED,
fd, 0x7e003000);
t4lo = (unsigned int *)(start + 0x04);
...
uint32_t Rpi::readTimer(void)
{
return *t4lo;
}
I should be checking the value of start, but gdb tells me it's reasonable so I don't think that's the problem.
(gdb) p t4lo
$4 = (uint32_t *) 0xb6f3a004
and gdb won't let me access *t4lo. Any ideas?
Edit: clock_gettime() is fulfilling my needs, but I'm still curious.
A closer look at https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf
figure 1 on page 5 shows that addresses vary depending upon who's looking at things. If you start with 0x7c00_0000 on the left side and follow it over to the right, it's apparent that it shows up at 0xfc00_0000 to the processor. So changing the timer base address to 0xfe00_3000 fixed the problem.
The secret is hidden in section 1.2.4:
So a peripheral described in this document as being at legacy address 0x7Enn_nnnn
is available in the 35-bit address space at 0x4_7Enn_nnnn, and visible to the ARM
at 0x0_FEnn_nnnn if Low Peripheral mode is enabled.
The address of the BCM2711 ARM Peripherals is the bus address which is not the same as the physical address in most systems. The bus address is easily used by DMA(Direct Memory Access) controller. mmap creates a new mapping from physical address to virtual address not bus address. So you can't use mmap funtion with parameter 0x7e003000. The rich answer is right.
So changing the timer base address to 0xfe00_3000 fixed the problem.
In addtion, your program run in the User space, only virtual address can you directly use.

U-Boot ethernet hardware MAC address in environment on SD card

According to U-Boot's README.enetaddr "Usage" section:
If the hardware design mandates that the MAC address is stored in some special place (like EEPROM etc...), then the board specific init code (such as the board-specific misc_init_r() function) is responsible for locating the MAC address(es) and initializing the respective environment variable(s) from it. Note that this shall be done if, and only if, the environment does not already contain these environment variables, i.e. existing variable definitions must not be overwritten.
During runtime, the ethernet layer will use the environment variables to sync the MAC addresses to the ethernet structures. All ethernet driver code should then only use the enetaddr member of the eth_device structure. This is done on every network command, so the ethernet copies will stay in sync.
Let us suppose a device has an EEPROM containing a universally administered MAC address and that the device's misc_init_r() function reads that MAC address and writes it to an environment variable (e.g. "ethaddr") if and only if the environment variable doesn't exist. Let us also suppose that the environment is later saved by the U-Boot saveenv command, and moreover, that the whole system is running off a removable storage device such as an SD card, where the U-Boot environment is also saved.
EDIT
In my case, I am in the above situation for a new custom board which has a "MAC address EEPROM" from Micronix containing a unique, universally administered MAC address, and the board's only available storage location for a saved U-Boot environment is the SD card from which it boots. I have the following code for the board-specific misc_init_r() function:
#include <common.h>
#ifndef CONFIG_SPL_BUILD
static int my_board_read_mac(uchar *mac)
{
int ret;
/* EEPROM is at bus 0. */
ret = i2c_set_bus_num(0);
if (ret) {
printf("Cannot select EEPROM I2C bus - err %d\n", ret);
return ret;
}
/* EEPROM is at address 0x50. MAC address is at offset 0xfa. */
ret = eeprom_read(0x50, 0xfa, mac, 6);
if (ret) {
printf("Cannot read I2C EEPROM - err %d\n", ret);
return ret;
}
return 0;
}
static int my_board_do_mac(void)
{
int ret;
uchar mac[6];
ret = my_board_read_mac(mac);
if (ret) {
printf("Failed to read MAC address - err %d\n", ret);
return ret;
}
if (!is_valid_ethaddr(mac)) {
printf("Read invalid MAC address %pM\n", mac);
return -EINVAL;
}
if (!getenv("ethaddr")) {
return eth_setenv_enetaddr("ethaddr", mac);
}
return 0;
}
int misc_init_r(void)
{
int ret;
ret = my_board_do_mac();
if (ret) {
printf("Failed to set MAC address - err %d\n", ret);
}
return 0;
}
#endif
My question is: If the SD card is removed from the original device and placed into a similar device (with a different universally administered MAC address in its EEPROM), will the new device use the original device's MAC address rather than it's own, unique MAC address? If so, what is the best way to prevent that from happening?
EDIT2
What I'm looking for is some way to set the MAC address automatically, but not to "pollute" any copy of the environment saved on the SD card with this board specific MAC address. I.e. I want the MAC address to remain tied to the board, rather than to the SD card.
The answer is that this becomes a policy question. The non-default environment (so, what gets saved to someplace when you use saveenv) is intended to be instance-specific. This is why generally speaking board code will see if ethaddr is set, and if so, not try and derive one.
When you move from board to board you can use env default -f -a to restore the running environment to the built-in defaults.
If you want to share the same SD card between multiple boards then you need to come up with a policy to manage this situation.

how to redirect Keil printf out through STM32F4xx USB to connected Windows computer

How can I configure Keil uVision5 to redirect printf output from STM32F4xx out through MCU's USB interface? Then, USB will connect to a Windows computer, virtual port driver, and a terminal program.
I cannot find an example uVision5 project that configures printf to output through the STM32F4xx MCU's USB interface.
The USB of the STM32F4xx MCU has to implement the USB CDC as device class or interface class (https://en.wikipedia.org/wiki/USB_communications_device_class) and in host mode (USB OTG) in its firmware. On the PC's USB has to be created a virtual COM port (driver) to process the RS232 protocol that the STM32F4 sends over the USB CDC (in host mode). When the CDC firmware on the STM32F4 is working correctly, on linux the STM32F4 appears i.e. as /dev/ttyACM0 and this can be accessed with standard RS232 terminal programs.
basics -> http://www.keil.com/support/man/docs/rlarm/rlarm_usb_create_cdc_acm.htm
Here is a basic example how to send serial data over USB CDC (that on pc's USB appears as a Virtual COM port i.e. /dev/ttyACM0): http://visualgdb.com/tutorials/arm/stm32/usb/
See also this http://stm32f4-discovery.net/2014/08/library-24-virtual-com-port-vcp-stm32f4xx/
Follow or adapt the tutorial above to linux (http://visualgdb.com/tutorials/arm/stm32/usb/).
To redirect printf() to USB CDC override the standard I/O routines like in the following. The ARM version of printf() invokes these routines then, see this https://mcuoneclipse.com/2014/07/11/printf-and-scanf-with-gnu-arm-libraries/ (the ARM version of printf() is very different from the x86/x64 version of printf()):
#include<sys/stat.h>
extern"C"
{
int _fstat (int fd, struct stat *pStat)
{
pStat->st_mode = S_IFCHR;
return 0;
}
int _close(int)
{
return -1;
}
int _write (int fd, char *pBuffer, int size)
{
return VCP_write(pBuffer, size);
}
int _isatty (int fd)
{
return 1;
}
int _lseek(int, int, int)
{
return -1;
}
int _read (int fd, char *pBuffer, int size)
{
for (;;)
{
int done = VCP_read(pBuffer, size);
if (done)
return done;
}
}
}
Or use the ARM semihosting interface (http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471c/Bgbjhiea.html). It will provide the functionality automatically to redirect printf() to USB CDC
More information:
http://www.keil.com/forum/60565/ (usbd_STM32F4xx_FS.c)
http://www.beyondlogic.org/usbnutshell/usb1.shtml
This is an example how to implement a USB-RS232 adapter: http://www.wolinlabs.com/blog/stm32f4.virtual.com.port.html
The git also contains many interesting files git clone https://github.com/rowol/stm32_discovery_arm_gcc

Linux Device Tree (DTS): i2c device on USB-I2C bridge

I have an i2c device (touch controller). Usually I would add it to the .dts file like this when it is connected to the SoC i2c master (a tegra chip in my case):
i2c#7000c000 {
st1332: touchscreen#55 {
compatible = "sitronix,st1232";
reg = <0x55>;
interrupt-parent = <&gpio>;
interrupts = <189 IRQ_TYPE_EDGE_FALLING>;
};
};
With the i2c controler i2c#7000c000 being defined in the SoC's .dtsi file:
i2c1: i2c#7000c000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "nvidia,tegra124-i2c";
reg = <0x0 0x7000c000 0x0 0x100>;
interrupts = <0 38 0x04>;
scl-gpio = <&gpio 20 0>; /* gpio PC4 */
sda-gpio = <&gpio 21 0>; /* gpio PC5 */
nvidia,memory-clients = <14>;
status = "okay";
clock-frequency = <400000>;
};
However, I don't want to connect the touch controller to one of the i2c masters from the SoC. Instead I have it connected to a cp2112 USB to i2c bridge.
The cp2112 driver works fine: I can use commands such as i2cget to access it from the command line. But how do I add it to the .dts file so that the touch controller driver will talk to it?
Because USB devices are enumerated automatically, I don't have a node in my .dts file that I can use as parent for the touch controller node. I would assume that I need to create a placeholder node in the .dts file under the usb controller (xusb#70090000 in my case), that is then associated with the enumerated USB device by the kernel, and move the touch controller into this node, but I don't know how to do that. What would such a node for a USB device look like? Or is there a completely different solution to the problem?
I am running Linux 3.10.40 with a backported version of hid-cp2112 from Linux v4.1.0-rc5.
As the hid-cp2112 driver is probed by the USB device enumeration, it does not even try to find itself in the device tree. I have created the following patch to hid-cp2112.c that links the found cp2112 device to a /i2c#cp2112 node in the devie tree. (This of course only works for situations where there is only one cp2112 chip on the USB.)
diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c
index 2bd7f97..fa88590 100644
--- a/drivers/hid/hid-cp2112.c
+++ b/drivers/hid/hid-cp2112.c
## -31,6 +31,8 ##
#include <linux/module.h>
#include <linux/nls.h>
#include <linux/usb/ch9.h>
+#include <linux/of.h>
+#include <linux/of_i2c.h>
#include "hid-ids.h"
enum {
## -1014,6 +1016,7 ## static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
dev->adap.algo = &smbus_algorithm;
dev->adap.algo_data = dev;
dev->adap.dev.parent = &hdev->dev;
+ dev->adap.dev.of_node = of_find_node_by_path("/i2c#cp2112");
snprintf(dev->adap.name, sizeof(dev->adap.name),
"CP2112 SMBus Bridge on hiddev%d", hdev->minor);
init_waitqueue_head(&dev->wait);
## -1029,6 +1032,8 ## static int cp2112_probe(struct hid_device *hdev, const struct hid_device_id *id)
hid_dbg(hdev, "adapter registered\n");
+ of_i2c_register_devices(&dev->adap);
+
dev->gc.label = "cp2112_gpio";
dev->gc.direction_input = cp2112_gpio_direction_input;
dev->gc.direction_output = cp2112_gpio_direction_output;
The entry in the .dts file for the touch controller looks like this:
i2c#cp2112 {
#address-cells = <1>;
#size-cells = <0>;
st1332: touchscreen#55 {
compatible = "sitronix,st1232";
reg = <0x55>;
interrupt-parent = <&gpio>;
interrupts = <189 IRQ_TYPE_EDGE_FALLING>;
};
};
As a reference for those who may encounter similar problems notice that Clifford was backporting the cp2112 driver from Linux 4+ back to v3.10.40.
If you look at the kernel source for i2c busses it seems that they had to register themselves by using of_i2c_register_devices, but this need was removed from kernel v3.12 onwards. This is why the cp2112 driver does not call of_i2c_register_devices.

RaspberryPi: snd_bcm2835 port to mainline kernel

i'm trying to port snd_bcm2835 to mainline kernel(mainly because the rpi official kernel doesn't use device tree).
now the vcio, vchiq, snd_bcm2835 can all be insmod into the kernel, but there's still no valid device for audio playback.
the module_init function of snd_bcm2835 module is bcm2835_alsa_device_init(), it's main job is(a simplify version):
static int bcm2835_alsa_device_init(void)
{
platform_driver_register(&bcm2835_alsa0_driver);
platform_driver_register(&bcm2835_alsa1_driver);
//...repeat for 8 times
}
this bcm2835_alsa_device_init function is called when i insmod snd_bcm2835, it can be run without error.
and the struct bcm2835_alsa0_driver, bcm2835_alsa1_driver look like:
static struct platform_driver bcm2835_alsa0_driver = {
.probe = snd_bcm2835_alsa_probe,
.remove = snd_bcm2835_alsa_remove,
//...
}
the .probe function snd_bcm2835_alsa_probe is the main workhorse, but it was not been called.
so the question is when will this snd_bcm2835_alsa_probe been called?
I have ported 4.0 on raspberry pi using the following steps given by elinux, and i was succesfull . So you can also give a try:Raspberry pi Upstream

Resources