| /* |
| * 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/amlogic/aml_gpio_consumer.h> |
| #include <linux/gpio/consumer.h> |
| #include <linux/device.h> |
| #include <linux/slab.h> |
| #include <linux/of_irq.h> |
| #include <linux/irq.h> |
| #include <linux/compat.h> |
| #include "aml_ci_bus.h" |
| #include "aml_ci.h" |
| |
| |
| |
| //can see jtag dts and driver to select gpio function. |
| //write dts config for cam/tsin/out |
| //gpio irq is can used. |
| // |
| static struct aml_ci_bus ci_bus; |
| static int aml_ci_bus_debug = 1; |
| static int aml_ci_bus_time = 500; |
| static int aml_ci_bus_set_delay = 0; |
| |
| static DECLARE_WAIT_QUEUE_HEAD(wq); |
| static u32 fetch_done; |
| |
| module_param_named(ci_bus_debug, aml_ci_bus_debug, int, 0644); |
| MODULE_PARM_DESC(ci_bus_debug, "enable verbose debug messages"); |
| |
| module_param_named(ci_bus_set_delay, aml_ci_bus_set_delay, int, 0644); |
| MODULE_PARM_DESC(ci_bus_set_delay, "enable ci bus delay set"); |
| |
| module_param_named(ci_bus_time, aml_ci_bus_time, int, 0644); |
| MODULE_PARM_DESC(ci_bus_time, "set ci bus time"); |
| |
| |
| #define pr_dbg(args...)\ |
| do {\ |
| if (aml_ci_bus_debug)\ |
| printk(args);\ |
| } while (0) |
| #define pr_error(fmt, args...) printk("AML_CI_BUS: " fmt, ## args) |
| |
| |
| #define INPUT 0 |
| #define OUTPUT 1 |
| #define OUTLEVEL_LOW 0 |
| #define OUTLEVEL_HIGH 1 |
| #define PULLLOW 1 |
| #define PULLHIGH 0 |
| |
| #define AML_MODE_NAME "aml_dvbci_bus" |
| |
| int aml_ci_bus_mod_init(void); |
| void aml_ci_bus_mod_exit(void); |
| static int aml_read_self(unsigned int reg); |
| static void aml_write_self(unsigned int reg, unsigned int val); |
| static int aml_set_gpio_out(struct gpio_desc *gpio, int val); |
| static int aml_set_gpio_in(struct gpio_desc *gpio); |
| |
| #define WRITE_CIBUS_REG(_r, _v) aml_write_self(_r, _v) |
| #define READ_CIBUS_REG(_r) aml_read_self(_r) |
| |
| #define USED_IRQ 0 |
| |
| static void *p_hw_base; |
| //write reg |
| static void aml_write_self(unsigned int reg, unsigned int val) |
| { |
| void *ptr = (void *)(p_hw_base + reg); |
| writel(val, ptr); |
| } |
| //read reg |
| static int aml_read_self(unsigned int reg) |
| { |
| void *addr = p_hw_base + reg; |
| int ret = readl(addr); |
| return ret; |
| } |
| |
| /**\brief init_ci_addr:ci bus init mem addr |
| * \param pdev: |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| int init_ci_addr(struct platform_device *pdev) |
| { |
| struct resource *res; |
| |
| res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!res) { |
| pr_dbg("%s fail\n", __func__); |
| return -1; |
| } |
| |
| p_hw_base = devm_ioremap_nocache(&pdev->dev, res->start, |
| resource_size(res)); |
| if (p_hw_base) { |
| pr_dbg("%s base addr = %lx\n", __func__, |
| (unsigned long)p_hw_base); |
| } else { |
| pr_dbg("%s base addr error\n", __func__); |
| } |
| return 0; |
| } |
| |
| |
| /**\brief aml_ci_bus_io:ci bus read or write api with bus |
| * \param ci_bus_dev: ci_bus_dev obj,used this data to ctl |
| * \param val: read or write value |
| * \param addr: rw addr |
| * \param mode: cmd |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_select_gpio(struct aml_ci_bus *ci_bus_dev, |
| int select/*, int enable*/) |
| { |
| //SELECT GPIO FUNCTION |
| unsigned int old_select = ci_bus_dev->select; |
| struct pinctrl_state *s; |
| int ret = 0; |
| |
| if (old_select == select) |
| return 0; |
| |
| if (ci_bus_dev->addr_ts_mode_multiplex == 0) { |
| //not multiplex ts and ci addr, so no need to |
| //change ts and addr |
| if (select == AML_GPIO_ADDR) { |
| return 0; |
| } |
| } |
| if (!ci_bus_dev->pinctrl) { |
| ci_bus_dev->pinctrl = devm_pinctrl_get(&ci_bus_dev->pdev->dev); |
| if (IS_ERR_OR_NULL(ci_bus_dev->pinctrl)) { |
| dev_err(&ci_bus_dev->pdev->dev, "could not get pinctrl handle\n"); |
| return -EINVAL; |
| } |
| } |
| if (IS_ERR_OR_NULL(ci_bus_dev->pinctrl)) { |
| dev_err(&ci_bus_dev->pdev->dev, "return, could not get pinctrl handle\n"); |
| return -EINVAL; |
| } |
| /* set pinmux */ |
| switch (select) { |
| case AML_GPIO_ADDR: |
| s = pinctrl_lookup_state(ci_bus_dev->pinctrl, "ci_addr_pins"); |
| if (IS_ERR_OR_NULL(s)) { |
| dev_err(&ci_bus_dev->pdev->dev, |
| "could not get ci_addr_pins state\n"); |
| return -EINVAL; |
| } |
| ret = pinctrl_select_state(ci_bus_dev->pinctrl, s); |
| if (ret) { |
| dev_err(&ci_bus_dev->pdev->dev, "failed to set ci_addr_pins pinctrl\n"); |
| return -EINVAL; |
| } |
| if (ci_bus_dev->le_pin) { |
| aml_set_gpio_out(ci_bus_dev->le_pin, ci_bus_dev->le_enable_level); |
| pr_dbg("set le pin to low"); |
| } |
| break; |
| case AML_GPIO_TS: |
| if (ci_bus_dev->le_pin) { |
| aml_set_gpio_out(ci_bus_dev->le_pin, !ci_bus_dev->le_enable_level); |
| pr_dbg("set le pin to high"); |
| } |
| s = pinctrl_lookup_state(ci_bus_dev->pinctrl, "ci_ts_pins"); |
| if (IS_ERR_OR_NULL(s)) { |
| dev_err(&ci_bus_dev->pdev->dev, |
| "could not get ci_ts_pins state\n"); |
| return -EINVAL; |
| } |
| ret = pinctrl_select_state(ci_bus_dev->pinctrl, s); |
| if (ret) { |
| dev_err(&ci_bus_dev->pdev->dev, "failed to set ci_ts_pins pinctrl\n"); |
| return -EINVAL; |
| } |
| break; |
| default: |
| break; |
| } |
| ci_bus_dev->select = select; |
| return 0; |
| } |
| |
| /**\brief aml_ci_bus_set_delay_time:set ci bus delay time |
| * \param mode: cmd,io rd/wr or mem rd/wr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_set_delay_time(int mode) |
| { |
| u32 delay0 = 0, delay1 = 0; |
| //set cmd delay |
| if (mode == AM_CI_CMD_IOR) { |
| //delay0 |
| delay0 = delay0 | (DELAY_RIO_INIT_ADDR << 0); |
| delay0 = delay0 | (DELAY_RIO_ADDR_CE << 8); |
| delay0 = delay0 | (DELAY_RIO_CE_RD << 16); |
| delay0 = delay0 | (DELAY_RIO_RD_RWAIT << 24); |
| //delay1 |
| delay1 = delay1 | (DELAY_RIO_RWAIT_DATA << 0); |
| delay1 = delay1 | (DELAY_RIO_DATA_DRD << 8); |
| delay1 = delay1 | (DELAY_RIO_DRD_DCE << 16); |
| delay1 = delay1 | (DELAY_RIO_DCE_INIT << 24); |
| } else if (mode == AM_CI_CMD_IOW) { |
| //delay0 |
| delay0 = delay0 | (DELAY_WIO_INIT_ADDR << 0); |
| delay0 = delay0 | (DELAY_WIO_ADDR_CE << 8); |
| delay0 = delay0 | (DELAY_WIO_CE_WR << 16); |
| delay0 = delay0 | (DELAY_WIO_WR_RWAIT << 24); |
| //delay1 |
| delay1 = delay1 | (DELAY_WIO_RWAIT_DATA << 0); |
| delay1 = delay1 | (DELAY_WIO_DATA_DWR << 8); |
| delay1 = delay1 | (DELAY_WIO_DWR_DCE << 16); |
| delay1 = delay1 | (DELAY_WIO_DCE_INIT << 24); |
| } else if (mode == AM_CI_CMD_MEMR) { |
| //delay0 |
| delay0 = delay0 | (DELAY_RMEM_INIT_ADDR << 0); |
| delay0 = delay0 | (DELAY_RMEM_ADDR_CE << 8); |
| delay0 = delay0 | (DELAY_RMEM_CE_RD << 16); |
| delay0 = delay0 | (DELAY_RMEM_RD_RWAIT << 24); |
| //delay1 |
| delay1 = delay1 | (DELAY_RMEM_RWAIT_DATA << 0); |
| delay1 = delay1 | (DELAY_RMEM_DATA_DRD << 8); |
| delay1 = delay1 | (DELAY_RMEM_DRD_DCE << 16); |
| delay1 = delay1 | (DELAY_RMEM_DCE_INIT << 24); |
| } else if (mode == AM_CI_CMD_MEMW) { |
| //delay0 |
| delay0 = delay0 | (DELAY_WMEM_INIT_ADDR << 0); |
| delay0 = delay0 | (DELAY_WMEM_ADDR_CE << 8); |
| delay0 = delay0 | (DELAY_WMEM_CE_WR << 16); |
| delay0 = delay0 | (DELAY_WMEM_WR_RWAIT << 24); |
| //delay1 |
| delay1 = delay1 | (DELAY_WMEM_RWAIT_DATA << 0); |
| delay1 = delay1 | (DELAY_WMEM_DATA_DWR << 8); |
| delay1 = delay1 | (DELAY_WMEM_DWR_DCE << 16); |
| delay1 = delay1 | (DELAY_WMEM_DCE_INIT << 24); |
| } |
| //Wwrite cmd |
| WRITE_CIBUS_REG(CIPLUS_DELAY_CTRL0, delay0); |
| WRITE_CIBUS_REG(CIPLUS_DELAY_CTRL1, delay1); |
| return 0; |
| } |
| |
| |
| /**\brief aml_ci_bus_io:ci bus read or write api with bus |
| * \param ci_bus_dev: ci_bus_dev obj,used this data to ctl |
| * \param val: read or write value |
| * \param addr: rw addr |
| * \param mode: cmd |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_io(struct aml_ci_bus *ci_bus_dev, |
| u8 val, u16 addr, int mode) |
| { |
| int rd; |
| int ret = -1; |
| u32 address = addr; |
| u32 data = val; |
| u32 reg = 0; |
| u32 ctrl = 0; |
| int enable = 0; |
| int count = 0; |
| u32 int_status; |
| //only used hi addr. we to change tsout to addr |
| if (addr >= 4) { |
| enable = 1; |
| } |
| //clear irq |
| ctrl = READ_CIBUS_REG(CIPLUS_CTRL_REG); |
| ctrl = ctrl | (1 << CLEAR_CMP_IRQ); |
| ctrl = ctrl | (1 << CLEAR_TIMEOUT_IRQ); |
| //Wwrite cmd crtl |
| WRITE_CIBUS_REG(CIPLUS_CTRL_REG, ctrl); |
| fetch_done = 0; |
| //gpio select gpio func |
| //aml_ci_bus_select_gpio(ci_bus_dev, enable ? AML_GPIO_ADDR : AML_GPIO_TS); |
| while (1) { |
| count++; |
| if (count < aml_ci_bus_time) |
| break; |
| } |
| //enable delay reg,defalue is disable |
| if (aml_ci_bus_set_delay) |
| aml_ci_bus_set_delay_time(mode); |
| //cmd vilad |
| reg = reg | (1 << CI_CMD_VALID); |
| //set addr |
| reg = reg | ((address & 0xeFFF) << CI_CMD_ADDR); |
| //set cmd and write data |
| if (mode == AM_CI_CMD_IOR) { |
| reg = reg | (IORD << CI_CMD_TYPE); |
| } else if (mode == AM_CI_CMD_IOW) { |
| reg = reg | (data << CI_CMD_WDATA); |
| reg = reg | (IOWR << CI_CMD_TYPE); |
| } else if (mode == AM_CI_CMD_MEMR) { |
| reg = reg | (MEMRD << CI_CMD_TYPE); |
| } else if (mode == AM_CI_CMD_MEMW) { |
| reg = reg | (data << CI_CMD_WDATA); |
| reg = reg | (MEMWR << CI_CMD_TYPE); |
| } |
| //clear irq |
| ctrl = READ_CIBUS_REG(CIPLUS_CTRL_REG); |
| ctrl = ctrl | (1 << CLEAR_CMP_IRQ); |
| ctrl = ctrl | (1 << CLEAR_TIMEOUT_IRQ); |
| //Wwrite cmd crtl |
| WRITE_CIBUS_REG(CIPLUS_CTRL_REG, ctrl); |
| //Wwrite cmd reg |
| WRITE_CIBUS_REG(CIPLUS_CMD_REG, reg); |
| //wait cmp irq or timwout irq |
| if (USED_IRQ == 1) { |
| ret = |
| wait_event_interruptible_timeout(wq, fetch_done != 0, |
| HZ / 100);//10ms |
| } else { |
| count = 0; |
| while(1) { |
| count++; |
| int_status = READ_CIBUS_REG(CIPLUS_STATUS_REG); |
| if ((int_status&(1 << COMPLETE_IRQ_STATE)) == (1 << COMPLETE_IRQ_STATE)) { |
| break; |
| } |
| if (count > 50) { |
| printk("count timeout:%d\r\n", count); |
| break; |
| } |
| } |
| } |
| rd = READ_CIBUS_REG(CIPLUS_RDATA_REG); |
| //gpio select tsout func |
| //aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_TS, enable); |
| return rd; |
| } |
| /**\brief aml_ci_bus_init_reg:ci bus init reg,enable ci bus |
| * \param ci_bus_dev: ci_bus_dev obj,used this data to ctl |
| * \return |
| * - 0:ok |
| */ |
| static int aml_ci_bus_init_reg(struct aml_ci_bus *ci_bus_dev) |
| { |
| u32 ctrl = 0; |
| |
| if (ci_bus_dev->addr_ts_mode_multiplex == 0) { |
| aml_ci_bus_select_gpio(ci_bus_dev,AML_GPIO_TS); |
| } else { |
| aml_ci_bus_select_gpio(ci_bus_dev,AML_GPIO_ADDR); |
| } |
| |
| //init ci bus reg |
| pr_dbg("aml_ci_bus_init_reg---\r\n"); |
| ctrl = READ_CIBUS_REG(CIPLUS_CTRL_REG); |
| ctrl = ctrl | (1 << CI_ENABLE); |
| ctrl = ctrl | (1 << ENABLE_CMP_IRQ); |
| WRITE_CIBUS_REG(CIPLUS_CTRL_REG, ctrl); |
| |
| ctrl = 0; |
| ctrl = ctrl | (1 << ENABEL_TIMEOUT_IRQ); |
| ctrl = ctrl | (TIMEOUT_IRQ_HOLD_TIME << WATT_TIMEOUT_TIME); |
| //timeout hold time |
| //WRITE_CIBUS_REG(CIPLUS_WAIT_TIMEOUT, ctrl); |
| //aml_ci_bus_select_gpio(ci_bus_dev,AML_GPIO_TS); |
| return 0; |
| } |
| /********************************************************/ |
| /********************************************************/ |
| /******* 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; |
| } |
| |
| /**\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; |
| } |
| |
| /**\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 ci_bus_get_gpio_by_name:get gpio desc from dts file |
| * \param ci_bus_dev: aml_ci_bus 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 ci_bus_get_gpio_by_name(struct aml_ci_bus *ci_bus_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 = ci_bus_dev->pdev; |
| struct device_node *np = pdev->dev.of_node; |
| |
| /*get 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 bus %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("ci bus %s request failed\n", str); |
| return -1; |
| } |
| pr_dbg("ci bus 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("ci bus Request gpio direction invalid\n"); |
| } |
| return ret; |
| } |
| /********************************************************/ |
| /********************************************************/ |
| /******* gpio ci bus api end *************/ |
| /********************************************************/ |
| /********************************************************/ |
| /**\brief aml_ci_bus_mem_read:io read from cam |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_dev obj |
| * \param slot: slot index |
| * \param addr: read addr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_mem_read( |
| struct aml_ci *ci_dev, int slot, int addr) |
| { |
| u8 data = 0; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_ci_bus *ci_bus_dev = ci_dev->data; |
| mutex_lock(&(ci_bus_dev->mutex)); |
| aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_ADDR); |
| value = aml_ci_bus_io(ci_bus_dev, data, addres, AM_CI_CMD_MEMR); |
| mutex_unlock(&(ci_bus_dev->mutex)); |
| return value; |
| } |
| /**\brief aml_ci_bus_mem_write:io write to cam by bus api |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_dev obj |
| * \param slot: slot index |
| * \param addr: write addr |
| * \param addr: write value |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_mem_write( |
| struct aml_ci *ci_dev, int slot, int addr, u8 val) |
| { |
| u8 data = val; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_ci_bus *ci_bus_dev = ci_dev->data; |
| mutex_lock(&(ci_bus_dev->mutex)); |
| aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_ADDR); |
| value = aml_ci_bus_io(ci_bus_dev, data, addres, AM_CI_CMD_MEMW); |
| mutex_unlock(&(ci_bus_dev->mutex)); |
| return value; |
| } |
| /**\brief aml_ci_bus_io_read:io read from cam by bus api |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_dev obj |
| * \param slot: slot index |
| * \param addr: read addr |
| * \return |
| * - read value:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_io_read( |
| struct aml_ci *ci_dev, int slot, int addr) |
| { |
| u8 data = 0; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_ci_bus *ci_bus_dev = ci_dev->data; |
| mutex_lock(&(ci_bus_dev->mutex)); |
| aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_TS); |
| value = aml_ci_bus_io(ci_bus_dev, data, addres, AM_CI_CMD_IOR); |
| mutex_unlock(&(ci_bus_dev->mutex)); |
| return value; |
| } |
| /**\brief aml_ci_bus_io_write:io write to cam |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_dev obj |
| * \param slot: slot index |
| * \param addr: write addr |
| * \param addr: write value |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_io_write( |
| struct aml_ci *ci_dev, int slot, int addr, u8 val) |
| { |
| u8 data = val; |
| u16 addres = addr; |
| int value = 0; |
| struct aml_ci_bus *ci_bus_dev = ci_dev->data; |
| mutex_lock(&(ci_bus_dev->mutex)); |
| aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_TS); |
| value = aml_ci_bus_io(ci_bus_dev, data, addres, AM_CI_CMD_IOW); |
| mutex_unlock(&(ci_bus_dev->mutex)); |
| return value; |
| } |
| |
| |
| /**\brief aml_ci_bus_rst:reset cam by ci bus |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_dev obj |
| * \param slot: slot index |
| * \return |
| * - 0:ok |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_rst( |
| struct aml_ci *ci_dev, int slot, int level) |
| { |
| int value = 0; |
| u32 ctrl = 0; |
| struct aml_ci_bus *ci_bus_dev = ci_dev->data; |
| mutex_lock(&(ci_bus_dev->mutex)); |
| ctrl = READ_CIBUS_REG(CIPLUS_CTRL_REG); |
| if (level == AML_H) |
| ctrl = ctrl | (1 << CAM_RESET); |
| else |
| ctrl = ctrl & (~(1 << CAM_RESET)); |
| //Wwrite cmd crtl |
| WRITE_CIBUS_REG(CIPLUS_CTRL_REG, ctrl); |
| mutex_unlock(&(ci_bus_dev->mutex)); |
| return value; |
| } |
| |
| /**\brief aml_ci_slot_reset:reset slot |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_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_ci_bus *ci_bus_dev = ci_dev->data; |
| pr_dbg("Slot(%d): Slot RESET CAM\n", slot); |
| aml_pcmcia_reset(&ci_bus_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 ci_bus_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 ci_bus_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 ci_bus_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_ci_bus *ci_bus_dev = ci_dev->data; |
| int state = 0; |
| if (ci_bus_dev->pc.start_work == 0) { |
| return 0; |
| } |
| if (ci_bus_dev->pc.slot_state == MODULE_INSERTED) { |
| state = DVB_CA_EN50221_POLL_CAM_PRESENT | |
| DVB_CA_EN50221_POLL_CAM_READY; |
| } |
| if (ci_bus_dev->slot_state != ci_bus_dev->pc.slot_state) |
| { |
| printk("cam crad change-----\r\n"); |
| ci_bus_dev->slot_state = ci_bus_dev->pc.slot_state; |
| state = state | DVB_CA_EN50221_POLL_CAM_CHANGED; |
| } |
| return state; |
| } |
| /**\brief aml_ci_slot_wakeup:get slot wake up thread flag |
| * \param ci_dev: aml_ci obj,used this data to get ci_bus_dev obj |
| * \param slot: slot index |
| * \return |
| * - cam wake up flag |
| * - -EINVAL : error |
| */ |
| static int aml_ci_slot_wakeup(struct aml_ci *ci_dev, int slot) |
| { |
| struct aml_ci_bus *ci_bus_dev = ci_dev->data; |
| if (ci_bus_dev) { |
| return ci_bus_dev->wakeup_thread; |
| } |
| return 1; |
| } |
| |
| /**\brief aml_ci_gio_get_irq:get gpio cd1 irq pin value |
| * \return |
| * - irq pin value |
| * - -EINVAL : error |
| */ |
| static int aml_ci_gio_get_irq(void) |
| { |
| int ret = 0; |
| ret = aml_get_gpio_value(ci_bus.cd_pin1); |
| return ret; |
| } |
| |
| /********************************************************/ |
| /********************************************************/ |
| /******* for pcmcid api *************/ |
| /********************************************************/ |
| /********************************************************/ |
| /**\brief aml_gio_power:set power gpio hi or low |
| * \param pc: aml_pcmcia obj,used this priv to get ci_bus_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_ci_bus *ci_bus_dev = pc->priv; |
| if (ci_bus_dev == NULL) { |
| pr_dbg("ci bus dev is null %s : %d\r\n", __func__, enable); |
| return -1; |
| } |
| pr_dbg("%s : %d\r\n", __func__, enable); |
| |
| if (enable == AML_PWR_OPEN) { |
| /*hi level ,open power*/ |
| ret = aml_set_gpio_out(ci_bus_dev->pwr_pin, AML_GPIO_LOW); |
| } else { |
| /*low level ,close power*/ |
| ret = aml_set_gpio_in(ci_bus_dev->pwr_pin); |
| } |
| |
| return ret; |
| } |
| /**\brief aml_gio_reset:set reset gpio hi or low |
| * \param pc: aml_pcmcia obj,used this priv to get ci_bus_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_ci_bus *ci_bus_dev = pc->priv; |
| |
| if (ci_bus_dev == NULL) { |
| pr_dbg("ci bus dev is null %s : %d\r\n", __func__, enable); |
| return -1; |
| } |
| |
| pr_dbg("%s : %d type: %d\r\n", __func__, enable, ci_bus_dev->io_device_type); |
| if (ci_bus_dev == NULL || ci_bus_dev->priv == NULL) { |
| pr_dbg("rst by ci bus- ci bus dev-null-\r\n"); |
| return -1; |
| } |
| /*if (enable == AML_H) {*/ |
| aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_ADDR); |
| /*ci_bus_dev->select = AML_GPIO_TS; |
| }*/ |
| |
| aml_ci_bus_rst((struct aml_ci *)ci_bus_dev->priv, 0, enable); |
| pr_dbg("rst by ci bus- ci bus [%d]-\r\n", ci_bus_dev->select); |
| /*if (enable == AML_L) |
| aml_ci_bus_select_gpio(ci_bus_dev, AML_GPIO_TS);*/ |
| return ret; |
| } |
| |
| /**\brief aml_gio_init_irq:set gpio irq |
| * \param pc: aml_pcmcia obj,used this priv to get ci_bus_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_ci_bus *ci_bus_dev = (struct aml_ci_bus *)pc->priv; |
| gpiod_to_irq(ci_bus_dev->cd_pin1); |
| return 0; |
| } |
| |
| /**\brief aml_gio_get_cd1:get gpio cd1 pin value |
| * \param pc: aml_pcmcia obj,used this priv to get ci_bus_dev obj |
| * \return |
| * - cd1 pin value |
| * - -EINVAL : error |
| */ |
| static int aml_gio_get_cd1(struct aml_pcmcia *pc) |
| { |
| int ret = 1; |
| struct aml_ci_bus *ci_bus_dev = pc->priv; |
| ret = aml_get_gpio_value(ci_bus_dev->cd_pin1); |
| pr_dbg("%s :cd: %d\r\n", __func__, ret); |
| return ret; |
| } |
| /**\brief aml_gio_get_cd2:get gpio cd2 pin value |
| * \param pc: aml_pcmcia obj,used this priv to get ci_bus_dev obj |
| * \return |
| * - cd2 pin value |
| * - -EINVAL : error |
| */ |
| static int aml_gio_get_cd2(struct aml_pcmcia *pc) |
| { |
| int ret = 0; |
| struct aml_ci_bus *ci_bus_dev = pc->priv; |
| ret = aml_get_gpio_value(ci_bus_dev->cd_pin1); |
| pr_dbg("%s :cd: %d\r\n", __func__, ret); |
| return ret; |
| } |
| /**\brief aml_cam_plugin:notify en50221 cam card in or out |
| * \param pc: aml_pcmcia obj,used this priv to get ci_bus_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_ci_bus *)(pc->priv))->priv; |
| pr_dbg("%s : %d\r\n", __func__, plugin); |
| if (plugin) { |
| aml_ci_bus_select_gpio((struct aml_ci_bus *)(pc->priv), AML_GPIO_TS); |
| dvb_ca_en50221_cimcu_camchange_irq(&ci->en50221_cimcu, |
| 0, DVB_CA_EN50221_CAMCHANGE_INSERTED); |
| } else { |
| aml_ci_bus_select_gpio((struct aml_ci_bus *)(pc->priv), AML_GPIO_ADDR); |
| 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 ci_bus_dev: aml_ci_bus obj, |
| * \param pcmcia: aml_pcmcia * obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static void aml_pcmcia_alloc(struct aml_ci_bus *ci_bus_dev, |
| struct aml_pcmcia **pcmcia) |
| { |
| pr_dbg("aml_pcmcia_alloc----\n"); |
| *pcmcia = &ci_bus_dev->pc; |
| (*pcmcia)->irq = ci_bus_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 = ci_bus_dev; |
| (*pcmcia)->run_type = 0;/*0:irq;1:poll*/ |
| (*pcmcia)->io_device_type = AML_DVB_IO_TYPE_CIMAX; |
| (*pcmcia)->start_work = 0; |
| } |
| |
| /**\brief aml_ci_bus_get_config_from_dts: |
| * get gpio config from dts |
| * \param ci_bus_dev: aml_ci_bus obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static int aml_ci_bus_get_config_from_dts(struct aml_ci_bus *ci_bus_dev) |
| { |
| struct device_node *child = NULL; |
| struct platform_device *pdev = ci_bus_dev->pdev; |
| struct device_node *np = pdev->dev.of_node; |
| int ret = 0; |
| pr_dbg("into get ci bus dts -----\r\n"); |
| /*get 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; |
| } |
| //below is get cd1 cd2 pwr irq reset gpio info |
| if (ci_bus_dev->io_device_type == AML_DVB_IO_TYPE_CIBUS) { |
| struct resource *res; |
| char buf[32]; |
| int ival; |
| |
| /*get irq value*/ |
| ci_bus_dev->irq_cmp = 186; |
| memset(buf, 0, 32); |
| snprintf(buf, sizeof(buf), "%s", "irq_cmp"); |
| res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, buf); |
| if (res) |
| ci_bus_dev->irq_cmp = res->start; |
| else |
| pr_err("get irq cmp error\r\n"); |
| |
| ci_bus_dev->irq_timeout = 187; |
| memset(buf, 0, 32); |
| snprintf(buf, sizeof(buf), "%s", "irq_timeout"); |
| res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, buf); |
| if (res) |
| ci_bus_dev->irq_timeout = res->start; |
| else |
| pr_err("get irq irq_timeout error\r\n"); |
| //pin config |
| pr_dbg("ci bus irq[%d]cmp[%d] \r\n",ci_bus_dev->irq_cmp, ci_bus_dev->irq_timeout); |
| if (!ci_bus_dev->pinctrl) { |
| ci_bus_dev->pinctrl = devm_pinctrl_get(&pdev->dev); |
| if (IS_ERR_OR_NULL(ci_bus_dev->pinctrl)) { |
| pr_error("get pinctl could not get pinctrl handle\n"); |
| return -EINVAL; |
| } |
| } |
| |
| if (ci_bus_dev->pinctrl) { |
| struct pinctrl_state *s; |
| pr_dbg("ci bus get ts pin\r\n"); |
| s = pinctrl_lookup_state(ci_bus_dev->pinctrl, "ci_ts_pins"); |
| if (IS_ERR_OR_NULL(s)) { |
| pr_error("could not get jtag_apee_pins state\n"); |
| return -1; |
| } |
| pr_dbg("ci bus select ts pin\r\n"); |
| ret = pinctrl_select_state(ci_bus_dev->pinctrl, s); |
| if (ret) { |
| pr_error("failed to set pinctrl\n"); |
| return -1; |
| } |
| ci_bus_dev->select = AML_GPIO_TS; |
| } else if (IS_ERR_OR_NULL(ci_bus_dev->pinctrl)) { |
| pr_error("could not get pinctrl handle\n"); |
| return -EINVAL; |
| } |
| /*get reset pwd cd1 cd2 gpio pin*/ |
| ci_bus_dev->cd_pin1 = NULL; |
| pr_dbg("ci bus get cd1\r\n"); |
| ret = ci_bus_get_gpio_by_name(ci_bus_dev, |
| &ci_bus_dev->cd_pin1, |
| &ci_bus_dev->cd_pin1_value, "cd_pin1", |
| INPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci cd_pin1 pin request failed\n"); |
| return -1; |
| } |
| ci_bus_dev->cd_pin2 = ci_bus_dev->cd_pin1; |
| ci_bus_dev->cd_pin2_value = ci_bus_dev->cd_pin1_value; |
| ci_bus_dev->pwr_pin = NULL; |
| pr_dbg("ci_bus_dev->cd_pin1_value==%d\r\n", ci_bus_dev->cd_pin1_value); |
| ci_bus_dev->irq = gpiod_to_irq(ci_bus_dev->cd_pin1) ; |
| pr_dbg("ci_bus_dev->irq==%d get from gpio cd1\r\n", ci_bus_dev->irq); |
| |
| ret = ci_bus_get_gpio_by_name(ci_bus_dev, |
| &ci_bus_dev->pwr_pin, &ci_bus_dev->pwr_pin_value, |
| "pwr_pin", OUTPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci pwr_pin pin request failed\n"); |
| return -1; |
| } |
| aml_set_gpio_in(ci_bus_dev->pwr_pin); |
| |
| /*get le pin*/ |
| ci_bus_dev->le_pin = NULL; |
| ci_bus_dev->le_enable_level = 1; |
| ret = ci_bus_get_gpio_by_name(ci_bus_dev, |
| &ci_bus_dev->le_pin, &ci_bus_dev->le_pin_value, |
| "le_pin", OUTPUT, OUTLEVEL_HIGH); |
| if (ret) { |
| pr_error("dvb ci le_pin pin request failed\n"); |
| } else { |
| pr_dbg("ci_bus_dev->le_value %d\n", ci_bus_dev->le_pin_value); |
| } |
| memset(buf, 0, 32); |
| snprintf(buf, sizeof(buf), "%s", "le_enable_level"); |
| ret = of_property_read_u32(pdev->dev.of_node, buf, &ival); |
| if (ret) { |
| pr_error("dvb ci le_enable_level request failed\n"); |
| } else { |
| ci_bus_dev->le_enable_level = ival; |
| pr_dbg("ci_bus_dev->le_enable_level-- %d\n", ci_bus_dev->le_enable_level); |
| } |
| /*get addr_ts_mode_multiplex mode*/ |
| ci_bus_dev->addr_ts_mode_multiplex = 1; |
| memset(buf, 0, 32); |
| snprintf(buf, sizeof(buf), "%s", "addr_ts_mode_multiplex"); |
| ret = of_property_read_u32(pdev->dev.of_node, buf, &ival); |
| if (ret) { |
| pr_error("dvb ci addr_ts_mode_multiplex request failed\n"); |
| } else { |
| ci_bus_dev->addr_ts_mode_multiplex = ival; |
| pr_dbg("ci_bus_dev->addr_ts_mode_multiplex %d ******\n", ci_bus_dev->addr_ts_mode_multiplex); |
| } |
| |
| |
| } |
| return 0; |
| } |
| /**\brief aml_ci_free_gpio:free ci gpio |
| * \param ci_bus_dev: aml_ci_bus obj, |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| static void aml_ci_free_gpio(struct aml_ci_bus *ci_bus_dev) |
| { |
| if (ci_bus_dev == NULL) { |
| pr_error("ci_bus_dev is NULL,no need free gpio res\r\n"); |
| return; |
| } |
| |
| if (ci_bus_dev->pwr_pin) { |
| aml_gpio_free(ci_bus_dev->pwr_pin); |
| ci_bus_dev->pwr_pin = NULL; |
| } |
| if (ci_bus_dev->cd_pin1) { |
| aml_gpio_free(ci_bus_dev->cd_pin1); |
| ci_bus_dev->cd_pin1 = NULL; |
| ci_bus_dev->cd_pin2 = NULL; |
| } |
| if (ci_bus_dev->le_pin) { |
| aml_gpio_free(ci_bus_dev->le_pin); |
| ci_bus_dev->le_pin = NULL; |
| } |
| return; |
| } |
| |
| static irqreturn_t timeout_isr(int irq, void *dev_id) |
| { |
| u32 int_status = READ_CIBUS_REG(CIPLUS_STATUS_REG); |
| |
| if ((int_status & (1 << TIMEOUT_IRQ_STATE)) == (1 << TIMEOUT_IRQ_STATE)) { |
| fetch_done = 1; |
| wake_up_interruptible(&wq); |
| } |
| return IRQ_HANDLED; |
| } |
| |
| static irqreturn_t cmp_isr(int irq, void *dev_id) |
| { |
| u32 int_status = READ_CIBUS_REG(CIPLUS_STATUS_REG); |
| |
| if ((int_status&(1 << COMPLETE_IRQ_STATE)) == (1 << COMPLETE_IRQ_STATE)) { |
| fetch_done = 1; |
| wake_up_interruptible(&wq); |
| } |
| return IRQ_HANDLED; |
| } |
| |
| /**\brief aml_ci_bus_init:ci_bus_dev init |
| * \param ci_dev: aml_ci obj, |
| * \param pdev: platform_device obj,used to get dts info |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| int aml_ci_bus_init(struct platform_device *pdev, struct aml_ci *ci_dev) |
| { |
| struct aml_ci_bus *ci_bus_dev = NULL; |
| struct aml_pcmcia *pc; |
| int result,irq; |
| |
| ci_bus_dev = &ci_bus; |
| ci_bus_dev->pdev = pdev; |
| ci_bus_dev->priv = ci_dev; |
| ci_bus_dev->bus_pinctrl = NULL; |
| ci_bus_dev->pinctrl = NULL; |
| /*default mode is wake up,when trans a lot data,used sleep mode*/ |
| ci_bus_dev->wakeup_thread = 1; |
| mutex_init(&(ci_bus_dev->mutex)); |
| /*init io device type*/ |
| ci_bus_dev->io_device_type = ci_dev->io_type; |
| pr_dbg("*********ci bus Dev type [%d]\n", ci_dev->io_type); |
| /*get config from dts*/ |
| aml_ci_bus_get_config_from_dts(ci_bus_dev); |
| //iomap ci reg |
| init_ci_addr(pdev); |
| /*Register irq handlers */ |
| if (ci_bus_dev->irq_cmp != -1) { |
| if (USED_IRQ) { |
| irq = request_irq(ci_bus_dev->irq_cmp, |
| cmp_isr, |
| IRQF_SHARED|IRQF_TRIGGER_RISING, |
| "ciplus cmp irq", ci_bus_dev); |
| if (irq == 0) |
| pr_dbg("request cmp irq sucess\r\n"); |
| else if (irq == -EBUSY) |
| pr_err("request cmp irq busy\r\n"); |
| else |
| pr_err("request cmp irq error [%d]\r\n", irq); |
| } else { |
| disable_irq(ci_bus_dev->irq_cmp); |
| } |
| } |
| /*Register irq handlers */ |
| if (ci_bus_dev->irq_timeout != -1) { |
| if (USED_IRQ) { |
| pr_dbg("request timeout irq\n"); |
| irq = request_irq(ci_bus_dev->irq_timeout, |
| timeout_isr, |
| IRQF_SHARED|IRQF_TRIGGER_RISING, |
| "ciplus timeout irq", ci_bus_dev); |
| if (irq == 0) |
| pr_err("request timeout irq sucess\r\n"); |
| else if (irq == -EBUSY) |
| pr_err("request timeout irq busy\r\n"); |
| else |
| pr_err("request timeout irq error [%d]\r\n", irq); |
| } else { |
| disable_irq(ci_bus_dev->irq_timeout); |
| } |
| } |
| pr_dbg("*********ci bus init bus reg\n"); |
| aml_ci_bus_init_reg(ci_bus_dev); |
| /*init ci_dev used api.*/ |
| ci_dev->ci_mem_read = aml_ci_bus_mem_read; |
| ci_dev->ci_mem_write = aml_ci_bus_mem_write; |
| ci_dev->ci_io_read = aml_ci_bus_io_read; |
| ci_dev->ci_io_write = aml_ci_bus_io_write; |
| 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->ci_get_slot_wakeup = aml_ci_slot_wakeup; |
| ci_dev->data = ci_bus_dev; |
| |
| aml_pcmcia_alloc(ci_bus_dev, &pc); |
| pc->io_device_type = ci_bus_dev->io_device_type; |
| pr_dbg("*********ci bus aml_pcmcia_init start_work:%d\n", pc->start_work); |
| result = aml_pcmcia_init(pc); |
| if (result < 0) { |
| pr_error("aml_pcmcia_init failed\n"); |
| goto fail1; |
| } |
| pr_dbg("*********ci bus aml_ci_bus_mod_init---\n"); |
| aml_ci_bus_mod_init(); |
| return 0; |
| fail1: |
| kfree(ci_bus_dev); |
| ci_bus_dev = NULL; |
| return 0; |
| } |
| EXPORT_SYMBOL(aml_ci_bus_init); |
| /**\brief aml_ci_bus_exit:ci_bus exit |
| * \return |
| * - 0 |
| * - -EINVAL : error |
| */ |
| int aml_ci_bus_exit(struct aml_ci *ci) |
| { |
| aml_ci_bus_mod_exit(); |
| /*exit pc card*/ |
| aml_pcmcia_exit(&ci_bus.pc); |
| /*free gpio*/ |
| aml_ci_free_gpio(&ci_bus); |
| |
| return 0; |
| } |
| EXPORT_SYMBOL(aml_ci_bus_exit); |
| |
| #if 1 |
| /********************************************************/ |
| /********************************************************/ |
| /******* for ci bus 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_ci_bus_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 < 200; i++) { |
| mdelay(100); |
| cc = aml_ci_bus_mem_read(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_bus_io_write(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_bus_io_write(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_bus_io_read( |
| ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg1)) { |
| continue; |
| } else { |
| pr_dbg("CAM Reset Ok\r\n"); |
| break; |
| } |
| } |
| reg = aml_ci_bus_io_read(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_bus_io_write(ci_dev, 0, COM_STA_REG, SR); |
| mdelay(1000); |
| while (1) { |
| |
| reg = aml_ci_bus_io_read(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_bus_io_read(ci_dev, 0, SIZE_REG_L)) + |
| ((aml_ci_bus_io_read(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_bus_io_read(ci_dev, 0, DATA_REG); |
| |
| reg = aml_ci_bus_io_read(ci_dev, 0, COM_STA_REG); |
| if (RE == (RE & reg)) { |
| pr_dbg("(1)Read CAM buf size ERR!\r\n"); |
| return; |
| } |
| aml_ci_bus_io_write(ci_dev, 0, (COM_STA_REG), 0); |
| |
| mdelay(1000); |
| |
| while (count--) { |
| reg = aml_ci_bus_io_read(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_bus_io_read(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_bus_io_read(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) |
| pr_dbg("CAM is busy, waiting free\r\n"); |
| while (1) { |
| reg = aml_ci_bus_io_read(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_bus_io_write(ci_dev, 0, COM_STA_REG, SW); |
| reg = aml_ci_bus_io_read(ci_dev, 0, COM_STA_REG); |
| if (FR != (FR & reg)) |
| pr_dbg("CAM is busy, waiting\r\n"); |
| |
| while (1) { |
| reg = aml_ci_bus_io_read(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_bus_io_read(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_bus_io_write(ci_dev, |
| 0, COM_STA_REG, HC | SW); |
| mdelay(100); |
| reg = aml_ci_bus_io_read(ci_dev, |
| 0, COM_STA_REG); |
| if (FR != (FR & reg)) { |
| pr_dbg("CAM is busy 5, waiting\r\n"); |
| aml_ci_bus_io_write(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_bus_io_write(ci_dev, 0, SIZE_REG_M, 0); |
| aml_ci_bus_io_write(ci_dev, 0, SIZE_REG_L, 2); |
| for (i = 0; i < 2; i++) |
| aml_ci_bus_io_write(ci_dev, 0, DATA_REG, buf[i]); |
| |
| reg = aml_ci_bus_io_read(ci_dev, 0, COM_STA_REG); |
| if (WE == (WE & reg)) { |
| pr_dbg("Write CAM ERR!\r\n"); |
| return; |
| } else { |
| aml_ci_bus_io_write(ci_dev, 0, COM_STA_REG, SW); |
| mdelay(100); |
| aml_ci_bus_io_write(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_bus_mem_read((struct aml_ci *) |
| ci_bus.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_bus_mem_read((struct aml_ci *) |
| ci_bus.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_bus_mem_read((struct aml_ci *) |
| ci_bus.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 reset_show(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 reset_store(struct class *class, |
| struct class_attribute *attr, const char *buf, size_t size) |
| { |
| int ret; |
| struct aml_ci *ci = (struct aml_ci *)ci_bus.priv; |
| ret = aml_ci_slot_reset(ci, 0); |
| return size; |
| } |
| static CLASS_ATTR_RW(reset); |
| static ssize_t pwr_show(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 pwr_store(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(&ci_bus.pc, enable); |
| return size; |
| } |
| |
| static CLASS_ATTR_RW(pwr); |
| |
| static ssize_t start_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| ret = sprintf(buf, "start:%d\n", ci_bus.pc.start_work); |
| return ret; |
| } |
| |
| static ssize_t start_store(struct class *class, |
| struct class_attribute *attr, const char *buf, size_t size) |
| { |
| int enable = 0; |
| long value; |
| if (kstrtol(buf, 0, &value) == 0) { |
| enable = (int)value; |
| ci_bus.pc.start_work = enable; |
| printk("start set start\n"); |
| aml_pcmcia_detect_cam(&ci_bus.pc); |
| } |
| return size; |
| } |
| |
| static CLASS_ATTR_RW(start); |
| |
| static ssize_t wakeup_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| ret = sprintf(buf, "wakeup:%d\n", ci_bus.wakeup_thread); |
| return ret; |
| } |
| |
| static ssize_t wakeup_store(struct class *class, |
| struct class_attribute *attr, const char *buf, size_t size) |
| { |
| int enable = 0; |
| long value; |
| if (kstrtol(buf, 0, &value) == 0) { |
| enable = (int)value; |
| ci_bus.wakeup_thread = enable; |
| printk("wakeup is set\n"); |
| } |
| return size; |
| } |
| |
| static CLASS_ATTR_RW(wakeup); |
| |
| static ssize_t status_show(struct class *class, |
| struct class_attribute *attr, char *buf) |
| { |
| int ret; |
| struct aml_ci *ci = (struct aml_ci *)ci_bus.priv; |
| ret = aml_ci_slot_status(ci, 0, 0); |
| ret = sprintf(buf, "%s: %d;\n\t", attr->attr.name, ret); |
| return ret; |
| } |
| static CLASS_ATTR_RO(status); |
| |
| static ssize_t 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 CLASS_ATTR_RO(irq); |
| |
| static ssize_t iotest_show(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 iotest_store(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 *)ci_bus.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_bus_io_read(ci, 0, addr); |
| break; |
| case 'a': |
| for (i = 0; i < 1000; i++) |
| retval = aml_ci_bus_mem_read(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_bus_io_write(ci, 0, addr, val); |
| break; |
| case 'a': |
| retval = aml_ci_bus_mem_write(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_ci_bus_full_test(ci); |
| } 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 CLASS_ATTR_RW(iotest); |
| |
| static struct attribute *aml_ci_bus_attrs[] = { |
| &class_attr_iotest.attr, |
| &class_attr_status.attr, |
| &class_attr_irq.attr, |
| &class_attr_reset.attr, |
| &class_attr_pwr.attr, |
| &class_attr_start.attr, |
| &class_attr_wakeup.attr, |
| NULL |
| }; |
| |
| ATTRIBUTE_GROUPS(aml_ci_bus); |
| |
| |
| int aml_ci_bus_mod_init(void) |
| { |
| int ret; |
| struct class *clp; |
| #define CLASS_NAME_LEN 48 |
| pr_dbg("Amlogic DVB CI BUS Init\n"); |
| |
| clp = &(ci_bus.cls); |
| |
| clp->name = kzalloc(CLASS_NAME_LEN, GFP_KERNEL); |
| if (!clp->name) |
| return -ENOMEM; |
| |
| snprintf((char *)clp->name, CLASS_NAME_LEN, "aml_ci_bus_%s", "test"); |
| clp->owner = THIS_MODULE; |
| clp->class_groups = aml_ci_bus_groups; |
| ret = class_register(clp); |
| if (ret) |
| kfree(clp->name); |
| return 0; |
| } |
| |
| void aml_ci_bus_mod_exit(void) |
| { |
| pr_dbg("Amlogic DVB CI BUS Exit\n"); |
| class_unregister(&(ci_bus.cls)); |
| } |
| |
| #endif |
| #if 0 |
| module_init(aml_ci_bus_mod_init); |
| module_exit(aml_ci_bus_mod_exit); |
| |
| MODULE_LICENSE("GPL"); |
| #endif |