/* * The GPIO "subchip" supports 18 GPIOs which can be configured as * inputs or outputs, with pullups or pulldowns on each pin. Each * GPIO can trigger interrupts on either or both edges. * * GPIO interrupts can be fed to either of two IRQ lines; this is * intended to support multiple hosts. * * There are also two LED pins used sometimes as output-only GPIOs.
*/
/* genirq interfaces are not available to modules */ #ifdef MODULE #define is_module() true #else #define is_module() false #endif
/* The LED lines are open drain outputs ... a FET pulls to GND, so an * external pullup is needed. We could also expose the integrated PWM * as a LED brightness control; we initialize it as "always on".
*/ staticint twl4030_led_set_value(int led, int value)
{
u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM;
if (led)
mask <<= 1;
if (value)
cached_leden &= ~mask; else
cached_leden |= mask;
staticint twl_request(struct gpio_chip *chip, unsigned offset)
{ struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); int status = 0;
mutex_lock(&priv->mutex);
/* Support the two LED outputs as output-only GPIOs. */ if (offset >= TWL4030_GPIO_MAX) {
u8 ledclr_mask = LEDEN_LEDAON | LEDEN_LEDAEXT
| LEDEN_LEDAPWM | LEDEN_PWM_LENGTHA;
u8 reg = TWL4030_PWMAON_REG;
/* initialize PWM to always-drive */ /* Configure PWM OFF register first */
status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg + 1); if (status < 0) goto done;
/* Followed by PWM ON register */
status = twl_i2c_write_u8(TWL4030_MODULE_LED, 0x7f, reg); if (status < 0) goto done;
/* init LED to not-driven (high) */
status = twl_i2c_read_u8(TWL4030_MODULE_LED, &cached_leden,
TWL4030_LED_LEDEN_REG); if (status < 0) goto done;
cached_leden &= ~ledclr_mask;
status = twl_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
TWL4030_LED_LEDEN_REG); if (status < 0) goto done;
status = 0; goto done;
}
/* on first use, turn GPIO module "on" */ if (!priv->usage_count) { struct twl4030_gpio_platform_data *pdata;
u8 value = MASK_GPIO_CTRL_GPIO_ON;
/* optionally have the first two GPIOs switch vMMC1 * and vMMC2 power supplies based on card presence.
*/
pdata = dev_get_platdata(chip->parent); if (pdata)
value |= pdata->mmc_cd & 0x03;
status = gpio_twl4030_write(REG_GPIO_CTRL, value);
}
done: if (!status)
priv->usage_count |= BIT(offset);
mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX)
ret = twl4030_set_gpio_direction(offset, 1); else
ret = -EINVAL; /* LED outputs can't be set as input */
if (!ret)
priv->direction &= ~BIT(offset);
mutex_unlock(&priv->mutex);
return ret;
}
staticint twl_get(struct gpio_chip *chip, unsigned offset)
{ struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); int ret; int status = 0;
mutex_lock(&priv->mutex); if (!(priv->usage_count & BIT(offset))) {
ret = -EPERM; goto out;
}
if (priv->direction & BIT(offset))
status = priv->out_state & BIT(offset); else
status = twl4030_get_gpio_datain(offset);
ret = (status < 0) ? status : !!status;
out:
mutex_unlock(&priv->mutex); return ret;
}
staticint twl_set(struct gpio_chip *chip, unsignedint offset, int value)
{ struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); int ret;
mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX)
ret = twl4030_set_gpio_dataout(offset, value); else
ret = twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);
if (value)
priv->out_state |= BIT(offset); else
priv->out_state &= ~BIT(offset);
mutex_unlock(&priv->mutex);
return ret;
}
staticint twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
{ struct gpio_twl4030_priv *priv = gpiochip_get_data(chip); int ret = 0;
mutex_lock(&priv->mutex); if (offset < TWL4030_GPIO_MAX) {
ret = twl4030_set_gpio_direction(offset, 0); if (ret) {
mutex_unlock(&priv->mutex); return ret;
}
}
/* * LED gpios i.e. offset >= TWL4030_GPIO_MAX are always output
*/
/* For most pins, a pulldown was enabled by default. * We should have data that's specific to this board.
*/ for (gpio_bit = 1, i = 0; i < 5; i++) {
u8 bit_mask; unsigned j;
pdata = of_gpio_twl4030(&pdev->dev); if (pdata == NULL) {
dev_err(&pdev->dev, "Platform data is missing\n"); return -ENXIO;
}
/* * NOTE: boards may waste power if they don't set pullups * and pulldowns correctly ... default for non-ULPI pins is * pulldown, and some other pins may have external pullups * or pulldowns. Careful!
*/
ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); if (ret)
dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
pdata->pullups, pdata->pulldowns, ret);
ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); if (ret)
dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
pdata->debounce, pdata->mmc_cd, ret);
/* * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, * is (still) clear if use_leds is set.
*/ if (pdata->use_leds)
priv->gpio_chip.ngpio += 2;
ret = devm_gpiochip_add_data(&pdev->dev, &priv->gpio_chip, priv); if (ret < 0) {
dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
priv->gpio_chip.ngpio = 0; return ret;
}
/* * Special quirk for the OMAP3 to hog and export a WLAN power * GPIO.
*/ if (IS_ENABLED(CONFIG_ARCH_OMAP3) &&
of_machine_is_compatible("compulab,omap3-sbc-t3730")) { struct gpio_desc *d;
d = gpiochip_request_own_desc(&priv->gpio_chip,
2, "wlan pwr",
GPIO_ACTIVE_HIGH,
GPIOD_OUT_HIGH); if (IS_ERR(d)) return dev_err_probe(&pdev->dev, PTR_ERR(d), "unable to hog wlan pwr GPIO\n");
gpiod_export(d, 0);
ret = devm_add_action_or_reset(&pdev->dev, gpio_twl4030_power_off_action, d); if (ret) return dev_err_probe(&pdev->dev, ret, "failed to install power off handler\n");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.