Hi TI support teams,
I'm now implementing a Linux Driver to control an IC which is using SPI as communication channel and have some issue.
Please help check.
My devicetree will define the pin like these:
&main_spi5 { pinctrl-names = "default"; pinctrl-0 = <&poc_sepic_hsd_pins_default>; status = "okay"; /* POC_1_2 */ poc_1_2: poc_1_2@0 { compatible = "ti,tps92682"; reg = <0>; /* CE0 */ spi-max-frequency = <1000000>; pinctrl-names = "default"; pinctrl-0 = <&mygpio0_pins_sepic_enable>; en-gpios = <&main_gpio0 19 GPIO_ACTIVE_HIGH>; enable-active-high; }; };
poc_sepic_hsd_pins_default: poc_sepic_hsd_pins_default { pinctrl-single,pins = < J721E_IOPAD(0x1a0, PIN_INPUT, 3) /* (W29) RGMII6_TXC.SPI5_CLK SPI_POC_HSD.SCLK */ J721E_IOPAD(0x19c, PIN_INPUT, 3) /* (W27) RGMII6_TD0.SPI5_CS0 SPI_CS_POC_1_2 */ J721E_IOPAD(0x1b4, PIN_INPUT, 3) /* (W25) RGMII6_RD0.SPI5_CS1 SPI_CS_POC_3_4 */ J721E_IOPAD(0x194, PIN_INPUT, 3) /* (W28) RGMII6_TD2.SPI5_CS2 */ J721E_IOPAD(0x198, PIN_INPUT, 3) /* (V25) RGMII6_TD1.SPI5_D0 SPI_POC_HSD.MISO */ J721E_IOPAD(0x1b0, PIN_INPUT, 3) /* (W24) RGMII6_RD1.SPI5_D1 SPI_POC_HSD.MOSI */ >; };
And here is my driver that using the spi_write/spi_read to wirte/read via SPI:
#include <linux/init.h> #include <linux/module.h> #include <linux/ioctl.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/err.h> #include <linux/list.h> #include <linux/errno.h> #include <linux/mutex.h> #include <linux/slab.h> #include <linux/compat.h> #include <linux/of.h> #include <linux/of_device.h> #include <linux/acpi.h> #include <linux/spi/spi.h> #include <linux/spi/spidev.h> #include <linux/device.h> #include <linux/delay.h> /* Register definition */ #define REG_EN 0x00 #define REG_CFG1 0x01 #define REG_SWDIV 0x03 #define REG_ISLOPE 0x04 #define REG_SOFTSTART 0x06 #define REG_PWMDIV 0x09 #define REG_CH1PWMH 0x0B #define REG_CH1PWML 0x0A #define REG_CH2PWMH 0x0D #define REG_CH2PWML 0x0C #define REG_ILIM 0x0E #define REG_IFT 0x0F #define REG_MFT 0x10 #define REG_FEN1 0x13 #define REG_FEN2 0x14 #define REG_OV 0x16 #define REG_FLT1 0x11 #define REG_FLT2 0x12 #define REG_CH1IADJ 0x07 #define REG_CH2IADJ 0x08 #define NUM_OF_CONF 13 #define WRITE_REG 1 #define READ_REG 0 #define REG_ACTION_IDX 0 #define REG_ADDR_IDX 1 #define REG_VAL_IDX 2 static u16 regConfig[NUM_OF_CONF][3] = { {READ_REG, REG_FLT1, 0x00}, // READ FLT1 - clear all the fault read bits and PC bit {READ_REG, REG_FLT2, 0x00}, // READ FLT1 - clear all the fault read bits and PC bit {WRITE_REG, REG_EN, 0xBC}, // Set FPINRST = 1 Enable every channel setup - B0 {WRITE_REG, REG_CFG1, 0x03}, // LH = 1, chanel set to CV mode - 0x13 {WRITE_REG, REG_SWDIV, 0x00}, // 00: Division = 2. CHxCLK = fCLKM / 2 FSW= 400kHz {WRITE_REG, REG_ISLOPE, 0x55}, // VSLP(PK) = 250 mV // {WRITE_REG, REG_SOFTSTART, 0x66}, // 0110: Division factor = 16 for both channel // {WRITE_REG, REG_PWMDIV, 0x01}, // 01: PWMCLK = CLKM ÷ 2 // {WRITE_REG, REG_CH1PWMH, 0x03}, // Setup PWM width // {WRITE_REG, REG_CH1PWML, 0xFF}, // Setup PWM width // {WRITE_REG, REG_CH2PWMH, 0x03}, // Setup PWM width // {WRITE_REG, REG_CH2PWML, 0xFF}, // Setup PWM width {WRITE_REG, REG_ILIM, 0x5F}, // 01: ILIM event counter threshold = 4, 11: VILIM(THR) = 250 mV {WRITE_REG, REG_IFT, 0x0A}, // 10: ILIM Fault Timer maximum count = 16 {WRITE_REG, REG_MFT, 0x33}, // 0011: Main Fault Timer maximum count = 2000 // {WRITE_REG, REG_FEN1, 0x3F}, // CHxFBOEN, CHxOVEN, CHxUVEN set - Fault enable for FB pin open fault, output OV, output UV // {WRITE_REG, REG_FEN2, 0x0F}, // CHxILIMEN, CHxISOEN set - Fault enable for ILIM fault, ISN pin open fault {WRITE_REG, REG_OV, 0x44}, // 100: OVTHR = VFBREF×(1.100) - Factor 1,1 for both channels; max 45V {WRITE_REG, REG_CH1IADJ, 0x96}, // Equaltion 44 calculation - 12V output {WRITE_REG, REG_CH2IADJ, 0x96}, // Equaltion 44 calculation - 12V output {WRITE_REG, REG_EN, 0x33}, // Set FPINRST = 1 Enable every channel setup - B0 }; /*-------------------------------------------------------------------------*/ #define SPIDEV_MAJOR 0 #define N_SPI_MINORS 127 /* ... up to 256 */ static DECLARE_BITMAP(minors, N_SPI_MINORS); #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH \ | SPI_LSB_FIRST | SPI_READY | SPI_TX_QUAD | SPI_RX_QUAD ) struct spidev_data { dev_t devt; spinlock_t spi_lock; struct spi_device *spi; struct list_head device_entry; /* TX/RX buffers are NULL unless this device is open (users > 0) */ struct mutex buf_lock; unsigned users; u16 *tx_buffer; u16 *rx_buffer; u32 speed_hz; struct device *dev; }; static LIST_HEAD(device_list); static DEFINE_MUTEX(device_list_lock); static unsigned bufsiz = 4096; module_param(bufsiz, uint, S_IRUGO); MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); static struct class *tps92682_spidev_class; static const struct of_device_id tps92682_dt_ids[] = { { .compatible = "ti,tps92682" }, {}, }; MODULE_DEVICE_TABLE(of, tps92682_dt_ids); /*-------------------------------------------------------------------------*/ u16 assembleSPICmd_682(u16 write, u16 address, u8 data) { /* debug print to check input */ // printk("assembleSPICmd_682 - write = %u, addr = 0x%x, data= 0x%x", write, address, data); u16 assembledCmd = 0; // Build this to shift through parity calculation u16 parity = 0; // Parity bit calculated here u16 packet = 0; // This will be what we send if(write) { assembledCmd |= 0x8000; // Set CMD = 1 } assembledCmd |= (((address << 9) & 0x7E00) | (u16)(data & 0x00FF)); packet = assembledCmd; // Calculate parity while(assembledCmd > 0) { // Count the number of 1s in the LSb if(assembledCmd & 0x0001) { parity++; } // Shift right assembledCmd >>= 1; } // If the LSb is a 0 (even # of 1s), we need to add the odd parity bit if(!(parity & 0x0001)) { packet |= (1 << 8); } printk("assembleSPICmd_682 - packet 0x%x", packet); return(packet); } static ssize_t tps92682_transfer(struct spi_device *spi, u16 isWrite, u16 address, u8 data) { u16 spi92682cmd = 0; ssize_t status; struct spidev_data *spidev = spi_get_drvdata(spi); int ret = 0; mutex_lock(&spidev->buf_lock); // AssembleSPI command spi92682cmd = assembleSPICmd_682(isWrite, address, data); memset(spidev->tx_buffer, 0, bufsiz); memset(spidev->rx_buffer, 0, bufsiz); memcpy(spidev->tx_buffer, &spi92682cmd, sizeof(spi92682cmd)); // if(isWrite) // { ret = spi_write(spi,spidev->tx_buffer,2); if (ret) { printk("Failed writing: %d\n", ret); } // }else // { // ret = spi_read(spi,spidev->rx_buffer,2); // if (ret) { // printk("Failed reading: %d\n", ret); // } // } printk("Write/Read OK: %d\n", ret); mutex_unlock(&spidev->buf_lock); return status; } /*-------------------------------------------------------------------------*/ void tps92682_chip_init(struct spi_device *spi) { struct spidev_data *spidev = spi_get_drvdata(spi); int retval = 0; u32 tmp; mutex_lock(&spidev->buf_lock); spi->mode = SPI_MODE_0; spi->bits_per_word = 16; retval = spi_setup(spi); if (retval < 0) { printk("spi mode 0x%x 0x%x setup failed\n", spi->mode, tmp); } else printk("spi mode 0x%x 0x%x setup ok\n", spi->mode, tmp); mutex_unlock(&spidev->buf_lock); /* allocate buffer for tx & rx msg */ if (!spidev->tx_buffer) { spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL); if (!spidev->tx_buffer) { printk("spidev->tx_buffer allocate failed"); return; } } printk("tps92682_chip_init tx_buffer pointer 0x%x", spidev->tx_buffer); if (!spidev->rx_buffer) { spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL); if (!spidev->rx_buffer) { printk("spidev->rx_buffer allocate failed"); return; } } // /* First check */ // tps92682_transfer(spi, 1, 0x00, 0x00); // // udelay(100); // tps92682_transfer(spi, 1, 0x00, 0x01); // // udelay(100); // tps92682_transfer(spi, 1, 0x00, 0x02); // // udelay(100); // tps92682_transfer(spi, 0, 0x00, 0x00); // // udelay(100); // tps92682_transfer(spi, 1, 0x00, 0x03); // // udelay(100); // tps92682_transfer(spi, 1, 0x00, 0xB0); // tps92682_transfer(spi, 0, 0x00, 0x00); // tps92682_transfer(spi, 0, 0x00, 0x00); // tps92682_transfer(spi, 0, 0x11, 0x00); // tps92682_transfer(spi, 0, 0x11, 0x00); // tps92682_transfer(spi, 0, 0x12, 0x00); // tps92682_transfer(spi, 0, 0x12, 0x00); // tps92682_transfer(spi, 1, 0x00, 0xB0); // tps92682_transfer(spi, 1, 0x00, 0xB0); // tps92682_transfer(spi, 0, 0x00, 0x00); // udelay(100); // tps92682_transfer(spi, 0, 0x00, 0x00); // udelay(100); // tps92682_transfer(spi, 1, 0x00, 0x0C); // udelay(100); // tps92682_transfer(spi, 0, 0x00, 0x00); // udelay(100); // tps92682_transfer(spi, 0, 0x00, 0x00); // udelay(100); /* Chip config */ int i = 0; for (i = 0; i < NUM_OF_CONF; i++) { tps92682_transfer(spi, regConfig[i][REG_ACTION_IDX], regConfig[i][REG_ADDR_IDX], regConfig[i][REG_VAL_IDX]); // udelay(100); } return; } static const struct file_operations tps92682_fops = { .owner = THIS_MODULE, }; static int tps92682_probe(struct spi_device *spi) { struct spidev_data *spidev; int status, ret; unsigned long minor; int error; /* * spidev should never be referenced in DT without a specific * compatible string, it is a Linux implementation thing * rather than a description of the hardware. */ WARN(spi->dev.of_node && of_device_is_compatible(spi->dev.of_node, "spidev"), "%pOF: buggy DT: spidev listed directly in DT\n", spi->dev.of_node); /* Allocate driver data */ spidev = kzalloc(sizeof(*spidev), GFP_KERNEL); if (!spidev) return -ENOMEM; printk("tps92682_probe spidev: 0x%x", spidev); /* Initialize the driver data */ spidev->spi = spi; spin_lock_init(&spidev->spi_lock); mutex_init(&spidev->buf_lock); INIT_LIST_HEAD(&spidev->device_entry); /* If we can allocate a minor number, hook up this device. * Reusing minors is fine so long as udev or mdev is working. */ mutex_lock(&device_list_lock); minor = find_first_zero_bit(minors, N_SPI_MINORS); if (minor < N_SPI_MINORS) { spidev->devt = MKDEV(SPIDEV_MAJOR, minor); spidev->dev = device_create(tps92682_spidev_class, &spi->dev, spidev->devt, spidev, "tps92682_spidev%d.%d", spi->master->bus_num, spi->chip_select); status = PTR_ERR_OR_ZERO(spidev->dev); if(IS_ERR(spidev->dev)) printk("tps92682 device_create error"); printk("tps92682_probe device_create with status: %d - %d - %d - %d - %x", status, spi->master->bus_num, spi->chip_select, spidev->devt,spidev->dev); } else { dev_dbg(&spi->dev, "no minor number available!\n"); status = -ENODEV; } if (status == 0) { set_bit(minor, minors); list_add(&spidev->device_entry, &device_list); } mutex_unlock(&device_list_lock); spidev->speed_hz = spi->max_speed_hz; if (status == 0) spi_set_drvdata(spi, spidev); else kfree(spidev); struct gpio_desc *gpio_en = devm_gpiod_get_optional(spidev->dev, "en", GPIOD_OUT_HIGH); error = PTR_ERR_OR_ZERO(gpio_en); if (error) { printk("failed to request gpio: %d\n", error); return error; } printk("gpio val 1: %d\n", gpiod_get_value(gpio_en)); // printk("tps92682 gpio_en ok"); // gpiod_set_value(gpio_en, 1); // msleep(2000); // printk("gpio val 2: %d\n", gpiod_get_value(gpio_en)); // printk("tps92682 gpio_en set ok"); // struct gpio_desc *gpio_en; // int ret; // gpio_hog_lookup_name("p19", &gpio_en); // if (ret) // printk("Error gpio lookup"); // printk("gpio val 2: %d\n", dm_gpio_get_value(gpio_en)); tps92682_chip_init(spi); return status; } static int tps92682_remove(struct spi_device *spi) { printk("tps92682_remove"); struct spidev_data *spidev = spi_get_drvdata(spi); /* prevent new opens */ mutex_lock(&device_list_lock); /* make sure ops on existing fds can abort cleanly */ spin_lock_irq(&spidev->spi_lock); spidev->spi = NULL; spin_unlock_irq(&spidev->spi_lock); list_del(&spidev->device_entry); device_destroy(tps92682_spidev_class, spidev->devt); clear_bit(MINOR(spidev->devt), minors); if (spidev->users == 0) kfree(spidev); mutex_unlock(&device_list_lock); return 0; } static struct spi_driver tps92682_spi_driver = { .driver = { .name = "tps92682", .of_match_table = of_match_ptr(tps92682_dt_ids), }, .probe = tps92682_probe, .remove = tps92682_remove, /* NOTE: suspend/resume methods are not necessary here. * We don't do anything except pass the requests to/from * the underlying controller. The refrigerator handles * most issues; the controller driver handles the rest. */ }; /*-------------------------------------------------------------------------*/ static int __init tps92682_spidev_init(void) { int status; printk("tps92682_spidev_init"); /* Claim our 256 reserved device numbers. Then register a class * that will key udev/mdev to add/remove /dev nodes. Last, register * the driver which manages those device numbers. */ // BUILD_BUG_ON(N_SPI_MINORS > 256); status = register_chrdev(SPIDEV_MAJOR, "tps92682_spi", &tps92682_fops); if (status < 0) { printk("tps92682_spidev_init register_chrdev with status: %d", status); return status; } tps92682_spidev_class = class_create(THIS_MODULE, "tps92682_spidev"); if (IS_ERR(tps92682_spidev_class)) { unregister_chrdev(SPIDEV_MAJOR, tps92682_spi_driver.driver.name); printk("tps92682_spidev_init class_create with status: %d", status); return PTR_ERR(tps92682_spidev_class); } status = spi_register_driver(&tps92682_spi_driver); if (status < 0) { class_destroy(tps92682_spidev_class); unregister_chrdev(SPIDEV_MAJOR, tps92682_spi_driver.driver.name); printk("tps92682_spidev_init spi_register_driver with status: %d", status); } printk("tps92682_spidev_init done with status: %d", status); return status; } module_init(tps92682_spidev_init); static void __exit tps92682_spidev_exit(void) { printk("tps92682_spidev_exit"); spi_unregister_driver(&tps92682_spi_driver); class_destroy(tps92682_spidev_class); unregister_chrdev(SPIDEV_MAJOR, tps92682_spi_driver.driver.name); } module_exit(tps92682_spidev_exit); /*-------------------------------------------------------------------------*/ MODULE_DESCRIPTION("SPI controller driver for TI TPS92682"); MODULE_AUTHOR("Loc Ta Vinh <LocTV3@fpt.com>"); MODULE_LICENSE("GPL");
Could you please help take a look to see if my implementation is ok? Are there any issue with it because it seems that the SPI communicate still not work.
I've captured the SPI data like below. I use logic analyzer to capture it.
Here you can see the SCK, MOSI and MISO.
The SCK and MOSI seems normal, but I don't see any value on the MISO line.
Could you please take a look about that. I'm not sure what I'm missing here.
Best regards,
Loc.