| /* |
| * Copyright (C) 2017 Amlogic, Inc. All rights reserved. |
| * |
| * 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., |
| * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| * |
| * Description: |
| */ |
| |
| |
| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/delay.h> |
| #include <linux/io.h> |
| #include <linux/interrupt.h> |
| #include <linux/device.h> |
| #include <linux/platform_device.h> |
| #include <linux/spi/spi.h> |
| #include <linux/amlogic/aml_gpio_consumer.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/device.h> |
| #include <linux/slab.h> |
| #include <linux/amlogic/sd.h> |
| #include <linux/of_irq.h> |
| #include <linux/irq.h> |
| #include "aml_spi.h" |
| #include "aml_ci.h" |
| |
| #define AML_MODE_NAME "aml_dvbci_spi" |
| |
| #define AML_SPI_READ_LEN 16 |
| |
| static int AML_CI_GPIO_IRQ_BASE = 251; |
| static struct aml_spi *g_spi_dev; |
| static int aml_spi_debug = 1; |
| static int G_rec_flag = AM_SPI_STEP_INIT; |
| |
| |
| module_param_named(spi_debug, aml_spi_debug, int, 0644); |
| MODULE_PARM_DESC(spi_debug, "enable verbose debug messages"); |
| |
| |
| #define pr_dbg(args...)\ |
| do {\ |
| if (aml_spi_debug)\ |
| printk(args);\ |
| } while (0) |
| #define pr_error(fmt, args...) printk("AML_CI_SPI: " fmt, ## args) |
| |
| struct spi_board_info aml_ci_spi_bdinfo = { |
| .modalias = "ci_spi_dev", |
| .mode = SPI_MODE_0, |
| .max_speed_hz = 1000000, /* 1MHz */ |
| .bus_num = 0, /* SPI bus No. */ |
| .chip_select = 0, /* the device index on the spi bus */ |
| .controller_data = NULL, |
| }; |
| |
| #define NORMAL_MSG (0<<7) |
| #define BROADCAST_MSG (1<<7) |
| #define BLOCK_DATA (0<<6) |
| #define SINGLE_DATA (1<<6) |
| #define CISPI_DEV_ADDR 1 |
| |
| #define INPUT 0 |
| #define OUTPUT 1 |
| #define OUTLEVEL_LOW 0 |
| #define OUTLEVEL_HIGH 1 |
| #define PULLLOW 1 |
| #define PULLHIGH 0 |
| |
| /* |
| sendbuf data struct |
| ---------------------------------------------------- |
| |start flag| cmd | data | addr |end flag | |
| ---------------------------------------------------- |
| | 2 byte | 1byte | 1byte | 2 byte| 2 byte | |
| ---------------------------------------------------- |
| */ |
| |
| #define SENDBUFLEN 8 |
| static u8 sendbuf[SENDBUFLEN];/* send data */ |
| static u8 rbuf[SENDBUFLEN];/*save get data */ |
| /**\brief aml_init_send_buf:init spi send buf |
| * \param cmd: ci cmd |
| * \param data: write value |
| * \param addr: read or write addr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_init_send_buf(u8 cmd, u8 data, u16 addr) |
| { |
| /* start flag */ |
| sendbuf[0] = DATASTART; |
| sendbuf[1] = DATASTART; |
| /* cmd */ |
| sendbuf[2] = cmd; |
| /* data */ |
| sendbuf[3] = data; |
| /* addr senf low 8 bit first,and then send hi 8bit */ |
| sendbuf[4] = addr & 0x00ff; |
| sendbuf[5] = (addr>>8) & 0xff; |
| /* end flag */ |
| sendbuf[6] = DATAEND; |
| sendbuf[7] = DATAEND; |
| return 0; |
| } |
| /**\brief aml_ci_spi_reciver |
| * \param[out] None |
| * \param[in] value,get from spi |
| * \return |
| * - 0:reciver end,-1:reciver |
| * - |
| */ |
| /* |
| data strouct |
| ---------------------------------------------------- |
| |start flag| cmd | data | addr |end flag | |
| ---------------------------------------------------- |
| | 2 byte | 1byte | 1byte | 2 byte| 2 byte | |
| ---------------------------------------------------- |
| */ |
| int aml_ci_spi_paser_bit(uint8_t value) |
| { |
| /* read spi data from slave */ |
| if (G_rec_flag == AM_SPI_STEP_INIT) { |
| /* start type first */ |
| if (value == DATASTART) { |
| rbuf[0] = value; |
| G_rec_flag = AM_SPI_STEP_START1; |
| } |
| } else if (G_rec_flag == AM_SPI_STEP_START1) { |
| /* start2 type seccond */ |
| if (value == DATASTART) { |
| rbuf[1] = value; |
| G_rec_flag = AM_SPI_STEP_START2; |
| } |
| } else if (G_rec_flag == AM_SPI_STEP_START2) { |
| /* cmd type */ |
| /* pr_dbg("spi value=%d\r\n",value); */ |
| rbuf[2] = value; |
| G_rec_flag = AM_SPI_STEP_CMD; |
| } else if (G_rec_flag == AM_SPI_STEP_CMD) { |
| /* data */ |
| rbuf[3] = value; |
| G_rec_flag = AM_SPI_STEP_DATA; |
| } else if (G_rec_flag == AM_SPI_STEP_DATA) { |
| /* ADDR1 */ |
| rbuf[4] = value; |
| G_rec_flag = AM_SPI_STEP_ADDR1; |
| } else if (G_rec_flag == AM_SPI_STEP_ADDR1) { |
| /* ADDR2 type */ |
| rbuf[5] = value; |
| G_rec_flag = AM_SPI_STEP_ADDR2; |
| } else if (G_rec_flag == AM_SPI_STEP_ADDR2) { |
| /* END1 type */ |
| if (value == DATAEND) { |
| rbuf[6] = value; |
| G_rec_flag = AM_SPI_STEP_END1; |
| } |
| } else if (G_rec_flag == AM_SPI_STEP_END1) { |
| /* END2 type */ |
| if (value == DATAEND) { |
| rbuf[7] = value; |
| G_rec_flag = AM_SPI_STEP_END2; |
| /* pr_dbg("spi read value ok end\r\n"); */ |
| return 0; |
| } |
| } |
| return -1; |
| } |
| |
| /**\brief aml_spi_io_api:spi read or write api with mcu |
| * \param spi_dev: aml_spi obj,used this data to get spi obj |
| * \param val: write value |
| * \param len: write value len |
| * \param mode: cmd |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_spi_io_api(struct aml_spi *spi_dev, u8 *val, int len, int mode) |
| { |
| u8 rb[32] = {0}; |
| int ret = 0; |
| int i = 0; |
| u8 rd = 0; |
| int j = 0; |
| int is_retry = 0; |
| if (spi_dev == NULL ) { |
| pr_error("%s spi_dev is null\r\n", __func__); |
| return -EINVAL; |
| } |
| if (spi_dev->spi == NULL) { |
| pr_error("%s spi is null\r\n", __func__); |
| return -EINVAL; |
| } |
| spin_lock(&spi_dev->spi_lock); |
| if (spi_dev->cs_hold_delay) |
| udelay(spi_dev->cs_hold_delay); |
| restart: |
| dirspi_start(spi_dev->spi); |
| if (spi_dev->cs_clk_delay) |
| udelay(spi_dev->cs_clk_delay); |
| |
| ret = dirspi_xfer(spi_dev->spi, val, rb, len); |
| if (ret != 0) |
| pr_dbg("spi xfer value errro ret %d\r\n", ret); |
| /* wait mcu io 1ms */ |
| udelay(1000); |
| /* init rec flag */ |
| G_rec_flag = AM_SPI_STEP_INIT; |
| memset(rbuf, 0, 8); |
| |
| for (i = 0; i < 4 * len; i++) { |
| udelay(50); |
| memset(rb, 0, 32); |
| ret = dirspi_read(spi_dev->spi, rb, AML_SPI_READ_LEN); |
| if (ret != 0) { |
| pr_dbg("spi read value timeout:%x ret %d\r\n", rd, ret); |
| } |
| for (j = 0; j < AML_SPI_READ_LEN; j++) { |
| /*pr_dbg("spi read value rb[%d]: 0x%2x\r\n", j, rb[j]);*/ |
| ret = aml_ci_spi_paser_bit(rb[j]); |
| if (ret == 0) |
| break; |
| } |
| if (ret == 0) |
| break; |
| } |
| if (ret == 0) { |
| rd = rbuf[3];/* data */ |
| } else { |
| pr_dbg("*spi rec flag[%d]index [%d] read error[0x%x] mode[%d]addr[%d]****\r\n", |
| G_rec_flag, i,rd, mode, (val[5] << 8 | val[4]) & 0xffff); |
| dirspi_stop(spi_dev->spi); |
| //only retry once |
| if (is_retry == 0) { |
| is_retry = 1; |
| goto restart; |
| } |
| } |
| if (spi_dev->cs_clk_delay) |
| udelay(spi_dev->cs_clk_delay); |
| |
| /* pr_error("ci spi is stop in %s rd=%d\r\n",__func__,rd);*/ |
| dirspi_stop(spi_dev->spi); |
| |
| spin_unlock(&spi_dev->spi_lock); |
| |
| return rd; |
| } |
| |
| /********************************************************/ |
| /********************************************************/ |
| /******* gpio api *************/ |
| /********************************************************/ |
| /********************************************************/ |
| /**\brief aml_set_gpio_out:set gio out and set val value |
| * \param gpio: gpio_desc obj, |
| * \param val: set val |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_set_gpio_out(struct gpio_desc *gpio, int val) |
| { |
| int ret = 0; |
| if (val < 0) { |
| pr_dbg("gpio out val = -1.\n"); |
| return -1; |
| } |
| if (val != 0) |
| val = 1; |
| ret = gpiod_direction_output(gpio, val); |
| pr_dbg("dvb ci gpio out ret %d set val:%d\n", ret, val); |
| return ret; |
| } |
| #if 0//no used |
| /**\brief aml_set_gpio_in:set gio in |
| * \param gpio: gpio_desc obj, |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_set_gpio_in(struct gpio_desc *gpio) |
| { |
| gpiod_direction_input(gpio); |
| return 0; |
| } |
| #endif |
| |
| /**\brief aml_get_gpio_value:get gio value |
| * \param gpio: gpio_desc obj, |
| * \return |
| * - gpio value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_get_gpio_value(struct gpio_desc *gpio) |
| { |
| int ret = 0; |
| ret = gpiod_get_value(gpio); |
| return ret; |
| } |
| /**\brief aml_gpio_free:free gio |
| * \param gpio: gpio_desc obj, |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_gpio_free(struct gpio_desc *gpio) |
| { |
| gpiod_put(gpio); |
| return 0; |
| } |
| /**\brief spi_get_gpio_by_name:get gpio desc from dts file |
| * \param spi_dev: aml_spi obj |
| * \param gpiod: gpio_desc * obj |
| * \param str: gpio name at dts file |
| * \param input_output: gpio input or output type |
| * \param output_value: gpio out put value |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int spi_get_gpio_by_name(struct aml_spi *spi_dev, |
| struct gpio_desc **gpiod, int *pin_value, |
| char *str, int input_output, int output_level) |
| { |
| int ret = 0; |
| struct device_node *child = NULL; |
| struct platform_device *pdev = spi_dev->pdev; |
| struct device_node *np = pdev->dev.of_node; |
| |
| /*get spi and gpio config from dts*/ |
| /* get device config for dvbci_io*/ |
| child = of_get_child_by_name(np, "dvbci_io"); |
| if (IS_ERR(*gpiod)) { |
| pr_dbg("dvb ci spi %s request failed\n", str); |
| return -1; |
| } |
| |
| *pin_value = of_get_named_gpio_flags(child, str, 0, NULL); |
| *gpiod = gpio_to_desc(*pin_value); |
| if (IS_ERR(*gpiod)) { |
| pr_dbg("spi %s request failed\n", str); |
| return -1; |
| } |
| pr_dbg("spi get_gpio %s %p %d\n", str, *gpiod, *pin_value); |
| gpio_request(*pin_value, AML_MODE_NAME); |
| |
| if (input_output == OUTPUT) { |
| ret = gpiod_direction_output(*gpiod, output_level); |
| } else if (input_output == INPUT) { |
| ret = gpiod_direction_input(*gpiod); |
| /*ret |= gpiod_set_pullup(*gpiod, 1);*/ |
| } else { |
| pr_error("spi Request gpio direction invalid\n"); |
| } |
| return ret; |
| } |
| /********************************************************/ |
| /********************************************************/ |
| /******* gpio api end *************/ |
| /********************************************************/ |
| /********************************************************/ |
| #if 1 |
| /**\brief aml_ci_cis_test_by_spi:test cis |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param addr: read addr |
| * \return |
| * - test :ok |
| * - -EINVAL : error |
| */ |
| /**\brief aml_ci_full_test_by_spi:ci full test |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param addr: read addr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_full_test_by_spi( |
| struct aml_ci *ci_dev, int slot, int addr) |
| { |
| u8 data = 0; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| aml_init_send_buf(AM_CI_CMD_FULLTEST, data, addres); |
| value = aml_spi_io_api(spi_dev, |
| sendbuf, SENDBUFLEN, AM_CI_CMD_FULLTEST); |
| pr_dbg("FULL : TEST END \r\n"); |
| return value; |
| } |
| #endif |
| /**\brief aml_ci_mem_read_by_spi:io read from cam |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param addr: read addr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_mem_read_by_spi( |
| struct aml_ci *ci_dev, int slot, int addr) |
| { |
| u8 data = 0; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| aml_init_send_buf(AM_CI_CMD_MEMR, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_MEMR); |
| /*pr_dbg("Read : mem[%d] = 0x%x\n", addr, value);*/ |
| return value; |
| } |
| /**\brief aml_ci_mem_write_by_spi:io write to cam by spi api |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param addr: write addr |
| * \param addr: write value |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_mem_write_by_spi( |
| struct aml_ci *ci_dev, int slot, int addr, u8 val) |
| { |
| u8 data = val; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| aml_init_send_buf(AM_CI_CMD_MEMW, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_MEMW); |
| /*pr_dbg("write : mem[%d] = 0x%x\n", addr, data);*/ |
| return value; |
| } |
| /**\brief aml_ci_io_read_by_spi:io read from cam by spi api |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param addr: read addr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_io_read_by_spi( |
| struct aml_ci *ci_dev, int slot, int addr) |
| { |
| u8 data = 0; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| aml_init_send_buf(AM_CI_CMD_IOR, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_IOR); |
| /*pr_dbg("read : io[%d] = 0x%x\n", addr, value);*/ |
| return value; |
| } |
| /**\brief aml_ci_io_write_by_spi:io write to cam |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param addr: write addr |
| * \param addr: write value |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_io_write_by_spi( |
| struct aml_ci *ci_dev, int slot, int addr, u8 val) |
| { |
| u8 data = val; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| /*add by chl,need add time delay*/ |
| mdelay(10); |
| aml_init_send_buf(AM_CI_CMD_IOW, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_IOW); |
| /*pr_dbg("write : ATTR[%d] = 0x%x\n", addr, data);*/ |
| return value; |
| } |
| |
| |
| /**\brief aml_ci_rst_by_spi:reset cam by spi |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_rst_by_spi( |
| struct aml_ci *ci_dev, int slot, int level) |
| { |
| u8 data = (u8)level; |
| u16 addres = 0; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| /*add by chl,need add time delay*/ |
| mdelay(10); |
| aml_init_send_buf(AM_CI_CMD_RESET, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_RESET); |
| return value; |
| } |
| |
| /**\brief aml_ci_power_by_spi:power cam by spi |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param enable: enable or disable cam |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_power_by_spi( |
| struct aml_ci *ci_dev, int slot, int enable) |
| { |
| u8 data = (u8)enable; |
| u16 addres = 0; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| /*add by chl,need add time delay*/ |
| /*power is controled by mcu*/ |
| if (0) { |
| mdelay(10); |
| aml_init_send_buf(AM_CI_CMD_POWER, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_POWER); |
| } |
| return value; |
| } |
| |
| /**\brief aml_ci_getcd12_by_spi:get cd12 cam by spi |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param cd12: cd1 or cd2 value |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_getcd12_by_spi( |
| struct aml_ci *ci_dev, int slot, int cd12) |
| { |
| u8 data = (u8)cd12; |
| u16 addres = 0; |
| int value = 0; |
| struct aml_spi *spi_dev = ci_dev->data; |
| /*add by chl,need add time delay*/ |
| mdelay(10); |
| aml_init_send_buf(AM_CI_CMD_GETCD12, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_GETCD12); |
| return value; |
| } |
| |
| |
| |
| /**\brief aml_ci_slot_reset:reset slot |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_slot_reset(struct aml_ci *ci_dev, int slot) |
| { |
| struct aml_spi *spi_dev = ci_dev->data; |
| pr_dbg("Slot(%d): Slot RESET\n", slot); |
| aml_pcmcia_reset(&spi_dev->pc); |
| dvb_ca_en50221_cimcu_camready_irq(&ci_dev->en50221_cimcu, 0); |
| return 0; |
| } |
| /**\brief aml_ci_slot_shutdown:show slot |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| * readme:no use this api |
| */ |
| static int aml_ci_slot_shutdown(struct aml_ci *ci_dev, int slot) |
| { |
| pr_dbg("Slot(%d): Slot shutdown\n", slot); |
| return 0; |
| } |
| /**\brief aml_ci_ts_control:control slot ts |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| * readme:no use this api |
| */ |
| static int aml_ci_ts_control(struct aml_ci *ci_dev, int slot) |
| { |
| pr_dbg("Slot(%d): TS control\n", slot); |
| return 0; |
| } |
| /**\brief aml_ci_slot_status:get slot status |
| * \param ci_dev: aml_ci obj,used this data to get spi_dev obj |
| * \param slot: slot index |
| * \param open: no used |
| * \return |
| * - cam status |
| * - -EINVAL : error |
| */ |
| static int aml_ci_slot_status(struct aml_ci *ci_dev, int slot, int open) |
| { |
| struct aml_spi *spi_dev = ci_dev->data; |
| |
| pr_dbg("Slot(%d): Poll Slot status\n", slot); |
| |
| if (spi_dev->pc.slot_state == MODULE_INSERTED) { |
| pr_dbg("CA Module present and ready\n"); |
| return DVB_CA_EN50221_POLL_CAM_PRESENT | |
| DVB_CA_EN50221_POLL_CAM_READY; |
| } else { |
| pr_error("CA Module not present or not ready\n"); |
| } |
| return -EINVAL; |
| } |
| #if 1 |
| /**\brief aml_ci_gio_get_irq:get gpio cam irq pin value |
| * \return |
| * - irq pin value |
| * - -EINVAL : error |
| */ |
| static int aml_ci_gio_get_irq(void) |
| { |
| int ret = 0; |
| if (g_spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) |
| ret = aml_get_gpio_value(g_spi_dev->irq_cam_pin); |
| else if (g_spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) |
| ret = aml_get_gpio_value(g_spi_dev->mcu_irq_pin); |
| else |
| pr_error("aml_ci_gio_get_irq io type not surport\n"); |
| return ret; |
| } |
| #endif |
| |
| /********************************************************/ |
| /********************************************************/ |
| /******* for pcmcid api *************/ |
| /********************************************************/ |
| /********************************************************/ |
| /**\brief aml_gio_power:set power gpio hi or low |
| * \param pc: aml_pcmcia obj,used this priv to get spi_dev obj |
| * \param enable: power pin hi or low |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int aml_gio_power(struct aml_pcmcia *pc, int enable) |
| { |
| int ret = 0; |
| struct aml_spi *spi_dev = pc->priv; |
| if (spi_dev == NULL) { |
| pr_dbg("spi dev is null %s : %d\r\n", __func__, enable); |
| return -1; |
| } |
| pr_dbg("%s : %d\r\n", __func__, enable); |
| if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) { |
| if (enable == AML_PWR_OPEN) { |
| /*hi level ,open power*/ |
| ret = aml_set_gpio_out(spi_dev->pwr_pin, AML_GPIO_HIGH); |
| } else { |
| /*low level ,close power*/ |
| ret = aml_set_gpio_out(spi_dev->pwr_pin, AML_GPIO_LOW); |
| } |
| } else if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI_T312) { |
| //no need power cam,we power cam card on MCU. |
| aml_ci_power_by_spi((struct aml_ci *)spi_dev->priv, 0, enable); |
| } else { |
| pr_dbg("aml_gio_power type [%d] enable: %d\r\n", spi_dev->io_device_type, enable); |
| } |
| return ret; |
| } |
| /**\brief aml_gio_reset:set reset gpio hi or low |
| * \param pc: aml_pcmcia obj,used this priv to get spi_dev obj |
| * \param enable: reset pin hi or low |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int aml_gio_reset(struct aml_pcmcia *pc, int enable) |
| { |
| /*need set hi and sleep set low*/ |
| int ret = 0; |
| struct aml_spi *spi_dev = pc->priv; |
| |
| if (spi_dev != NULL) |
| pr_dbg("%s : %d \r\n", __func__, enable); |
| |
| pr_dbg("%s : %d type: %d\r\n", __func__, enable, spi_dev->io_device_type); |
| if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) { |
| if (enable == AML_L) |
| ret = aml_set_gpio_out(spi_dev->reset_pin, AML_GPIO_LOW); |
| else |
| ret = aml_set_gpio_out(spi_dev->reset_pin, AML_GPIO_HIGH); |
| } else if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI_T312) { |
| if (spi_dev == NULL || spi_dev->priv == NULL) { |
| pr_dbg("rst by spi-spidev-null-\r\n"); |
| return -1; |
| } |
| aml_ci_rst_by_spi((struct aml_ci *)spi_dev->priv, 0, enable); |
| } else { |
| pr_dbg("aml_gio_power type [%d] enable: %d\r\n", spi_dev->io_device_type, enable); |
| } |
| return ret; |
| } |
| |
| /**\brief aml_gio_init_irq:set gpio irq |
| * \param pc: aml_pcmcia obj,used this priv to get spi_dev obj |
| * \param flag: rising or falling or hi or low |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| /*need change*/ |
| static int aml_gio_init_irq(struct aml_pcmcia *pc, int flag) |
| { |
| struct aml_spi *spi_dev = (struct aml_spi *)pc->priv; |
| |
| #if 0 |
| int cd1_pin = desc_to_gpio(spi_dev->cd_pin1); |
| |
| int irq = pc->irq-AML_CI_GPIO_IRQ_BASE; |
| |
| printk("----cd1_pin=%d irq=%d\r\n", cd1_pin, irq); |
| aml_set_gpio_in(spi_dev->cd_pin1); |
| |
| if (flag == IRQF_TRIGGER_RISING) |
| gpio_for_irq(cd1_pin, |
| AML_GPIO_IRQ(irq, FILTER_NUM7, GPIO_IRQ_RISING)); |
| else if (flag == IRQF_TRIGGER_FALLING) |
| gpio_for_irq(cd1_pin, |
| AML_GPIO_IRQ(irq, FILTER_NUM7, GPIO_IRQ_FALLING)); |
| else if (flag == IRQF_TRIGGER_HIGH) |
| gpio_for_irq(cd1_pin, |
| AML_GPIO_IRQ(irq, FILTER_NUM7, GPIO_IRQ_HIGH)); |
| else if (flag == IRQF_TRIGGER_LOW) |
| gpio_for_irq(cd1_pin, |
| AML_GPIO_IRQ(irq, FILTER_NUM7, GPIO_IRQ_LOW)); |
| else |
| return -1; |
| #endif |
| if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) { |
| gpiod_to_irq(spi_dev->cd_pin1); |
| } else if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI_T312) { |
| gpiod_to_irq(spi_dev->mcu_irq_pin); |
| } else { |
| pr_dbg("aml_gio_init_irq type [%d] \r\n", spi_dev->io_device_type); |
| } |
| return 0; |
| } |
| |
| /**\brief aml_gio_get_cd1:get gpio cd1 pin value |
| * \param pc: aml_pcmcia obj,used this priv to get spi_dev obj |
| * \return |
| * - cd1 pin value |
| * - -EINVAL : error |
| */ |
| static int aml_gio_get_cd1(struct aml_pcmcia *pc) |
| { |
| int ret = 1; |
| struct aml_spi *spi_dev = pc->priv; |
| if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) { |
| ret = aml_get_gpio_value(spi_dev->cd_pin1); |
| } else if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI_T312) { |
| ret = aml_ci_getcd12_by_spi((struct aml_ci *)spi_dev->priv, 0, 0); |
| } else { |
| pr_dbg("aml_gio_get_cd1 not surport type [%d] \r\n", spi_dev->io_device_type); |
| } |
| return ret; |
| } |
| /**\brief aml_gio_get_cd2:get gpio cd2 pin value |
| * \param pc: aml_pcmcia obj,used this priv to get spi_dev obj |
| * \return |
| * - cd2 pin value |
| * - -EINVAL : error |
| */ |
| static int aml_gio_get_cd2(struct aml_pcmcia *pc) |
| { |
| int ret = 0; |
| struct aml_spi *spi_dev = pc->priv; |
| if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) { |
| ret = aml_get_gpio_value(spi_dev->cd_pin2); |
| } else if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI_T312) { |
| ret = aml_ci_getcd12_by_spi((struct aml_ci *)spi_dev->priv, 0, 1); |
| } else { |
| pr_dbg("aml_gio_get_cd2 not surport type [%d] \r\n", spi_dev->io_device_type); |
| } |
| return ret; |
| } |
| /**\brief aml_cam_plugin:notify en50221 cam card in or out |
| * \param pc: aml_pcmcia obj,used this priv to get spi_dev obj |
| * \plugin: 0:remove;1:in |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int aml_cam_plugin(struct aml_pcmcia *pc, int plugin) |
| { |
| struct aml_ci *ci = (struct aml_ci *) |
| ((struct aml_spi *)(pc->priv))->priv; |
| pr_dbg("%s : %d\r\n", __func__, plugin); |
| if (plugin) { |
| dvb_ca_en50221_cimcu_camchange_irq(&ci->en50221_cimcu, |
| 0, DVB_CA_EN50221_CAMCHANGE_INSERTED); |
| } else { |
| dvb_ca_en50221_cimcu_camchange_irq(&ci->en50221_cimcu, |
| 0, DVB_CA_EN50221_CAMCHANGE_REMOVED); |
| } |
| return 0; |
| } |
| /**\brief aml_pcmcia_alloc:alloc nad init pcmcia obj |
| * \param spi_dev: aml_spi obj, |
| * \param pcmcia: aml_pcmcia * obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static void aml_pcmcia_alloc(struct aml_spi *spi_dev, |
| struct aml_pcmcia **pcmcia) |
| { |
| pr_dbg("aml_pcmcia_alloc----\n"); |
| *pcmcia = &spi_dev->pc; |
| (*pcmcia)->irq = spi_dev->irq; |
| (*pcmcia)->init_irq = aml_gio_init_irq; |
| (*pcmcia)->get_cd1 = aml_gio_get_cd1; |
| (*pcmcia)->get_cd2 = aml_gio_get_cd2; |
| (*pcmcia)->pwr = aml_gio_power; |
| (*pcmcia)->rst = aml_gio_reset; |
| (*pcmcia)->pcmcia_plugin = aml_cam_plugin; |
| (*pcmcia)->slot_state = MODULE_XTRACTED; |
| (*pcmcia)->priv = spi_dev; |
| (*pcmcia)->run_type = 0;/*0:irq;1:poll*/ |
| (*pcmcia)->io_device_type = AML_DVB_IO_TYPE_CIMAX; |
| } |
| |
| /**\brief aml_spi_get_config_from_dts:get spi config and gpio config from dts |
| * \param spi_dev: aml_spi obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int aml_spi_get_config_from_dts(struct aml_spi *spi_dev) |
| { |
| struct device_node *child = NULL; |
| struct platform_device *pdev = spi_dev->pdev; |
| struct device_node *np = pdev->dev.of_node; |
| unsigned int temp[5], val; |
| int ret = 0; |
| pr_dbg("into get spi dts \r\n"); |
| |
| /*get spi and gpio config from dts*/ |
| /* get device config for dvbci_io*/ |
| child = of_get_child_by_name(np, "dvbci_io"); |
| if (child == NULL) { |
| pr_error("failed to get dvbci_io\n"); |
| return -1; |
| } |
| spi_dev->spi_bdinfo = &aml_ci_spi_bdinfo; |
| /* get spi config */ |
| ret = of_property_read_u32_array(child, "spi_bus_num", temp, 1); |
| if (ret) { |
| pr_error("failed to get spi_bus_num\n"); |
| } else { |
| aml_ci_spi_bdinfo.bus_num = temp[0]; |
| pr_dbg("bus_num: %d\n", aml_ci_spi_bdinfo.bus_num); |
| } |
| ret = of_property_read_u32_array(child, "spi_chip_select", |
| temp, 1); |
| if (ret) { |
| pr_error("failed to get spi_chip_select\n"); |
| } else { |
| aml_ci_spi_bdinfo.chip_select = temp[0]; |
| pr_dbg("chip_select: %d\n", aml_ci_spi_bdinfo.chip_select); |
| } |
| ret = of_property_read_u32_array(child, "spi_max_frequency", |
| temp, 1); |
| if (ret) { |
| pr_error("failed to get spi_chip_select\n"); |
| } else { |
| aml_ci_spi_bdinfo.max_speed_hz = temp[0]; |
| pr_dbg("max_speed_hz: %d\n", aml_ci_spi_bdinfo.max_speed_hz); |
| } |
| ret = of_property_read_u32_array(child, "spi_mode", temp, 1); |
| if (ret) { |
| pr_error("failed to get spi_mode\n"); |
| } else { |
| aml_ci_spi_bdinfo.mode = temp[0]; |
| pr_dbg("mode: %d\n", aml_ci_spi_bdinfo.mode); |
| } |
| ret = of_property_read_u32_array(child, "spi_cs_delay", |
| &temp[0], 2); |
| if (ret) { |
| spi_dev->cs_hold_delay = 0; |
| spi_dev->cs_clk_delay = 0; |
| } else { |
| spi_dev->cs_hold_delay = temp[0]; |
| spi_dev->cs_clk_delay = temp[1]; |
| } |
| ret = of_property_read_u32(child, "spi_write_check", &val); |
| if (ret) |
| spi_dev->write_check = 0; |
| else |
| spi_dev->write_check = (unsigned char)val; |
| |
| //below is get cd1 cd2 pwr irq reset gpio info |
| if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI) { |
| /*get cd1 irq num*/ |
| ret = of_property_read_u32(child, "irq_cd1", &val); |
| if (ret) { |
| spi_dev->irq = 5; |
| } else { |
| /*set irq value need add |
| AML_CI_GPIO_IRQ_BASE,but |
| we need minus |
| AML_CI_GPIO_IRQ_BASE |
| when gpio request irq */ |
| spi_dev->irq = val+AML_CI_GPIO_IRQ_BASE; |
| } |
| |
| spi_dev->irq = irq_of_parse_and_map( |
| pdev->dev.of_node, 0); |
| AML_CI_GPIO_IRQ_BASE = spi_dev->irq - val; |
| pr_dbg("get spi irq : %d 0:%d USEDBASE:%d val:%d\r\n", |
| spi_dev->irq, INT_GPIO_0, AML_CI_GPIO_IRQ_BASE, val); |
| /*get reset pwd cd1 cd2 gpio pin*/ |
| spi_dev->reset_pin = NULL; |
| ret = spi_get_gpio_by_name(spi_dev, &spi_dev->reset_pin, |
| &spi_dev->reset_pin_value, "reset_pin", |
| OUTPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci reset pin request failed\n"); |
| return -1; |
| } |
| spi_dev->cd_pin1 = NULL; |
| ret = spi_get_gpio_by_name(spi_dev, |
| &spi_dev->cd_pin1, |
| &spi_dev->cd_pin1_value, "cd_pin1", |
| INPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci cd_pin1 pin request failed\n"); |
| return -1; |
| } |
| spi_dev->cd_pin2 = spi_dev->cd_pin1; |
| spi_dev->cd_pin2_value = spi_dev->cd_pin1_value; |
| spi_dev->pwr_pin = NULL; |
| pr_dbg("spi_dev->cd_pin1_value==%d\r\n", spi_dev->cd_pin1_value); |
| ret = spi_get_gpio_by_name(spi_dev, |
| &spi_dev->pwr_pin, &spi_dev->pwr_pin_value, |
| "pwr_pin", OUTPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci pwr_pin pin request failed\n"); |
| return -1; |
| } |
| spi_dev->irq_cam_pin = NULL; |
| ret = spi_get_gpio_by_name(spi_dev, |
| &spi_dev->irq_cam_pin, &spi_dev->irq_cam_pin_value, |
| "irq_cam_pin", INPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvbci irq_cam_pin pin request failed\n"); |
| return -1; |
| } |
| } else if (spi_dev->io_device_type == AML_DVB_IO_TYPE_SPI_T312) { |
| //get mcu irq gpio |
| spi_dev->mcu_irq_pin = NULL; |
| ret = spi_get_gpio_by_name(spi_dev, |
| &spi_dev->mcu_irq_pin, |
| &spi_dev->mcu_irq_pin_value, "mcu_irq_pin", |
| INPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci mcu_irq_pin pin request failed\n"); |
| return -1; |
| } |
| spi_dev->irq = gpiod_to_irq(spi_dev->mcu_irq_pin) ; |
| } else { |
| pr_error("dvbci io device type error [%d]\n", spi_dev->io_device_type); |
| } |
| return 0; |
| } |
| /**\brief aml_ci_free_gpio:free ci gpio |
| * \param spi_dev: aml_spi obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static void aml_ci_free_gpio(struct aml_spi *spi_dev) |
| { |
| if (spi_dev == NULL) { |
| pr_error("spi_dev is NULL,no need free gpio res\r\n"); |
| return; |
| } |
| |
| if (spi_dev->pwr_pin) { |
| aml_gpio_free(spi_dev->pwr_pin); |
| spi_dev->pwr_pin = NULL; |
| } |
| if (spi_dev->cd_pin1) { |
| aml_gpio_free(spi_dev->cd_pin1); |
| spi_dev->cd_pin1 = NULL; |
| spi_dev->cd_pin2 = NULL; |
| } |
| if (spi_dev->reset_pin) { |
| aml_gpio_free(spi_dev->reset_pin); |
| spi_dev->reset_pin = NULL; |
| } |
| if (spi_dev->irq_cam_pin) { |
| aml_gpio_free(spi_dev->irq_cam_pin); |
| spi_dev->irq_cam_pin = NULL; |
| } |
| return; |
| } |
| |
| |
| /**\brief ci_spi_dev_remove:spi probe api |
| * \param spi: spi obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int ci_spi_dev_probe(struct spi_device *spi) |
| { |
| int ret; |
| pr_dbg("spi Dev probe--\r\n"); |
| spin_lock(&(g_spi_dev->spi_lock)); |
| if (g_spi_dev) |
| g_spi_dev->spi = spi; |
| else |
| pr_dbg("spi Dev probe-error-\n"); |
| spi->bits_per_word = 8; |
| ret = spi_setup(spi); |
| if (ret) |
| pr_dbg("spi setup failed\n"); |
| spin_unlock(&(g_spi_dev->spi_lock)); |
| return ret; |
| } |
| /**\brief ci_spi_dev_remove:spi remove api |
| * \param spi: spi obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int ci_spi_dev_remove(struct spi_device *spi) |
| { |
| pr_dbg("spi Dev remove--\n"); |
| if (g_spi_dev) |
| g_spi_dev->spi = NULL; |
| |
| return 0; |
| } |
| |
| static struct spi_driver ci_spi_dev_driver = { |
| .probe = ci_spi_dev_probe, |
| .remove = ci_spi_dev_remove, |
| .driver = { |
| .name = "ci_spi_dev",/*set same with board info modalias*/ |
| .owner = THIS_MODULE, |
| }, |
| }; |
| /**\brief aml_spi_init:spi_dev init |
| * \param ci_dev: aml_ci obj, |
| * \param pdev: platform_device obj,used to get dts info |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| int aml_spi_init(struct platform_device *pdev, struct aml_ci *ci_dev) |
| { |
| struct aml_spi *spi_dev = NULL; |
| struct aml_pcmcia *pc; |
| int result; |
| |
| spi_dev = kmalloc(sizeof(struct aml_spi), GFP_KERNEL); |
| if (!spi_dev) { |
| pr_error("Out of memory!, exiting ..\n"); |
| result = -ENOMEM; |
| goto err; |
| } |
| g_spi_dev = spi_dev; |
| spi_dev->pdev = pdev; |
| spi_dev->priv = ci_dev; |
| spi_dev->spi = NULL; |
| /*init io device type*/ |
| spi_dev->io_device_type = ci_dev->io_type; |
| pr_dbg("*********spi Dev type [%d]\n", ci_dev->io_type); |
| /*get config from dts*/ |
| aml_spi_get_config_from_dts(spi_dev); |
| |
| /*init spi_lock*/ |
| spin_lock_init(&(spi_dev->spi_lock)); |
| /*regist api dev*/ |
| pr_dbg("*********spi Dev regist**********\r\n"); |
| result = dirspi_register_board_info(spi_dev->spi_bdinfo, 1); |
| if (result) { |
| pr_error("register amlspi_dev spi boardinfo failed\n"); |
| goto fail1; |
| } |
| result = spi_register_driver(&ci_spi_dev_driver); |
| if (result) { |
| pr_error("register amlspi_dev spi driver failed\n"); |
| goto fail1; |
| } |
| |
| /*init ci_dev used api.*/ |
| ci_dev->ci_mem_read = aml_ci_mem_read_by_spi; |
| ci_dev->ci_mem_write = aml_ci_mem_write_by_spi; |
| ci_dev->ci_io_read = aml_ci_io_read_by_spi; |
| ci_dev->ci_io_write = aml_ci_io_write_by_spi; |
| ci_dev->ci_slot_reset = aml_ci_slot_reset; |
| ci_dev->ci_slot_shutdown = aml_ci_slot_shutdown; |
| ci_dev->ci_slot_ts_enable = aml_ci_ts_control; |
| ci_dev->ci_poll_slot_status = aml_ci_slot_status; |
| ci_dev->data = spi_dev; |
| |
| aml_pcmcia_alloc(spi_dev, &pc); |
| pc->io_device_type = spi_dev->io_device_type; |
| result = aml_pcmcia_init(pc); |
| if (result < 0) { |
| pr_error("aml_pcmcia_init failed\n"); |
| goto fail2; |
| } |
| return 0; |
| fail2: |
| spi_unregister_driver(&ci_spi_dev_driver); |
| fail1: |
| kfree(spi_dev); |
| spi_dev = NULL; |
| err: |
| return -1; |
| } |
| EXPORT_SYMBOL(aml_spi_init); |
| /**\brief aml_spi_exit:spi exit |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| int aml_spi_exit(struct aml_ci *ci) |
| { |
| /*exit pc card*/ |
| aml_pcmcia_exit(&g_spi_dev->pc); |
| /*un regist spi driver*/ |
| spi_unregister_driver(&ci_spi_dev_driver); |
| /*free gpio*/ |
| aml_ci_free_gpio(g_spi_dev); |
| /*free spi dev*/ |
| kfree(g_spi_dev); |
| g_spi_dev = NULL; |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(aml_spi_exit); |
| |
| |
| #if 1 |
| /********************************************************/ |
| /********************************************************/ |
| /******* for spi test api *************/ |
| /********************************************************/ |
| /********************************************************/ |
| |
| /*cam difines*/ |
| #define DA 0x80 |
| #define FR 0x40 |
| #define WE 0x02 |
| #define RE 0x01 |
| |
| #define RS 0x08 |
| #define SR 0x04 |
| #define SW 0x02 |
| #define HC 0x01 |
| #define DATA_REG 0 |
| #define COM_STA_REG 1 |
| #define SIZE_REG_L 2 |
| #define SIZE_REG_M 3 |
| static void aml_spi_ca_full_test(struct aml_ci *ci_dev) |
| { |
| unsigned int BUF_SIZE = 0; |
| unsigned int i = 0; |
| unsigned char cc = 0; |
| unsigned char reg; |
| unsigned int bsize = 0; |
| int cnt = 0; |
| unsigned char buf[10]; |
| int count = 1000; |
| mdelay(1000); |
| pr_dbg("READ CIS START\r\n"); |
| for (i = 0; i < 267; i++) { |
| mdelay(100); |
| cc = aml_ci_mem_read_by_spi(ci_dev, 0, i); |
| pr_dbg("0x%x ", cc); |
| if ((i + 1) % 16 == 0) |
| pr_dbg(" \r\n"); |
| } |
| pr_dbg("READ CIS OVER\r\n"); |
| mdelay(1000); |
| pr_dbg("SW rst CAM...\r\n"); |
| aml_ci_io_write_by_spi(ci_dev, 0, COM_STA_REG, RS); |
| pr_dbg("SW rst over.\r\n"); |
| pr_dbg("-----------------------------------\r\n"); |
| pr_dbg("TO delay 2000ms\r\n"); |
| mdelay(2000); |
| pr_dbg("\r\n"); |
| pr_dbg("--------------clear rs--!!!-YOU MUST CLEAR RS BIT--no sleep--------\r\n"); |
| aml_ci_io_write_by_spi(ci_dev, 0, COM_STA_REG, 0); |
| pr_dbg("--------------sleep---------------------\r\n"); |
| mdelay(2000); |
| pr_dbg("TO check sw-rst is OK\r\n"); |
| pr_dbg("start read fr \r\n"); |
| if (1) { |
| unsigned char reg; |
| unsigned char reg1; |
| int count1 = 4000; |
| while (1) { |
| mdelay(20); |
| count1--; |
| reg1 = aml_ci_io_read_by_spi( |
| ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg1)) { |
| continue; |
| } else { |
| pr_dbg("CAM Reset Ok\r\n"); |
| break; |
| } |
| } |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| pr_dbg("STA_REG = 0x%2.2x\r\n", reg); |
| if (FR & reg) { |
| pr_dbg("SW-RST is OK!\r\n"); |
| } else { |
| pr_dbg("SW-RST is ERR!\r\n"); |
| goto end; |
| } |
| } |
| end: |
| pr_dbg("TO check sw-rst over.\r\n"); |
| pr_dbg("\r\n"); |
| pr_dbg("-----------------------------------\r\n"); |
| pr_dbg("TO buffer size negotiation protocol...\r\n"); |
| pr_dbg("Get which buf size CAM can support\r\n"); |
| aml_ci_io_write_by_spi(ci_dev, 0, COM_STA_REG, SR); |
| mdelay(1000); |
| while (1) { |
| |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if ((reg & DA) == DA) { |
| pr_dbg("Buffer negotiate size date avalible.\r\n"); |
| break; |
| } else { |
| /*pr_dbg("Buffer negotiate |
| size date NOT avalible\r\n");*/ |
| continue; |
| } |
| mdelay(100); |
| } |
| cnt = (aml_ci_io_read_by_spi(ci_dev, 0, SIZE_REG_L)) + |
| ((aml_ci_io_read_by_spi(ci_dev, 0, SIZE_REG_M)) * 256); |
| pr_dbg("Moudle have <%d> Bytes send to host.\r\n", cnt); |
| if (cnt != 2) { |
| pr_dbg("The Bytes will be tx is ERR!\r\n"); |
| return; |
| } |
| for (i = 0; i < cnt; i++) |
| buf[i] = aml_ci_io_read_by_spi(ci_dev, 0, DATA_REG); |
| |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (RE == (RE & reg)) { |
| pr_dbg("(1)Read CAM buf size ERR!\r\n"); |
| return; |
| } |
| aml_ci_io_write_by_spi(ci_dev, 0, (COM_STA_REG), 0); |
| |
| mdelay(1000); |
| |
| while (count--) { |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) { |
| pr_dbg("CAM is busy 2, waiting...\r\n"); |
| continue; |
| } else { |
| pr_dbg("CAM is OK 2.\r\n"); |
| break; |
| } |
| } |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) { |
| pr_dbg("(2)Read CAM buf size ERR!-\r\n"); |
| return; |
| } |
| bsize = (buf[0] * 256) + buf[1]; |
| |
| pr_dbg("CAM can support buf size is: <%d>B\r\n", bsize); |
| |
| pr_dbg("Tell CAM which size buf is be used\r\n"); |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) |
| pr_dbg("CAM is busy, waiting free\r\n"); |
| while (1) { |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) { |
| pr_dbg("CAM is busy 3, waiting\r\n"); |
| continue; |
| } else { |
| pr_dbg("CAM is OK 3\r\n"); |
| break; |
| } |
| } |
| |
| bsize = bsize - 0; |
| BUF_SIZE = bsize; |
| pr_dbg("We will use this buf size: <%d>B\r\n", bsize); |
| aml_ci_io_write_by_spi(ci_dev, 0, COM_STA_REG, SW); |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) |
| pr_dbg("CAM is busy, waiting\r\n"); |
| |
| while (1) { |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) { |
| pr_dbg("CAM is busy 4, waiting\r\n"); |
| continue; |
| } else { |
| pr_dbg("CAM is OK 4\r\n"); |
| break; |
| } |
| } |
| /*SHOULD CHECK DA!!!!!*/ |
| /*PLS ADD THIS CHECK CODE:*/ |
| pr_dbg("PRIOR to check CAM'S DA\r\n"); |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if ((reg & DA) == DA) { |
| pr_dbg("CAM have data send to HOST\r\n"); |
| return; |
| } |
| |
| |
| buf[0] = (unsigned char)((bsize >> 8) & 0xff); |
| buf[1] = (unsigned char)(bsize & 0xff); |
| |
| while (1) { |
| mdelay(10); |
| aml_ci_io_write_by_spi(ci_dev, |
| 0, COM_STA_REG, HC | SW); |
| mdelay(100); |
| reg = aml_ci_io_read_by_spi(ci_dev, |
| 0, COM_STA_REG); |
| if (FR != (FR & reg)) { |
| pr_dbg("CAM is busy 5, waiting\r\n"); |
| aml_ci_io_write_by_spi(ci_dev, |
| 0, COM_STA_REG, SW); |
| continue; |
| } else { |
| pr_dbg("CAM is OK 5\r\n"); |
| break; |
| } |
| } |
| pr_dbg("<2> Bytes send to CAM\r\n"); |
| aml_ci_io_write_by_spi(ci_dev, 0, SIZE_REG_M, 0); |
| aml_ci_io_write_by_spi(ci_dev, 0, SIZE_REG_L, 2); |
| for (i = 0; i < 2; i++) |
| aml_ci_io_write_by_spi(ci_dev, 0, DATA_REG, buf[i]); |
| |
| reg = aml_ci_io_read_by_spi(ci_dev, 0, COM_STA_REG); |
| if (WE == (WE & reg)) { |
| pr_dbg("Write CAM ERR!\r\n"); |
| return; |
| } else { |
| aml_ci_io_write_by_spi(ci_dev, 0, COM_STA_REG, SW); |
| mdelay(100); |
| aml_ci_io_write_by_spi(ci_dev, 0, COM_STA_REG, 0); |
| pr_dbg("Buffer size negotiation over!\r\n"); |
| pr_dbg("NOW, HOST can communicates with CAM\r\n"); |
| pr_dbg("NOW, TEST END\r\n"); |
| } |
| } |
| |
| /** |
| * Read a tuple from attribute memory. |
| * |
| * @param ca CA instance. |
| * @param slot Slot id. |
| * @param address Address to read from. Updated. |
| * @param tupleType Tuple id byte. Updated. |
| * @param tupleLength Tuple length. Updated. |
| * @param tuple Dest buffer for tuple (must be 256 bytes). Updated. |
| * |
| * @return 0 on success, nonzero on error. |
| */ |
| static int dvb_ca_en50221_read_tuple( |
| int *address, int *tupleType, int *tupleLength, u8 *tuple) |
| { |
| int i; |
| int _tupleType; |
| int _tupleLength; |
| int _address = *address; |
| |
| /* grab the next tuple length and type */ |
| _tupleType = aml_ci_mem_read_by_spi((struct aml_ci *) |
| g_spi_dev->priv, 0, _address); |
| if (_tupleType < 0) |
| return _tupleType; |
| if (_tupleType == 0xff) { |
| pr_dbg("END OF CHAIN TUPLE type:0x%x\n", _tupleType); |
| *address += 2; |
| *tupleType = _tupleType; |
| *tupleLength = 0; |
| return 0; |
| } |
| _tupleLength = aml_ci_mem_read_by_spi((struct aml_ci *) |
| g_spi_dev->priv, 0, _address + 2); |
| if (_tupleLength < 0) |
| return _tupleLength; |
| _address += 4; |
| |
| pr_dbg("TUPLE type:0x%x length:%i\n", _tupleType, _tupleLength); |
| |
| /* read in the whole tuple */ |
| for (i = 0; i < _tupleLength; i++) { |
| tuple[i] = aml_ci_mem_read_by_spi((struct aml_ci *) |
| g_spi_dev->priv, 0, _address + (i * 2)); |
| pr_dbg(" 0x%02x: 0x%02x %c\n", |
| i, tuple[i] & 0xff, |
| ((tuple[i] > 31) && (tuple[i] < 127)) ? tuple[i] : '.'); |
| } |
| _address += (_tupleLength * 2); |
| |
| /* success */ |
| *tupleType = _tupleType; |
| *tupleLength = _tupleLength; |
| *address = _address; |
| return 0; |
| } |
| static char *findstr(char *haystack, int hlen, char *needle, int nlen) |
| { |
| int i; |
| |
| if (hlen < nlen) |
| return NULL; |
| |
| for (i = 0; i <= hlen - nlen; i++) { |
| if (!strncmp(haystack + i, needle, nlen)) |
| return haystack + i; |
| } |
| |
| return NULL; |
| } |
| |
| /** |
| * Parse attribute memory of a CAM module, extracting Config register, and checking |
| * it is a DVB CAM module. |
| * |
| * @param ca CA instance. |
| * @param slot Slot id. |
| * |
| * @return 0 on success, <0 on failure. |
| */ |
| static int dvb_ca_en50221_parse_attributes(void) |
| { |
| int address = 0; |
| int tupleLength; |
| int tupleType; |
| u8 tuple[257]; |
| char *dvb_str; |
| int rasz; |
| int status; |
| int got_cftableentry = 0; |
| int end_chain = 0; |
| int i; |
| u16 manfid = 0; |
| u16 devid = 0; |
| int config_base = 0; |
| int config_option; |
| |
| /* CISTPL_DEVICE_0A */ |
| status = dvb_ca_en50221_read_tuple(&address, |
| &tupleType, &tupleLength, tuple); |
| if (status < 0) { |
| pr_error("read status error\r\n"); |
| return status; |
| } |
| if (tupleType != 0x1D) { |
| pr_error("read tupleType error [0x%x]\r\n", tupleType); |
| return -EINVAL; |
| } |
| |
| |
| |
| /* CISTPL_DEVICE_0C */ |
| status = dvb_ca_en50221_read_tuple(&address, |
| &tupleType, &tupleLength, tuple); |
| if (status < 0) { |
| pr_error("read read cis error\r\n"); |
| return status; |
| } |
| if (tupleType != 0x1C) { |
| pr_error("read read cis type error\r\n"); |
| return -EINVAL; |
| } |
| |
| |
| |
| /* CISTPL_VERS_1 */ |
| status = dvb_ca_en50221_read_tuple(&address, |
| &tupleType, &tupleLength, tuple); |
| if (status < 0) { |
| pr_error("read read cis version error\r\n"); |
| return status; |
| } |
| if (tupleType != 0x15) { |
| pr_error("read read cis version type error\r\n"); |
| return -EINVAL; |
| } |
| |
| |
| |
| /* CISTPL_MANFID */ |
| status = dvb_ca_en50221_read_tuple(&address, &tupleType, |
| &tupleLength, tuple); |
| if (status < 0) { |
| pr_error("read read cis manfid error\r\n"); |
| return status; |
| } |
| if (tupleType != 0x20) { |
| pr_error("read read cis manfid type error\r\n"); |
| return -EINVAL; |
| } |
| if (tupleLength != 4) { |
| pr_error("read read cis manfid len error\r\n"); |
| return -EINVAL; |
| } |
| manfid = (tuple[1] << 8) | tuple[0]; |
| devid = (tuple[3] << 8) | tuple[2]; |
| |
| |
| |
| /* CISTPL_CONFIG */ |
| status = dvb_ca_en50221_read_tuple(&address, &tupleType, |
| &tupleLength, tuple); |
| if (status < 0) { |
| pr_error("read read cis config error\r\n"); |
| return status; |
| } |
| if (tupleType != 0x1A) { |
| pr_error("read read cis config type error\r\n"); |
| return -EINVAL; |
| } |
| if (tupleLength < 3) { |
| pr_error("read read cis config len error\r\n"); |
| return -EINVAL; |
| } |
| |
| /* extract the configbase */ |
| rasz = tuple[0] & 3; |
| if (tupleLength < (3 + rasz + 14)) { |
| pr_error("read extract the configbase error\r\n"); |
| return -EINVAL; |
| } |
| |
| for (i = 0; i < rasz + 1; i++) |
| config_base |= (tuple[2 + i] << (8 * i)); |
| |
| |
| /* check it contains the correct DVB string */ |
| dvb_str = findstr((char *)tuple, tupleLength, "DVB_CI_V", 8); |
| if (dvb_str == NULL) { |
| pr_error("find dvb str DVB_CI_V error\r\n"); |
| return -EINVAL; |
| } |
| if (tupleLength < ((dvb_str - (char *) tuple) + 12)) { |
| pr_error("find dvb str DVB_CI_V len error\r\n"); |
| return -EINVAL; |
| } |
| |
| /* is it a version we support? */ |
| if (strncmp(dvb_str + 8, "1.00", 4)) { |
| pr_error(" Unsupported DVB CAM module version %c%c%c%c\n", |
| dvb_str[8], dvb_str[9], dvb_str[10], dvb_str[11]); |
| return -EINVAL; |
| } |
| |
| /* process the CFTABLE_ENTRY tuples, and any after those */ |
| while ((!end_chain) && (address < 0x1000)) { |
| status = dvb_ca_en50221_read_tuple(&address, &tupleType, |
| &tupleLength, tuple); |
| if (status < 0) { |
| pr_error("process the CFTABLE_ENTRY tuples error\r\n"); |
| return status; |
| } |
| |
| switch (tupleType) { |
| case 0x1B: /* CISTPL_CFTABLE_ENTRY */ |
| if (tupleLength < (2 + 11 + 17)) |
| break; |
| |
| /* if we've already parsed one, just use it */ |
| if (got_cftableentry) |
| break; |
| |
| /* get the config option */ |
| config_option = tuple[0] & 0x3f; |
| |
| /* OK, check it contains the correct strings */ |
| if ((findstr((char *)tuple, |
| tupleLength, "DVB_HOST", 8) == NULL) || |
| (findstr((char *)tuple, |
| tupleLength, "DVB_CI_MODULE", 13) == NULL)) |
| break; |
| |
| |
| got_cftableentry = 1; |
| break; |
| |
| case 0x14: /* CISTPL_NO_LINK*/ |
| break; |
| |
| case 0xFF: /* CISTPL_END */ |
| end_chain = 1; |
| break; |
| |
| default: |
| /* Unknown tuple type - just skip |
| *this tuple and move to the next one |
| */ |
| pr_error("Skipping unknown tupletype:0x%x L:0x%x\n", |
| tupleType, tupleLength); |
| break; |
| } |
| } |
| |
| if ((address > 0x1000) || (!got_cftableentry)) { |
| pr_error("got_cftableentry :%d\r\n", got_cftableentry); |
| return -EINVAL; |
| } |
| |
| pr_error("----------ci cis ok-----\r\n"); |
| return 0; |
| } |
| |
| static ssize_t aml_spi_ci_reset_help(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| ret = sprintf(buf, "echo 1 > %s\n\t", attr->attr.name); |
| return ret; |
| } |
| |
| static ssize_t aml_spi_ci_reset(struct class *class, |
| struct class_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| struct aml_ci *ci = (struct aml_ci *)g_spi_dev->priv; |
| ret = aml_ci_slot_reset(ci, 0); |
| return size; |
| } |
| |
| static ssize_t aml_spi_ci_pwr_help(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| ret = sprintf(buf, "echo 1|0> %s\n\t", attr->attr.name); |
| return ret; |
| } |
| |
| static ssize_t aml_spi_ci_pwr(struct class *class, |
| struct class_attribute *attr, const char *buf, size_t size) |
| { |
| int ret = 0; |
| int enable = 0; |
| long value; |
| if (kstrtol(buf, 0, &value) == 0) |
| enable = (int)value; |
| ret = aml_gio_power(&g_spi_dev->pc, enable); |
| return size; |
| } |
| static ssize_t aml_spi_ci_state_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| struct aml_ci *ci = (struct aml_ci *)g_spi_dev->priv; |
| ret = aml_ci_slot_status(ci, 0, 0); |
| ret = sprintf(buf, "%s: %d;\n\t", attr->attr.name, ret); |
| return ret; |
| } |
| |
| static ssize_t aml_spi_ci_irq_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| ret = aml_ci_gio_get_irq(); |
| ret = sprintf(buf, "%s irq: %d\n\t", attr->attr.name, ret); |
| return ret; |
| } |
| |
| static ssize_t aml_spi_io_test_help(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| ret = sprintf(buf, "echo (r|w|f|c)(i|a) addr data > %s\n", |
| attr->attr.name); |
| return ret; |
| } |
| |
| static ssize_t aml_spi_io_test(struct class *class, |
| struct class_attribute *attr, const char *buf, size_t size) |
| { |
| int n = 0; |
| int i = 0; |
| char *buf_orig, *ps, *token; |
| char *parm[3]; |
| unsigned int addr = 0, val = 0, retval = 0; |
| long value = 0; |
| struct aml_ci *ci = (struct aml_ci *)g_spi_dev->priv; |
| buf_orig = kstrdup(buf, GFP_KERNEL); |
| ps = buf_orig; |
| while (1) { |
| /*need set '\n' to ' \n'*/ |
| token = strsep(&ps, " "); |
| if (token == NULL) |
| break; |
| if (*token == '\0') |
| continue; |
| parm[n++] = token; |
| } |
| |
| if (!n || ((n > 0) && (strlen(parm[0]) != 2))) { |
| pr_err("invalid command n[%x]p[%x][%s]\n", n,(int)strlen(parm[0]),parm[0]); |
| kfree(buf_orig); |
| return size; |
| } |
| |
| if ((parm[0][0] == 'r')) { |
| if (n > 2) { |
| pr_err("read: invalid parameter\n"); |
| kfree(buf_orig); |
| return size; |
| } |
| if (kstrtol(parm[1], 0, &value) == 0) |
| addr = (int)value; |
| pr_err("%s 0x%x\n", parm[0], addr); |
| switch ((char)parm[0][1]) { |
| case 'i': |
| for (i = 0; i < 1000; i++) |
| retval = aml_ci_io_read_by_spi(ci, 0, addr); |
| break; |
| case 'a': |
| for (i = 0; i < 1000; i++) |
| retval = aml_ci_mem_read_by_spi(ci, 0, addr); |
| break; |
| default: |
| break; |
| } |
| pr_dbg("%s: 0x%x --> 0x%x\n", parm[0], addr, retval); |
| } else if ((parm[0][0] == 'w')) { |
| if (n != 3) { |
| pr_err("write: invalid parameter\n"); |
| kfree(buf_orig); |
| return size; |
| } |
| if (kstrtol(parm[1], 0, &value) == 0) |
| addr = (int)value; |
| if (kstrtol(parm[2], 0, &value) == 0) |
| val = (int)value; |
| |
| pr_err("%s 0x%x 0x%x", parm[0], addr, val); |
| /*switch ((char)parm[0][1]) { |
| case 'i': |
| retval = aml_ci_io_write_by_spi(ci, 0, addr, val); |
| break; |
| case 'a': |
| retval = aml_ci_mem_write_by_spi(ci, 0, addr, val); |
| break; |
| default: |
| break; |
| }*/ |
| pr_dbg("%s: 0x%x <-- 0x%x\n", parm[0], addr, retval); |
| } else if ((parm[0][0] == 'f')) { |
| pr_dbg("full test----\r\n"); |
| aml_spi_ca_full_test(ci); |
| } else if ((parm[0][0] == 'c')) { |
| pr_dbg("cis test----\r\n"); |
| aml_ci_full_test_by_spi(ci, 0, addr); |
| } else if ((parm[0][0] == 'p')) { |
| pr_dbg("cis dvb_ca_en50221_parse_attributes----\r\n"); |
| dvb_ca_en50221_parse_attributes(); |
| } |
| |
| kfree(buf_orig); |
| return size; |
| } |
| |
| static struct class_attribute aml_spi_class_attrs[] = { |
| __ATTR(reset, S_IRUGO | S_IWUSR, |
| aml_spi_ci_reset_help, aml_spi_ci_reset), |
| __ATTR(pwr, S_IRUGO | S_IWUSR, |
| aml_spi_ci_pwr_help, aml_spi_ci_pwr), |
| __ATTR(irq, S_IRUGO | S_IWUSR, |
| aml_spi_ci_irq_show, NULL), |
| __ATTR(status, S_IRUGO | S_IWUSR, |
| aml_spi_ci_state_show, NULL), |
| __ATTR(iotest, S_IRUGO | S_IWUSR, |
| aml_spi_io_test_help, aml_spi_io_test), |
| __ATTR_NULL |
| }; |
| |
| static struct class aml_spi_class = { |
| .name = "aml_dvb_spi_test", |
| .class_attrs = aml_spi_class_attrs, |
| }; |
| |
| |
| /**\brief aml_con_gpio_by_spi:control gpio by spi |
| * \param gpio: the value is from AM_CON_GPIO def |
| * \param level: 0: set low,1:set hight |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| int aml_con_gpio_by_spi(int gpio, int level) |
| { |
| u8 data = gpio; |
| u16 addres = level; |
| int value = 0; |
| struct aml_spi *spi_dev = g_spi_dev; |
| /*add by chl,need add time delay*/ |
| mdelay(10); |
| aml_init_send_buf(AM_CI_CMD_CONGPIO, data, addres); |
| value = aml_spi_io_api(spi_dev, sendbuf, SENDBUFLEN, AM_CI_CMD_CONGPIO); |
| return value; |
| } |
| EXPORT_SYMBOL(aml_con_gpio_by_spi); |
| |
| int aml_spi_mod_init(void) |
| { |
| int ret; |
| pr_dbg("Amlogic DVB SPI Init\n"); |
| ret = class_register(&aml_spi_class); |
| return 0; |
| } |
| //EXPORT_SYMBOL(aml_spi_mod_init); |
| void aml_spi_mod_exit(void) |
| { |
| pr_dbg("Amlogic DVB SPI Exit\n"); |
| class_unregister(&aml_spi_class); |
| } |
| EXPORT_SYMBOL(aml_spi_mod_exit); |
| #endif |
| #if 0 |
| module_init(aml_spi_mod_init); |
| module_exit(aml_spi_mod_exit); |
| |
| MODULE_LICENSE("GPL"); |
| #endif |