/* * Simple synchronous userspace interface to SPI devices * * Copyright (C) 2006 SWAPP * Andrea Paterniani * Copyright (C) 2007 David Brownell (simplification, cleanup) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "isp_ext.h" static unsigned bus_num = 0; static unsigned csn = 0; static char* sensor = ""; module_param(bus_num, uint, S_IRUGO); MODULE_PARM_DESC(bus_num, "spi bus number"); module_param(csn, uint, S_IRUGO); MODULE_PARM_DESC(csn, "chip select number"); /* some sensor has special dev addr */ module_param(sensor, charp, S_IRUGO); MODULE_PARM_DESC(sensor, "sensor name"); struct spi_master* hi_master; struct spi_device* hi_spi; extern struct bus_type spi_bus_type; #define SPI_MSG_NUM 20 typedef struct hi_spi_message_s { struct spi_transfer t; struct spi_message m; unsigned char buf[8]; } spi_message_s; typedef struct hi_spi_message_info_s { int msg_idx; spi_message_s spi_msg_array[SPI_MSG_NUM]; } spi_message_info_s; static spi_message_info_s g_spi_msg = {0}; /***************************************************************** This function will be called in interrupt route. So use spi_async, can't call spi_sync here. *****************************************************************/ int ssp_write_alt(unsigned int addr1, unsigned int addr1bytenum, unsigned int addr2, unsigned int addr2bytenum, unsigned int data , unsigned int databytenum) { struct spi_master* master = hi_master; struct spi_device* spi = hi_spi; struct spi_transfer* t; struct spi_message* m; unsigned char* buf; int status = 0; unsigned long flags; int buf_idx = 0; int idx = g_spi_msg.msg_idx; g_spi_msg.msg_idx++; if (g_spi_msg.msg_idx > SPI_MSG_NUM - 1) { g_spi_msg.msg_idx = 0; } buf = g_spi_msg.spi_msg_array[idx].buf; t = &g_spi_msg.spi_msg_array[idx].t; m = &g_spi_msg.spi_msg_array[idx].m; /* check spi_message is or no finish */ spin_lock_irqsave(&master->queue_lock, flags); if (m->state != NULL) { spin_unlock_irqrestore(&master->queue_lock, flags); dev_err(&spi->dev, "%s, %s, %d line: spi_message no finish!\n", __FILE__, __func__, __LINE__); return -EFAULT; } spin_unlock_irqrestore(&master->queue_lock, flags); spi->mode = SPI_MODE_3 | SPI_LSB_FIRST; memset(buf, 0, sizeof(g_spi_msg.spi_msg_array[idx].buf)); if (strcmp(sensor, "imx117")) { buf[buf_idx++] = addr1 & (~0x80); } else { /* imx117 has different dev addr format */ buf[buf_idx++] = addr1; } if (2 == addr2bytenum) { buf[buf_idx++] = addr2 >> 8; } buf[buf_idx++] = addr2; if (2 == databytenum) { buf[buf_idx++] = data >> 8; } buf[buf_idx++] = data; t->tx_buf = buf; t->rx_buf = buf; t->len = buf_idx; t->cs_change = 1; t->speed_hz = 2000000; t->bits_per_word = 8; spi_message_init(m); spi_message_add_tail(t, m); m->state = m; status = spi_async(spi, m); if (status) { dev_err(&spi->dev, "%s: spi_async() error!\n", __func__); status = -EFAULT; } return status; } int hi_ssp_write(unsigned int addr1, unsigned int addr1bytenum, unsigned int addr2, unsigned int addr2bytenum, unsigned int data , unsigned int databytenum) { if ((addr1bytenum > 1) || (addr2bytenum > 2) || (databytenum > 2)) { printk("addr1_num: %d, addr2_num: %d, data_num: %d, bit_width not support now.\n", addr1bytenum, addr2bytenum, databytenum); return -1; } #if 0 printk("addr1: 0x%x, addr1_num: %d, addr2: 0x%x, addr2_num: %d, data: 0x%x, data_num: %d.\n", addr1, addr1bytenum, addr2, addr2bytenum, data, databytenum); #endif return ssp_write_alt(addr1, addr1bytenum, addr2, addr2bytenum, data, databytenum); } /***************************************************************** This function can't be called in interrupt route because spi_sync will schedule out. *****************************************************************/ int ssp_read_alt(unsigned char devaddr, unsigned char addr, unsigned char* data) { struct spi_master* master = hi_master; struct spi_device* spi = hi_spi; int status = 0; unsigned long flags; static struct spi_transfer t; static struct spi_message m; static unsigned char buf[8]; int buf_idx = 0; /* check spi_message is or no finish */ spin_lock_irqsave(&master->queue_lock, flags); if (m.state != NULL) { spin_unlock_irqrestore(&master->queue_lock, flags); dev_err(&spi->dev, "\n**********%s, %s, %d line: spi_message no finish!*********\n", __FILE__, __func__, __LINE__); return -EFAULT; } spin_unlock_irqrestore(&master->queue_lock, flags); spi->mode = SPI_MODE_3 | SPI_LSB_FIRST; memset(buf, 0, sizeof(buf)); buf[buf_idx++] = devaddr | 0x80; buf[buf_idx++] = addr; buf[buf_idx++] = 0; t.tx_buf = buf; t.rx_buf = buf; t.len = buf_idx; t.cs_change = 1; t.speed_hz = 2000000; t.bits_per_word = 8; spi_message_init(&m); spi_message_add_tail(&t, &m); m.state = &m; status = spi_sync(spi, &m); if (status) { dev_err(&spi->dev, "%s: spi_async() error!\n", __func__); status = -EFAULT; } *data = buf[2]; printk("func:%s rx_buf = %#x, %#x, %#x\n", __func__, buf[0], buf[1], buf[2]); return status; } #if 0 static void ssp_test(void) { unsigned char data; ssp_write_alt(0x2, 1, 0x14, 1, 0x34, 1); // wait spi write finish msleep(1); ssp_read_alt(0x2, 0x14, &data); } #endif static int __init sensor_spi_dev_init(void) { int status = 0; struct spi_master* master; struct device* dev; char spi_name[128] = {0}; ISP_BUS_CALLBACK_S stBusCb = {0}; stBusCb.pfnISPWriteSSPData = hi_ssp_write; if (CKFN_ISP_RegisterBusCallBack()) { CALL_ISP_RegisterBusCallBack(0, ISP_BUS_TYPE_SSP, &stBusCb); } else { printk("register ssp_write_callback to isp failed, ssp init is failed!\n"); return -1; } master = spi_busnum_to_master(bus_num); if (master) { hi_master = master; snprintf(spi_name, sizeof(spi_name), "%s.%u", dev_name(&master->dev), csn); dev = bus_find_device_by_name(&spi_bus_type, NULL, spi_name); if (dev == NULL) { dev_err(NULL, "chipselect %d has not been used\n", csn); status = -ENXIO; goto end1; } hi_spi = to_spi_device(dev); if (hi_spi == NULL) { dev_err(dev, "to_spi_device() error!\n"); status = -ENXIO; goto end1; } } else { dev_err(NULL, "spi_busnum_to_master() error!\n"); status = -ENXIO; goto end0; } //ssp_test(); end1: put_device(dev); end0: return status; } static void __exit sensor_spi_dev_exit(void) { printk("%s, %s, %d line\n", __FILE__, __func__, __LINE__); } module_init(sensor_spi_dev_init); module_exit(sensor_spi_dev_exit); MODULE_AUTHOR("BVT OSDRV"); MODULE_LICENSE("GPL"); MODULE_ALIAS("sensor spidev");