/****************************************************************************** ** ** FILE NAME : port.c ** PROJECT : Amazon_S ** MODULES : GPIO ** ** DATE : 21 Jun 2004 ** AUTHOR : btxu ** DESCRIPTION : Global AMAZON_S_GPIO driver header file ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** 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. ** ** HISTORY ** $Date $Author $Comment ** 21 Jun 2004 btxu Generate from INCA-IP project ** 21 Jun 2005 Jin-Sze.Sow Comments edited ** 01 Jan 2006 Huang Xiaogang Modification & verification on Amazon_S chip ** 11 Feb 2008 Lei Chuanhua Detect GPIO conflicts ** 28 May 2008 Lei Chuanhua Added forth port support & cleanup source code *******************************************************************************/ /*! \defgroup AMAZON_S_GPIO \ingroup AMAZON_S_BSP \brief amazon_s gpio driver module */ /*! \file amazon_s_gpio.c \ingroup AMAZON_S_GPIO \brief gpio driver file */ /*! \defgroup AMAZON_S_GPIO_FUNCTIONS \ingroup AMAZON_S_GPIO \brief amazon_s gpio driver functions */ #include #include #include #include #include #include #include #include #include #include #include #define IFX_PORT_MAJOR 240 #define IFX_PORT_DRV_VERSION "1.0.2" #define MAX_PORTS 4 #define PINS_PER_PORT 16 /* Shared structure, must be protected by caller */ static unsigned int amazon_s_port_pin_usage[MAX_PORTS][PINS_PER_PORT] = { // Map for pin usage { PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE, PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE}, { PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE, PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE}, { PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE, PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE}, { PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE, PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE,PORT_AVAILABLE} }; static volatile u32 * amazon_s_gpio_reg_base[MAX_PORTS][AMAZON_S_PORT_REG_MAX_IDX]= { { AMAZON_S_GPIO_P0_OUT, AMAZON_S_GPIO_P0_IN, AMAZON_S_GPIO_P0_DIR, AMAZON_S_GPIO_P0_ALTSEL0, AMAZON_S_GPIO_P0_ALTSEL1, AMAZON_S_GPIO_P0_OD, AMAZON_S_GPIO_P0_STOFF, AMAZON_S_GPIO_P0_PUDSEL, AMAZON_S_GPIO_P0_PUDEN, }, { AMAZON_S_GPIO_P1_OUT, AMAZON_S_GPIO_P1_IN, AMAZON_S_GPIO_P1_DIR, AMAZON_S_GPIO_P1_ALTSEL0, AMAZON_S_GPIO_P1_ALTSEL1, AMAZON_S_GPIO_P1_OD, AMAZON_S_GPIO_P1_STOFF, AMAZON_S_GPIO_P1_PUDSEL, AMAZON_S_GPIO_P1_PUDEN, }, { AMAZON_S_GPIO_P2_OUT, AMAZON_S_GPIO_P2_IN, AMAZON_S_GPIO_P2_DIR, AMAZON_S_GPIO_P2_ALTSEL0, AMAZON_S_GPIO_P2_ALTSEL1, AMAZON_S_GPIO_P2_OD, AMAZON_S_GPIO_P2_STOFF, AMAZON_S_GPIO_P2_PUDSEL, AMAZON_S_GPIO_P2_PUDEN, }, { AMAZON_S_GPIO_P3_OUT, AMAZON_S_GPIO_P3_IN, AMAZON_S_GPIO_P3_DIR, AMAZON_S_GPIO_P3_ALTSEL0, AMAZON_S_GPIO_P3_ALTSEL1, AMAZON_S_GPIO_P3_OD, (volatile u32 *)0, AMAZON_S_GPIO_P3_PUDSEL, AMAZON_S_GPIO_P3_PUDEN, } }; static struct semaphore port_sem; /***************************************************************************/ /** **/ /** Port usage **/ /** **/ /***************************************************************************/ static char moduleid2name[PORT_MODULE_MAX + 1][PORT_MODULE_NAME_MAX] = { "Legacy", "MEI", "SSC", "ASC", "SDIO", "LED", "USB", "SWITCH", /*Reddy, Add for SWITCH module@14032008*/ "PCI", "NAND", "PPA", "TAPI", "VINAX", /* More */ }; /** * \fn int amazon_s_port_reserve_pin (int port, int pin, int module_id) * \brief Reserve port pin for usage * This function reserves a given pin for usage by the given module. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin allready used. * EINVAL Invalid port or pin provided. * OK pin reserved successfully. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_reserve_pin (int port, int pin, int module_id) { if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if ( amazon_s_port_pin_usage[port][pin] == module_id ) { return OK; } else if (amazon_s_port_pin_usage[port][pin] == PORT_AVAILABLE) { amazon_s_port_pin_usage[port][pin] = module_id; } else { if ((amazon_s_port_pin_usage[port][pin] & 0x7FFFFFFF) > PORT_MODULE_MAX ) { printk("Unknown module try to reserve GPIO\n"); } else { printk("Port %d pin %d has been reserved by %s module %s!!!\n", port, pin, (amazon_s_port_pin_usage[port][pin] & 0x80000000) ? "application" : "driver", moduleid2name[amazon_s_port_pin_usage[port][pin] & 0x7FFFFFFF]); } return -EBUSY; } return OK; } EXPORT_SYMBOL (amazon_s_port_reserve_pin); /** * \fn int amazon_s_port_free_pin (int port, int pin, int module_id) * \brief Free port pin * This functions frees a port pin and thus clears the entry in the * usage map * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module than the given ID. * EINVAL Invalid port or pin provided. * OK pin freed successfully. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_free_pin (int port, int pin, int module_id) { if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; amazon_s_port_pin_usage[port][pin] = PORT_AVAILABLE; return OK; } EXPORT_SYMBOL (amazon_s_port_free_pin); /** * \fn int amazon_s_port_set_open_drain (int port, int pin, int module_id) * \brief Enable Open Drain for given pin * This function sets Open Drain mode for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_open_drain (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OD_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OD_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_open_drain); /** * \fn int amazon_s_port_clear_open_drain (int port, int pin, int module_id) * \brief Disable Open Drain for given pin * This function clears Open Drain mode for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_open_drain (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OD_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OD_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_open_drain); /** * \fn int amazon_s_port_set_pudsel (int port, int pin, int module_id) * \brief Enable PUDSEL for given pin * This function sets the PUDSEL bit for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_pudsel (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDSEL_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDSEL_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_pudsel); /** * \fn int amazon_s_port_clear_pudsel (int port, int pin, int module_id) * \brief Clear PUDSEL bit for given pin * This function clears the PUDSEL bit for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_pudsel (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDSEL_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDSEL_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_pudsel); /** * \fn int amazon_s_port_set_puden (int port, int pin, int module_id) * \brief Set PUDEN bit for given pin * This function sets the PUDEN bit for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_puden (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDEN_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDEN_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_puden); /** * \fn int amazon_s_port_clear_puden (int port, int pin, int module_id) * \brief Disable PUDEN bit for given pin * This function clears the PUDEN bit for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_puden (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDEN_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_PUDEN_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_puden); /** * \fn int amazon_s_port_set_stoff (int port, int pin, int module_id) * \brief Enable Schmitt Trigger for given pin * This function enables the Schmitt Trigger for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_stoff (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_STOFF_REG] == 0) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_STOFF_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_STOFF_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_stoff); /** * \fn int amazon_s_port_clear_stoff (int port, int pin, int module_id) * \brief Disable Schmitt Trigger for given pin * This function disables the Schmitt Trigger for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_stoff (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_STOFF_REG] == 0) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_STOFF_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_STOFF_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_stoff); /** * \fn int amazon_s_port_set_dir_out (int port, int pin, int module_id) * \brief Set direction to output for given pin * This function sets the direction for the given pin to output. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_dir_out (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_DIR_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_DIR_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_dir_out); /** * \fn int amazon_s_port_set_dir_in (int port, int pin, int module_id) * \brief Set direction to input for given pin * This function sets the direction for the given pin to input. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_dir_in (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_DIR_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_DIR_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_dir_in); /** * \fn int amazon_s_port_set_output (int port, int pin, int module_id) * \brief Set output bit for given pin * This function sets the output bit to 1 for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_output (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OUT_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OUT_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_output); /** * \fn int amazon_s_port_clear_output (int port, int pin, int module_id) * \brief Clear output bit for given pin * This function sets the output bit to 0 for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK. * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_output (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OUT_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_OUT_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_output); /** * \fn int amazon_s_port_get_input (int port, int pin, int module_id) * \brief Get input bit for given pin * This function gets the value of the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * 0 Pin is set to 0 * 1 Pin is set to 1 * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_get_input (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_IN_REG], reg); reg &= (1 << pin); if (reg == 0x00) return 0; else return 1; } EXPORT_SYMBOL (amazon_s_port_get_input); /** * \fn int amazon_s_port_set_altsel0 (int port, int pin, int module_id) * \brief Set alternate select register0 bit for given pin * This function sets the alternate select register0 bit to 1 for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_altsel0 (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL0_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL0_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_altsel0); /** * \fn int amazon_s_port_clear_altsel0 (int port, int pin, int module_id) * \brief Clear alternate select register0 bit for given pin * This function sets the alternate select register0 bit to 0 for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_altsel0 (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL0_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL0_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_altsel0); /** * \fn int amazon_s_port_set_altsel1 (int port, int pin, int module_id) * \brief Set alternate select register1 bit for given pin * This function sets the alternate select register1 bit to 1 for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_set_altsel1 (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL1_REG], reg); reg |= (1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL1_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_set_altsel1); /** * \fn int amazon_s_port_clear_altsel1 (int port, int pin, int module_id) * \brief Clear alternate select register1 bit for given pin * This function sets the alternate select register0 bit to 0 for the given pin. * * \param port Port number * \param pin Pin to be reserved * \param module_id Module ID to identify the owner of a pin * * \return EBUSY Pin used by another module. * EINVAL Invalid port or pin provided * OK OK * \ingroup AMAZON_S_GPIO_FUNCTIONS */ int amazon_s_port_clear_altsel1 (int port, int pin, int module_id) { u32 reg; if (port < 0 || pin < 0) return -EINVAL; if (port >= MAX_PORTS || pin >= PINS_PER_PORT) return -EINVAL; if (amazon_s_port_pin_usage[port][pin] != module_id) return -EBUSY; PORT_READ_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL1_REG], reg); reg &= ~(1 << pin); PORT_WRITE_REG (amazon_s_gpio_reg_base[port][AMAZON_S_PORT_ALTSEL1_REG], reg); return OK; } EXPORT_SYMBOL (amazon_s_port_clear_altsel1); /** * amazon_s_port_read_procmem - Create proc file output * @buf: Buffer to write the string to * @start: not used (Linux internal) * @offset: not used (Linux internal) * @count: not used (Linux internal) * @eof: Set to 1 when all data is stored in buffer * @data: not used (Linux internal) * * This function creates the output for the port proc file, by reading * all appropriate registers and displaying the content as a table. * * Return Value: * @len = Lenght of data in buffer */ static int amazon_s_port_read_procmem (char *buf, char **start, off_t offset, int count, int *eof, void *data) { long len = 0; int t = 0; u32 bit = 0; u32 reg = 0; int i; int j; static char *gpio_suffix_str[AMAZON_S_PORT_REG_MAX_IDX] = { "OUT", "IN", "DIR", "ALT0", "ALT1", "OD", "STO", "PUDS", "PUDE", }; len = sprintf (buf, "\nAmazon_S Port Settings\n"); len += sprintf (buf + len, " 3 2 1 0\n"); len += sprintf (buf + len, " 10987654321098765432109876543210\n"); len += sprintf (buf + len, "-----------------------------------------\n"); for (i = 0; i < AMAZON_S_PORT_REG_MAX_IDX; i++) { for (j = 0; j < MAX_PORTS; j++){ len += sprintf (buf + len, "\nP%d-%-4s: ", j, gpio_suffix_str[i]); /* There is no P3.STO */ if (amazon_s_gpio_reg_base[j][i] == NULL) continue; PORT_READ_REG (amazon_s_gpio_reg_base[j][i], reg); bit = 0x80000000; for (t = 0; t < 32; t++) { if ((reg & bit) > 0) len += sprintf (buf + len, "X"); else len += sprintf (buf + len, " "); bit = bit >> 1; } } } len += sprintf (buf + len, "\n\n"); if (offset >= len) { *start = buf; *eof = 1; return 0; } *start = buf + offset; if ((len -= offset) > count) return count; *eof = 1; return len; } static int amazon_s_port_open (struct inode *inode, struct file *filep) { return OK; } static int amazon_s_port_release (struct inode *inode, struct file *filelp) { return OK; } struct amazon_s_port_ioctl_parm_old { int port; /*!< GPIO port number 0 ~ 3 */ int pin; /*!< GPIO pin number 0 ~ 15 */ int value; /*!< value to be set */ }; #define AMAZON_S_PORT_IOCOD_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,0,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCPUDSEL_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,1,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCPUDEN_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,2,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCSTOFF_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,3,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCDIR_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,4,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCOUTPUT_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,5,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCINPUT_OLD _IOWR(AMAZON_S_PORT_IOC_MAGIC,6,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCALTSEL0_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,7,struct amazon_s_port_ioctl_parm_old) #define AMAZON_S_PORT_IOCALTSEL1_OLD _IOW( AMAZON_S_PORT_IOC_MAGIC,8,struct amazon_s_port_ioctl_parm_old) static int amazon_s_port_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int ret = 0; volatile struct amazon_s_port_ioctl_parm parm; if (_IOC_TYPE (cmd) != AMAZON_S_PORT_IOC_MAGIC) return -EINVAL; if (_IOC_DIR (cmd) & _IOC_WRITE) { if (!access_ok (VERIFY_READ, arg, sizeof (struct amazon_s_port_ioctl_parm))) return -EFAULT; ret = copy_from_user ((void *) &parm, (void *) arg, sizeof (struct amazon_s_port_ioctl_parm)); } if (_IOC_DIR (cmd) & _IOC_READ) { if (!access_ok (VERIFY_WRITE, arg, sizeof (struct amazon_s_port_ioctl_parm))) return -EFAULT; } if (down_trylock (&port_sem) != 0) return -EBUSY; switch (cmd) { case AMAZON_S_PORT_IOCOD: case AMAZON_S_PORT_IOCOD_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_open_drain, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_open_drain, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCPUDSEL: case AMAZON_S_PORT_IOCPUDSEL_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_pudsel, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_pudsel, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCPUDEN: case AMAZON_S_PORT_IOCPUDEN_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_puden, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_puden, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCSTOFF: case AMAZON_S_PORT_IOCSTOFF_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_stoff, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_stoff, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCDIR: case AMAZON_S_PORT_IOCDIR_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_dir_in, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_dir_out, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCOUTPUT: case AMAZON_S_PORT_IOCOUTPUT_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_output, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_output, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCALTSEL0: case AMAZON_S_PORT_IOCALTSEL0_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_altsel0, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_altsel0, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCALTSEL1: case AMAZON_S_PORT_IOCALTSEL1_OLD: if (parm.value == 0x00) { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_clear_altsel1, parm.module | 0x80000000); } else { PORT_IOC_CALL (ret, parm.port, parm.pin, amazon_s_port_set_altsel1, parm.module | 0x80000000); } break; case AMAZON_S_PORT_IOCINPUT: case AMAZON_S_PORT_IOCINPUT_OLD: ret = amazon_s_port_reserve_pin (parm.port, parm.pin, parm.module | 0x80000000); if (ret == 0) parm.value = amazon_s_port_get_input (parm.port, parm.pin, parm.module | 0x80000000); ret = amazon_s_port_free_pin (parm.port, parm.pin, parm.module | 0x80000000); copy_to_user ((void *) arg, (void *) &parm, sizeof (struct amazon_s_port_ioctl_parm)); break; default: ret = -EINVAL; } up (&port_sem); return ret; } static struct file_operations port_fops = { .open = amazon_s_port_open, .release = amazon_s_port_release, .ioctl = amazon_s_port_ioctl }; static inline void amazon_s_port_version(void) { printk(KERN_INFO "Infineon Technologies port driver version %s \n", IFX_PORT_DRV_VERSION); } /** * amazon_s_port_init - Initialize port structures * * This function initializes the internal data structures of the driver * and will create the proc file entry and device. * * Return Value: * @OK = OK */ static int __init amazon_s_port_init (void) { // int t = 0; // int i = 0; int err = 0; sema_init (&port_sem, 1); #if 0 /* Check ports for available pins */ for (t = 0; t < MAX_PORTS; t++) { for (i = 0; i < PINS_PER_PORT; i++) amazon_s_port_pin_usage[t][i] = PORT_AVAILABLE; } #endif /* register port device */ err = register_chrdev (IFX_PORT_MAJOR, "amazon_s-port", &port_fops); if (err != 0) { printk ("amazon_s-port: Error! Could not register port device. #%d\n", err); return err; } /* Create proc file */ create_proc_read_entry ("driver/amazon_s_port", 0, NULL, amazon_s_port_read_procmem, NULL); amazon_s_port_version(); return OK; } static void __exit amazon_s_port_exit (void) { unregister_chrdev (IFX_PORT_MAJOR, "amazon_s-port"); remove_proc_entry ("driver/amazon_s_port", NULL); } module_init (amazon_s_port_init); module_exit(amazon_s_port_exit); MODULE_LICENSE ("GPL"); MODULE_AUTHOR ("Bing-Tao.Xu@infineon.com"); MODULE_SUPPORTED_DEVICE ("Infineon Amazon_S"); MODULE_DESCRIPTION ("Infineon technologies GPIO device driver");