/** * @brief AVM gpio module * @author AMY * @date 2015-2016 * * This controls GPIOs for AVM stuff. Eg. buttons, LEDs and USB power control. */ #include #include "mach/avm_gpio.h" #include "linux/avm_hw_config.h" #include "linux/avm_hw_config_def.h" #include #include #include #include #include #include #include #include #include #include #include #include static void (*qca_phy_mmd_write)(u32, u32, u16, u16, u16); static u16 (*qca_phy_mmd_read)(u32, u32, u16, u16); static void __iomem *msm_tlmm_regs; static bool is_ipq807x; static void dakota_pinctrl_print(unsigned int gpio_pin); static spinlock_t gpio_ctrl_lock; #define GPIO_NUM_MAX 70 #define SHIFT_GPIO_BASE 100 /**< AVM GPIO base for pins on shift registers */ #define PHY_GPIO_BASE 200 /**< AVM GPIO base for pins on PHY/MDIO */ #define PHY_NUM_LEDS 10 /**< Number of LEDs accessible from this module */ #define PWM_GPIO_BASE 600 /**< AVM GPIO base for pins on PWM */ #define PWM_GPIO_A 601 /**< AVM RGB LED GPIO 30-33 */ #define PWM_GPIO_B 602 /**< AVM RGB LED GPIO 64-67 */ #define PWM_NUM_LEDS 2 /**< AVM GPIO base for pins on PWM */ #define LED_MMD_NUM 7 /**< MMD register for LED control */ /** * @brief Kernel-API for AVM LED module */ enum _led_event { /* DUMMY DEFINITION */ LastEvent = 0 }; int (*led_event_action)(int a, enum _led_event b, unsigned int c); EXPORT_SYMBOL(led_event_action); static struct _avm_hw_config *get_gpio_config(const unsigned int gpio_num) { struct _avm_hw_config *config; config = avm_get_hw_config_table(); if (!config) return 0; /* error: no hardware config found! */ while (config->name) { if (gpio_num == config->value) return config; config++; } return 0; } /** * @brief Check if GPIO is inverted in AVM GPIO config * * @param gpio_num AVM GPIO number to check * @return Check result * @retval 0 if NOT inverted * @retval != 0 if inverted */ static unsigned int get_gpio_invert(const unsigned int gpio_num) { struct _avm_hw_config *config; config = get_gpio_config(gpio_num); if (!config) return 0; /* error: no hardware config found! */ return (config->param == AVM_DEF_HW_PARAM_GPIO_OUT_ACTIVE_LOW || config->param == AVM_DEF_HW_PARAM_GPIO_IN_ACTIVE_LOW); } /* The frequency range supported is 762Hz to 100MHz. */ #define USED_PERIOD_NS 666667 /** * @brief GPIO ctrl * * Set function of AVM GPIO pin. This is called from @ref avm_gpio_ctrl * * @param gpio_pin AVM GPIO pin to change * @param pin_mode The pin mode to set * @param pin_dir Pin I/O direction to set * @return Operation result * @retval GPIO_OK on success * @retval GPIO_FAIL on error */ #ifdef CONFIG_AVM_PWM #include extern int avm_pwm_request_channel(unsigned int channel, unsigned int duty_ns, unsigned int period_ns); extern void avm_pwm_configure_channel(unsigned int channel, unsigned int duty_ns, unsigned int period_ns); extern void avm_pwm_update(void); static unsigned int get_pwm_channel(const unsigned int gpio_num) { switch (gpio_num) { case 30: case 64: return 0; case 31: case 65: return 1; case 32: case 66: return 2; case 33: case 67: return 3; default: pr_err("Invalid GPIO [%d] for channel mapping\n", gpio_num); } return 0; } #define PWM_PERIOD 2550 #define PWM_DUTY 1784 void avm_set_rgb(const unsigned int gpio_num, uint32_t rgb) { struct _avm_hw_config *config; u8 r = (rgb >> 16); u8 g = (rgb >> 8); u8 b = (rgb >> 0); config = get_gpio_config(gpio_num); if (!config) { pr_err("No RGB GPIO mapping found\n"); return; } if (config->param == avm_hw_param_gpio_out_rgb_active_low) { r = 0xFF - r; g = 0xFF - g; b = 0xFF - b; } avm_pwm_configure_channel(get_pwm_channel(config->rgb_gpio_red), (r * PWM_PERIOD) / 255, PWM_PERIOD); avm_pwm_configure_channel(get_pwm_channel(config->rgb_gpio_green), (g * PWM_PERIOD) / 255, PWM_PERIOD); avm_pwm_configure_channel(get_pwm_channel(config->rgb_gpio_blue), (b * PWM_PERIOD) / 255, PWM_PERIOD); avm_pwm_update(); } #endif /* CONFIG_AVM_PWM */ int dakota_gpio_pinconfig(unsigned int gpio_pin, enum _hw_gpio_config param, unsigned int set); int dakota_gpio_ctrl(unsigned int gpio_pin, enum _hw_gpio_function pin_mode, enum _hw_gpio_direction pin_dir) { pr_debug("[%s] GPIO %d, DIR %d\n", __func__, gpio_pin, pin_dir); #ifdef CONFIG_AVM_PWM if (gpio_pin >= PWM_GPIO_BASE && gpio_pin < PWM_GPIO_BASE + GPIO_NUM_MAX) { if (gpio_pin == PWM_GPIO_A) { struct _avm_hw_config *config = get_gpio_config(gpio_pin); avm_pwm_request_channel(get_pwm_channel(config->rgb_gpio_red), PWM_PERIOD, PWM_PERIOD); avm_pwm_request_channel(get_pwm_channel(config->rgb_gpio_green), PWM_PERIOD, PWM_PERIOD); avm_pwm_request_channel(get_pwm_channel(config->rgb_gpio_blue), PWM_PERIOD, PWM_PERIOD); // set the remaining pwm channel. Otherwise nothing is set. // The channels are 0, 1, 2, 3. Substract the used channels from 6 gives the remaining channel avm_pwm_request_channel(6 - get_pwm_channel(config->rgb_gpio_red) - get_pwm_channel(config->rgb_gpio_green) - get_pwm_channel(config->rgb_gpio_blue), PWM_PERIOD, PWM_PERIOD); avm_pwm_update(); dakota_gpio_ctrl(30, FUNCTION_PINMUX3, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(30, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(30, PINCONF_PARAM_DRIVE_STRENGTH, 7); dakota_gpio_ctrl(31, FUNCTION_PINMUX3, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(31, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(31, PINCONF_PARAM_DRIVE_STRENGTH, 7); dakota_gpio_ctrl(32, FUNCTION_PINMUX3, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(32, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(32, PINCONF_PARAM_DRIVE_STRENGTH, 7); dakota_gpio_ctrl(33, FUNCTION_PINMUX2, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(33, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(33, PINCONF_PARAM_DRIVE_STRENGTH, 7); return GPIO_OK; } else if (gpio_pin == PWM_GPIO_B) { struct _avm_hw_config *config = get_gpio_config(gpio_pin); avm_pwm_request_channel(get_pwm_channel(config->rgb_gpio_red), PWM_PERIOD, PWM_PERIOD); avm_pwm_request_channel(get_pwm_channel(config->rgb_gpio_green), PWM_PERIOD, PWM_PERIOD); avm_pwm_request_channel(get_pwm_channel(config->rgb_gpio_blue), PWM_PERIOD, PWM_PERIOD); avm_pwm_request_channel(6 - get_pwm_channel(config->rgb_gpio_red) - get_pwm_channel(config->rgb_gpio_green) - get_pwm_channel(config->rgb_gpio_blue), PWM_PERIOD, PWM_PERIOD); avm_pwm_update(); dakota_gpio_ctrl(64, FUNCTION_PINMUX2, GPIO_OUTPUT_PIN); dakota_gpio_ctrl(65, FUNCTION_PINMUX2, GPIO_OUTPUT_PIN); dakota_gpio_ctrl(66, FUNCTION_PINMUX2, GPIO_OUTPUT_PIN); dakota_gpio_ctrl(67, FUNCTION_PINMUX2, GPIO_OUTPUT_PIN); return GPIO_OK; } else { gpio_pin -= PWM_GPIO_BASE; if (gpio_pin == 30) { avm_pwm_request_channel(0, PWM_PERIOD, PWM_PERIOD); dakota_gpio_ctrl(gpio_pin, FUNCTION_PINMUX3, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_DRIVE_STRENGTH, 7); avm_pwm_update(); return GPIO_OK; } else if (gpio_pin == 31) { avm_pwm_request_channel(1, PWM_PERIOD, PWM_PERIOD); dakota_gpio_ctrl(gpio_pin, FUNCTION_PINMUX3, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_DRIVE_STRENGTH, 7); avm_pwm_update(); return GPIO_OK; } else if (gpio_pin == 32) { avm_pwm_request_channel(2, PWM_PERIOD, PWM_PERIOD); dakota_gpio_ctrl(gpio_pin, FUNCTION_PINMUX3, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_DRIVE_STRENGTH, 7); avm_pwm_update(); return GPIO_OK; } else if (gpio_pin == 33) { avm_pwm_request_channel(3, PWM_PERIOD, PWM_PERIOD); dakota_gpio_ctrl(gpio_pin, FUNCTION_PINMUX2, GPIO_OUTPUT_PIN); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_HIGHPOWER, 0); dakota_gpio_pinconfig(gpio_pin, PINCONF_PARAM_DRIVE_STRENGTH, 7); avm_pwm_update(); return GPIO_OK; } pr_err("GPIO [%d] is not configured for pwm\n", gpio_pin); return GPIO_FAIL; } } else #endif /* CONFIG_AVM_PWM */ if (gpio_pin < GPIO_NUM_MAX) { void __iomem *reg_tlmm_cfg; u32 tlmm_cfg_val; unsigned long lflags; if (msm_tlmm_regs == NULL) { pr_info("[%s] gpio%u - msm_tlmm uninitialized!\n", __func__, gpio_pin); return GPIO_FAIL; } reg_tlmm_cfg = msm_tlmm_regs + gpio_pin * 0x1000; spin_lock_irqsave(&gpio_ctrl_lock, lflags); tlmm_cfg_val = ioread32(reg_tlmm_cfg); /*--- printk(KERN_ERR"[%s] gpio%d tlmm_cfg_val=%x\n", __func__, gpio_pin, tlmm_cfg_val); ---*/ /* Set FUNC_SEL */ if (pin_mode < FUNCTION_PIN_NOCHANGE) { tlmm_cfg_val = (tlmm_cfg_val & ~(0xf << 2)) | ((pin_mode & 0xf) << 2); } /* Set OE */ if (pin_dir == GPIO_INPUT_PIN) { tlmm_cfg_val &= ~(1 << 9); } else { tlmm_cfg_val |= (1 << 9); } iowrite32(tlmm_cfg_val, reg_tlmm_cfg); spin_unlock_irqrestore(&gpio_ctrl_lock, lflags); } else { pr_debug("[%s] --> Ignored!\n", __func__); } //dakota_pinctrl_print(gpio_pin); return GPIO_OK; } EXPORT_SYMBOL(dakota_gpio_ctrl); /** */ static void dakota_gpio_list(struct seq_file *seq, void *priv) { void __iomem *reg_tlmm_cfg; unsigned int gpio_pin, gpio_start = 0, gpio_end = 69, val; if (msm_tlmm_regs == NULL) { return; } if (priv) { gpio_start = (unsigned int)priv; gpio_end = gpio_start; if (gpio_start >= GPIO_NUM_MAX) { return; } } for (gpio_pin = gpio_start; gpio_pin <= gpio_end; gpio_pin++) { reg_tlmm_cfg = msm_tlmm_regs + gpio_pin * 0x1000; val = ioread32(reg_tlmm_cfg); if (is_ipq807x) { seq_printf(seq, "gpio%02u %s func=%x %s %s strength=0x%x val=%x (TLMM_GPIO_CFG=0x%08x)\n", gpio_pin, ((val >> 9) & 0x1) ? "OUT" : "IN ", ((val >> 2) & 0xF), ((val >> 0) & 0x3) == 0 ? " " : ((val >> 0) & 0x3) == 1 ? "PD" : ((val >> 0) & 0x3) == 2 ? "KP" : "PU", ((val >> 10) & 0x1) ? "HIHYS_EN" : " ", ((val >> 6) & 0x7), dakota_gpio_in_bit(gpio_pin), val); } else { seq_printf(seq, "gpio%02u %s func=%x %s %s %s %s strength=0x%x val=%x (TLMM_GPIO_CFG=0x%08x)\n", gpio_pin, ((val >> 9) & 0x1) ? "OUT" : "IN ", ((val >> 2) & 0xF), ((val >> 0) & 0x3) == 1 ? "PD" : ((val >> 0) & 0x3) == 2 ? "PU" : ((val >> 0) & 0x3) == 3 ? "? " : " ", ((val >> 12) & 0x1) ? "OD" : " ", ((val >> 13) & 0x3) == 0 ? " 10KΩ" : ((val >> 13) & 0x3) == 1 ? "1.5KΩ" : ((val >> 13) & 0x3) == 2 ? " 35KΩ" : " 20KΩ", ((val >> 11) & 0x3) ? "1.8V" : "3.3V", ((val >> 6) & 0x7), dakota_gpio_in_bit(gpio_pin), val); } } } /** */ static void dakota_pinctrl_print(unsigned int gpio_pin) { char txtbuf[256]; struct seq_file s; memset(&s, 0, sizeof(s)); txtbuf[0] = 0; s.buf = txtbuf; s.size = sizeof(txtbuf); dakota_gpio_list(&s, (void *)gpio_pin); pr_err("%s", txtbuf); } /** */ int dakota_gpio_pinconfig(unsigned int gpio_pin, enum _hw_gpio_config param, unsigned int set) { if (gpio_pin < GPIO_NUM_MAX) { void __iomem *reg_tlmm_cfg; u32 tlmm_cfg_val; unsigned long lflags; unsigned int mask; if (msm_tlmm_regs == NULL) { pr_info("[%s] gpio%u - msm_tlmm uninitialized!\n", __func__, gpio_pin); return GPIO_FAIL; } switch (param) { case PINCONF_PARAM_PULLDOWN: /** * Dakota (IPQ4019): * Only these GPIOs support this field: * GPIO0~GPIO9, GPIO14, GPIO16~GPIO19, * GPIO36~GPIO39, GPIO41~GPIO49, GPIO51~GPIO57, * GPIO60~GPIO69 */ mask = 0x3 << 0; set = (set & 0x1) << 0; break; case PINCONF_PARAM_PULLUP: if (is_ipq807x) { mask = 0x3 << 0; set = set ? mask : 0; } else { mask = 0x3 << 0; set = (set & 0x1) << 1; } break; case PINCONF_PARAM_OPEN_DRAIN: if (is_ipq807x) { pr_err("%s: PINCONF_PARAM_OPEN_DRAIN not supported (gpio%d)\n", __func__, gpio_pin); set = 0, mask = 0; break; } /** * Dakota (IPQ4019) * Only these GPIOs support this field: * GPIO6~GPIO7, GPIO10~GPIO13, GPIO15, GPIO20, * GPIO21, GPIO34, GPIO35, GPIO39, GPIO40, GPIO50, * GPIO52, GPIO53, GPIO58, GPIO59 */ mask = 0x1 << 12; set = (set & 0x1) << 12; break; case PINCONF_PARAM_HIHYS_EN: if (!is_ipq807x) { pr_err("%s: PINCONF_PARAM_HIGHPOWER not supported (gpio%d)\n", __func__, gpio_pin); set = 0, mask = 0; break; } mask = 0x1 << 10; set = (set & 0x1) << 10; break; case PINCONF_PARAM_HIGHPOWER: if (is_ipq807x) { pr_err("%s: PINCONF_PARAM_HIGHPOWER not supported (gpio%d)\n", __func__, gpio_pin); set = 0, mask = 0; break; } /** * Dakota (IPQ4019) * Only these GPIOs support this field: * GPIO22~GPIO33 * Choose high power supply for GPIO[n]. * 0: dvdd_hv=2.8 V/3.3 V * 1: dvdd_hv=1.8 V */ mask = 0x1 << 11; set = (set & 0x1) << 11; break; case PINCONF_PARAM_DRIVE_STRENGTH: /** * Controls the GPIO pad drive strength. This applies regardless of * the FUNC_SEL field selection. Almost all of them except * * IPQ807x: * GPIO38~GPIO41 are 3'h0 by default. Their default value of * GPIO38~GPIO41 are 3'h2. * 0x0: DRV_2_MA * (Sets the drive strength to 2mA. When in RFFE mode, sets output * cap to 5-26 pF) * 0x1: DRV_4_MA * (Sets the drive strength to 4mA. When in RFFE mode, sets output * cap to 27-45 pF) * 0x2: DRV_6_MA * (Sets the drive strength to 6mA. When in RFFE mode, sets output * cap to 46-60 pF) * 0x3: DRV_8_MA * (Sets the drive strength to 8mA. When in RFFE mode, sets output * cap to 61-79 pF) * 0x4: DRV_10_MA * (Sets the drive strength to 10mA. When in RFFE mode, it is a * don't care) * 0x5: DRV_12_MA * (Sets the drive strength to 12mA. When in RFFE mode, it is a * don't care) * 0x6: DRV_14_MA * (Sets the drive strength to 14mA. When in RFFE mode, it is a * don't care) * 0x7: DRV_16_MA * (Sets the drive strength to 16mA. When in RFFE mode, it is a * don't care) * * Dakota (IPQ4019): * For GPIO22~GPIO33: * vm=1&&drv=111: Type A (x1.5) * drv=011: Type B (x1,50ohm) * drv=001: Type C (x0.75) * drv=000: Type D (x0.5) * vm=0&&drv=111: Optional (x1.25) * For GPIO0~GPIO21,GPIO34~GPIO69: * 00: Highest drive capability * 01: Half of highest drive capability * 10&11: 1/4 of highest drive capability */ mask = 0x7 << 6; set = (set & 0x7) << 6; break; case PINCONF_PARAM_GPIO_PU_RES: if (is_ipq807x) { pr_err("%s: PINCONF_PARAM_GPIO_PU_RES not supported (gpio%d)\n", __func__, gpio_pin); set = 0, mask = 0; break; } /** * Only these GPIOs support this field: GPIO22~GPIO33 * Choose pull-up resistor value. * 00: Pull up resistance=10 KOhm * 01: Pull up resistance=1.5 KOhm * 10: Pull up resistance=35 KOhm * 11: Pull up resistance=20 KOhm */ mask = 0x3 << 13; set = (set & 0x3) << 13; break; case PINCONF_PARAM_NOCHANGE: default: set = 0, mask = 0; break; } reg_tlmm_cfg = msm_tlmm_regs + gpio_pin * 0x1000; spin_lock_irqsave(&gpio_ctrl_lock, lflags); tlmm_cfg_val = ioread32(reg_tlmm_cfg); /*--- printk(KERN_ERR"[%s] gpio%d tlmm_cfg_val=0x%x, mask=%x set=%x\n", __func__, gpio_pin, tlmm_cfg_val, mask, set); ---*/ tlmm_cfg_val &= ~mask; tlmm_cfg_val |= set; iowrite32(tlmm_cfg_val, reg_tlmm_cfg); spin_unlock_irqrestore(&gpio_ctrl_lock, lflags); } else { pr_debug("[%s] --> Ignored!\n", __func__); } return GPIO_OK; } EXPORT_SYMBOL(dakota_gpio_pinconfig); /** * ret: negval -> not found/no range/no match */ static int generic_gpio_param_parse(char *string, char *match, int maxval, char *matchstrg1, char *matchstrg2) { char *p = string; int ret = -1; p = strstr(string, match); if (p) { p += strlen(match); while (*p == ' ' || *p == '\t') p++; if (matchstrg1 && strncmp(p, matchstrg1, strlen(matchstrg1)) == 0) { ret = 0; } else if (matchstrg2 && strncmp(p, matchstrg2, strlen(matchstrg2)) == 0) { ret = 1; } else if (*p) { sscanf(p, "%d", &ret); if (ret > maxval) { ret = -1; } } } return ret; } /** */ static int dakota_gpio_set(char *string, void *priv __maybe_unused) { int gpio, dir, set, pd, pu, od, mux, strength, power, hihys; gpio = generic_gpio_param_parse(string, "gpio", 69, NULL, NULL); dir = generic_gpio_param_parse(string, "dir=", 0, "in", "out"); set = generic_gpio_param_parse(string, "set=", 1, NULL, NULL); pd = generic_gpio_param_parse(string, "pulldown=", 1, NULL, NULL); pu = generic_gpio_param_parse(string, "pullup=", 1, NULL, NULL); od = generic_gpio_param_parse(string, "od=", 1, NULL, NULL); strength = generic_gpio_param_parse(string, "strength=", 7, NULL, NULL); hihys = generic_gpio_param_parse(string, "hihys=", 1, NULL, NULL); mux = generic_gpio_param_parse(string, "mux=", 15, NULL, NULL); if (mux == -1) { mux = generic_gpio_param_parse(string, "func=", 15, NULL, NULL); } power = generic_gpio_param_parse(string, "power=", 1, NULL, NULL); if ((gpio < 0) || (strstr(string, "help"))) { pr_err("use: gpio dir= set=<0|1> mux=<0..15> pullup=<0|1> pulldown=<0|1> od=<0|1> strength=<0..7> power=<0|1> hihys=<0|1>\n"); return 0; } if (dir >= 0 || mux >= 0) { dakota_gpio_ctrl(gpio, mux, dir); } if (set >= 0) { dakota_gpio_out_bit(gpio, set); } if (hihys >= 0) { dakota_gpio_pinconfig(gpio, PINCONF_PARAM_HIHYS_EN, hihys); } if (pd >= 0) { dakota_gpio_pinconfig(gpio, PINCONF_PARAM_PULLDOWN, pd); } if (pu >= 0) { dakota_gpio_pinconfig(gpio, PINCONF_PARAM_PULLUP, pu); } if (od >= 0) { dakota_gpio_pinconfig(gpio, PINCONF_PARAM_OPEN_DRAIN, od); } if (strength >= 0) { dakota_gpio_pinconfig(gpio, PINCONF_PARAM_DRIVE_STRENGTH, strength); } if (power >= 0) { dakota_gpio_pinconfig(gpio, PINCONF_PARAM_HIGHPOWER, power); } dakota_pinctrl_print(gpio); return 0; } static struct proc_dir_entry *gpioprocdir; /** */ static int __init avm_gpioproc_init(void) { #define PROC_GPIODIR "avm/gpio" gpioprocdir = proc_mkdir(PROC_GPIODIR, NULL); if (gpioprocdir == NULL) { return 0; } add_simple_proc_file("avm/gpio/list", NULL, dakota_gpio_list, NULL); add_simple_proc_file("avm/gpio/set", dakota_gpio_set, NULL, NULL); return 0; } late_initcall(avm_gpioproc_init); /** * @brief Check if qca mmd access functions got registered * * @retval TRUE if registered * @retval FALSE if mmd functions not registered yet */ static bool qca_phy_mmd_init_done(void) { return (qca_phy_mmd_write && qca_phy_mmd_read); } /** * @brief PHY mmd register write * * Write to PHY mmd register. * This is basically a reimplementation of write_mmd_indirect available in * later Kernels. * * @todo Replace this with mmd functions from phy.h as they become available. * * @param phy ID of PHY to write to * @param mmd_num mmd number to write to * @param reg_id register address in mmd * @param reg_val Value to write */ static void mmd_write(uint32_t phy, uint32_t mmd_num, uint32_t reg_id, uint32_t reg_val) { pr_debug("[%s] phy %d, mmd_num %d, reg_id 0x%x, reg_val: 0x%x\n", __func__, phy, mmd_num, reg_id, reg_val); if (qca_phy_mmd_write) qca_phy_mmd_write(0, phy, mmd_num, reg_id, reg_val); } /** * @brief PHY mmd register read * * Read from PHY mmd register. * This is basically a reimplementation of read_mmd_indirect available in * later Kernels. * * @todo Replace this with mmd functions from phy.h as they become available. * * @param phy ID of PHY to read from * @param mmd_num mmd number to read from * @param reg_id register address in mmd * @return Value read from PHY mmd */ static int mmd_read(uint32_t phy, uint32_t mmd_num, uint32_t reg_id) { if (qca_phy_mmd_read) return qca_phy_mmd_read(0, phy, mmd_num, reg_id); return 0; } /** Worker thread used to do actual GPIO access */ static struct task_struct *gpio_worker_thread; /** * @brief Register functions to call for phy mmd access * * This is called at the end of qca ssdk_init. It registers functions to use * to access PHY mmd registers with a common lock held. * * @param write pointer to phy mmd write function. See @ref qca_phy_mmd_write * @param read pointer to phy mmd read function. See @ref qca_phy_mmd_read */ void avmgpio_register_phy_mmd(void *write, void *read) { uint32_t gpio_pin; uint32_t value; uint32_t addr; uint32_t phy_id; pr_debug("[%s] write=%pS; read=%pS\n", __func__, write, read); qca_phy_mmd_write = write; /* TODO: Weitere Ports initialisieren? */ /* LEDs initialisieren (aus) */ if (write) { for (gpio_pin = 0; gpio_pin < PHY_NUM_LEDS; ++gpio_pin) { addr = 0x8074 + ((gpio_pin & 1) << 1); phy_id = (gpio_pin >> 1); pr_debug("LED %d, v %d (addr=0x%x, phy_id=%d)\n", gpio_pin, value, addr, phy_id); mmd_write(phy_id, LED_MMD_NUM, addr, 0); mmd_write(phy_id, LED_MMD_NUM, addr + 1, (1 << 15) | (get_gpio_invert(gpio_pin + PHY_GPIO_BASE) ? (1 << 13) : 0)); } } qca_phy_mmd_read = read; // Wake up wake_up_process(gpio_worker_thread); } EXPORT_SYMBOL(avmgpio_register_phy_mmd); void avmgpio_register_msm_pinctrl(void __iomem *regs) { msm_tlmm_regs = regs; is_ipq807x = cpu_is_ipq807x(); pr_debug("%s: %d IPQ%s\n", __func__, read_ipq_cpu_type(), is_ipq807x ? "807x" : "4019"); } EXPORT_SYMBOL(avmgpio_register_msm_pinctrl); /** Lock for getting elements from AVM GPIO command stack */ static DEFINE_MUTEX(gpio_pop_mutex); /** Lock for putting elements on AVM GPIO command stack */ static DEFINE_SPINLOCK(gpio_push_lock); /** Element on AVM GPIO command queue */ struct led_cmd { unsigned int led:4; /**< GPIO/LED number */ unsigned int value:1; /**< GPIO output value to set */ }; /** * @brief AVM GPIO command queue * * This is used to queue switch commands and process them later in a separate * thread set up in @ref gpio_worker_thread. */ static struct led_cmd led_cmd_que[32]; static unsigned int led_cmd_q_head; /**< AVM GPIO command queue head index */ static unsigned int led_cmd_q_tail; /**< AVM GPIO command queue tail index */ /** * @brief Push command to set AVM GPIO to command queue * * @param led GPIO/LED number (0..9) * @param value to set (0=off, 1=on) * @return Operation result * @retval 0 on success * @retval 1 on error */ static int push_led_cmd(unsigned int led, unsigned int value) { int curr_head, next_head; unsigned long flags; spin_lock_irqsave(&gpio_push_lock, flags); curr_head = led_cmd_q_head; next_head = curr_head + 1; if (next_head >= ARRAY_SIZE(led_cmd_que)) next_head = 0; if (next_head == led_cmd_q_tail) { pr_err("Push led queue is full\n"); spin_unlock_irqrestore(&gpio_push_lock, flags); return 1; } led_cmd_que[curr_head].led = led & 15; led_cmd_que[curr_head].value = value ? 1 : 0; led_cmd_q_head = next_head; spin_unlock_irqrestore(&gpio_push_lock, flags); wake_up_process(gpio_worker_thread); return 0; } /** * @brief GPIO worker thread * * This thread carries out commands in AVM GPIO/LED command queue. * When elements are queued up in @ref push_led_cmd, this thread is woken up. * * @note Unloading the mdio driver will likely crash this function/thread. * * @param data Pointer to data structure - not used * @return Exit code - But this function never returns... */ static int gpio_worker_fn(void *data) { uint32_t gpio_pin; uint32_t value; uint32_t next_tail, curr_tail; uint32_t addr; uint32_t phy_id; /*--- int i; ---*/ /*--- static int gpio_last_values = 0; ---*/ pr_debug("[%s] Started.\n", __func__); while (1) { if (qca_phy_mmd_init_done()) { while (led_cmd_q_head != led_cmd_q_tail) { mutex_lock(&gpio_pop_mutex); curr_tail = led_cmd_q_tail; next_tail = curr_tail + 1; if (next_tail >= ARRAY_SIZE(led_cmd_que)) next_tail = 0; led_cmd_q_tail = next_tail; mutex_unlock(&gpio_pop_mutex); gpio_pin = led_cmd_que[curr_tail].led; value = led_cmd_que[curr_tail].value; #if 0 /*--- Das hier ist für den Fall, dass stets ALLE LEDs geupdated werden sollen. ---*/ if (value) gpio_last_values |= (1 << gpio_pin); else gpio_last_values &= ~(1 << gpio_pin); for (i = 0; i < PHY_NUM_LEDS; ++i) { addr = 0x8074 + ((i & 1) << 1); // 100N oder 1000N? phy_id = (i >> 1); // PHY-NR // mmd_write(phy_id, LED_MMD_NUM, addr, 0); mmd_write(phy_id, LED_MMD_NUM, addr + 1, (1 << 15) | (!(gpio_last_values & (1 << i)) ? (1 << 13) : 0)); } #endif addr = 0x8074 + ((gpio_pin & 1) << 1); // 100N oder 1000N? phy_id = (gpio_pin >> 1); // PHY-NR pr_debug ("LED %d, v %d (addr=0x%x, phy_id=%d)\n", gpio_pin, value, addr, phy_id); mmd_write(phy_id, LED_MMD_NUM, addr + 1, (1 << 15) | (value ? (1 << 13) : 0)); } } else { pr_debug("phy not ready\n"); } /*--- pr_debug("[%s] Going to sleep...\n", __func__); ---*/ set_current_state(TASK_INTERRUPTIBLE); schedule(); /*--- pr_debug("[%s] Waking up...\n", __func__); ---*/ } return 0; } /** * @brief Set GPIO pin output value (asynchonous) * * This is called by @ref avm_gpio_out_bit. This queues an element in AVM * GPIO/LED command queue. Actual GPIO switching is carried out later in * the worker thread, * * @param gpio_pin AVM GPIO pin to set * @param value Value to set pin to (0 = off) * @return Operation result * @retval GPIO_OK on success * @retval GPIO_FAIL on error */ int dakota_gpio_out_bit(unsigned int gpio_pin, int value) { int oval = value; value = get_gpio_invert(gpio_pin) ? !value : value; pr_debug("[%s] GPIO %d, VAL %d, invval %d\n", __func__, gpio_pin, oval, value); #ifdef CONFIG_AVM_PWM if (gpio_pin >= PWM_GPIO_BASE && gpio_pin < PWM_GPIO_BASE + GPIO_NUM_MAX) { struct _avm_hw_config *config = get_gpio_config(gpio_pin); if (gpio_pin == PWM_GPIO_A || gpio_pin == PWM_GPIO_B) { avm_set_rgb(gpio_pin, oval); return GPIO_OK; } gpio_pin -= PWM_GPIO_BASE; if (config->value_overwrite != -1) { if ((config->param == avm_hw_param_gpio_out_active_low && (oval & 0xff) != 255) || (config->param == avm_hw_param_gpio_out_active_high && oval)) { oval = config->value_overwrite; } } if (config->param != avm_hw_param_gpio_out_active_low) { oval = 0xFF - oval; } avm_pwm_configure_channel(get_pwm_channel(gpio_pin), ((oval & 0xFF) * PWM_PERIOD) / 255, PWM_PERIOD); avm_pwm_update(); return GPIO_OK; } #endif /* CONFIG_AVM_PWM */ if (gpio_pin >= PHY_GPIO_BASE) { gpio_pin -= PHY_GPIO_BASE; if (gpio_pin >= PHY_NUM_LEDS) { pr_err("[%s] gpio_pin = %d out of range - max = %d\n", __func__, gpio_pin, PHY_NUM_LEDS); return GPIO_FAIL; } push_led_cmd(gpio_pin, value); return GPIO_OK; } else if (gpio_pin < GPIO_NUM_MAX) { void __iomem *reg; if (msm_tlmm_regs == NULL) { pr_info("[%s] gpio%u - msm_tlmm uninitialized!\n", __func__, gpio_pin); return GPIO_FAIL; } reg = msm_tlmm_regs + gpio_pin * 0x1000 + 4; iowrite32(value ? 2 : 0, reg); /*--- gpio_set_value(gpio_pin, value); ---*/ return GPIO_OK; } return GPIO_FAIL; } EXPORT_SYMBOL(dakota_gpio_out_bit); /** * @brief Set GPIO pin output value (synchronous) * * This is called by @ref avm_gpio_out_bit_no_sched. GPIO switching is done * synchronously in this function. * * @note This function MUST NOT be called from interrupt context. This is * a requirement for using the mii bus functions. * * @param gpio_pin AVM GPIO pin to set * @param value Value to set pin to (0 = off) * @return Operation result * @retval GPIO_OK on success * @retval GPIO_FAIL on error */ int dakota_gpio_out_bit_no_sched(unsigned int gpio_pin, int value) { return dakota_gpio_out_bit(gpio_pin, value); } EXPORT_SYMBOL(dakota_gpio_out_bit_no_sched); /** * @brief Read AVM GPIO pin input value synchronously * * This is called by @ref avm_gpio_in_bit. It synchronously reads the input * value of an AVM GPIO pin. * * @note This function MUST NOT be called from interrupt context. This is * a requirement for using the mii bus functions. * * @note For PHY output ports this reads the currently set output value * * @param gpio_pin AVM GPIO pin to set * @return AVM GPIO pin value */ int dakota_gpio_in_bit(unsigned int gpio_pin) { int retval = 0; if (gpio_pin >= PHY_GPIO_BASE) { int phy_id; int addr; gpio_pin -= PHY_GPIO_BASE; if (gpio_pin >= PHY_NUM_LEDS) return (GPIO_FAIL); addr = 0x8074 + ((gpio_pin & 1) << 1); /* 100N oder 1000N? */ phy_id = (gpio_pin >> 1); /* PHY-NR */ retval = (mmd_read(phy_id, LED_MMD_NUM, addr + 1) >> 13) & 1; } else if (gpio_pin < GPIO_NUM_MAX) { /*--- retval = gpio_get_value(gpio_pin); ---*/ void __iomem *reg; if (msm_tlmm_regs == NULL) { pr_info("[%s] gpio%u - msm_tlmm uninitialized!\n", __func__, gpio_pin); return 0; } reg = msm_tlmm_regs + gpio_pin * 0x1000 + 4; retval = (ioread32(reg) & 1) ? 1 : 0; } pr_debug("[%s] GPIO %d; value %d\n", __func__, gpio_pin, retval); return get_gpio_invert(gpio_pin) ? !retval : retval; } EXPORT_SYMBOL(dakota_gpio_in_bit); void __iomem *dakota_gpio_get_membase(unsigned int gpio_pin) { void __iomem *reg = NULL; if (msm_tlmm_regs == NULL) { pr_info("[%s] gpio%u - msm_tlmm uninitialized!\n", __func__, gpio_pin); return reg; } if (gpio_pin < GPIO_NUM_MAX) { reg = msm_tlmm_regs + gpio_pin * 0x1000; } return reg; } EXPORT_SYMBOL(dakota_gpio_get_membase); /** * @brief Init call for this module * * Gets a ref to the AVM GPIO configuration and reserves "normal" GPIOs (GPIO * num < 100). Also creates the worker thread for @ref gpio_worker_fn * * @return Operation result * @retval 0 on success * @retval -1 on error */ int avm_gpio_init(void) { struct _avm_hw_config *config; pr_debug("[%s] called.\n", __func__); spin_lock_init(&gpio_ctrl_lock); config = avm_get_hw_config_table(); if (!config) { pr_err("[%s] No hardware config found!\n", __func__); return -1; /* error: no hardware config found! */ } /* GPIOs reservieren */ while (config->name) { if (config->value < 100) { int result; result = gpio_request(config->value, config->name); if (result) pr_err("[%s] Request GPIO 0x%08x as \"%s\" failed with result %d\n", __func__, config->value, config->name, result); pr_debug("[%s] Request GPIO 0x%08x as \"%s\" with result %d\n", __func__, config->value, config->name, result); } config++; } gpio_worker_thread = kthread_create(gpio_worker_fn, NULL, "avm_gpio_worker"); /*--- wake_up_process(gpio_worker_thread); ---*/ return 0; } device_initcall(avm_gpio_init); /** * @brief Get IRQ associated to GPIO * * @note Uses a CONFIG_GPIOLIB function * * @param gpio GPIO number to get IRQ for * @return IRQ number */ unsigned int avm_get_irq_for_gpio(unsigned int gpio) { return gpio_to_irq(gpio); } EXPORT_SYMBOL(avm_get_irq_for_gpio); /** * @brief Get GPIO that is connected with an IRQ * * @note The hwirq field of the irq_desc struct associated to * the IRQ is the same as the GPIO number. * This may not be the case on every platform. * Tested: DAKOTA * * @param irq IRQ number to get GPIO for * @return GPIO number */ unsigned int avm_get_gpio_for_irq(unsigned int irq) { struct irq_desc *irq_desc = irq_to_desc(irq); if (irq_desc) { return irq_desc->irq_data.hwirq; } pr_err("[%s] Unable to convert irq (%u) to gpio nr\n", __func__, irq); return 0; } EXPORT_SYMBOL(avm_get_gpio_for_irq);