* intended to support multiple hosts.
*
* There are also two LED pins used sometimes as output-only GPIOs.
- *
- * FIXME code currently only handles the first IRQ line.
*/
return twl4030_i2c_write_u8(TWL4030_MODULE_GPIO, data, address);
}
+/*----------------------------------------------------------------------*/
+
+/*
+ * LED register offsets (use TWL4030_MODULE_{LED,PWMA,PWMB}))
+ * PWMs A and B are dedicated to LEDs A and B, respectively.
+ */
+
+#define TWL4030_LED_LEDEN 0x0
+
+/* LEDEN bits */
+#define LEDEN_LEDAON BIT(0)
+#define LEDEN_LEDBON BIT(1)
+#define LEDEN_LEDAEXT BIT(2)
+#define LEDEN_LEDBEXT BIT(3)
+#define LEDEN_LEDAPWM BIT(4)
+#define LEDEN_LEDBPWM BIT(5)
+#define LEDEN_PWM_LENGTHA BIT(6)
+#define LEDEN_PWM_LENGTHB BIT(7)
+
+#define TWL4030_PWMx_PWMxON 0x0
+#define TWL4030_PWMx_PWMxOFF 0x1
+
+#define PWMxON_LENGTH BIT(7)
+
+/*----------------------------------------------------------------------*/
+
/*
* To read a TWL4030 GPIO module register
*/
return (ret < 0) ? ret : data;
}
-/*
- * twl4030 GPIO request function
- */
-int twl4030_request_gpio(int gpio)
-{
- if (unlikely(gpio >= TWL4030_GPIO_MAX))
- return -EPERM;
+/*----------------------------------------------------------------------*/
- return gpio_request(twl_gpiochip.base + gpio, NULL);
-}
-EXPORT_SYMBOL(twl4030_request_gpio);
+static u8 cached_leden; /* protected by gpio_lock */
-/*
- * TWL4030 GPIO free module
+/* 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".
*/
-int twl4030_free_gpio(int gpio)
+static void twl4030_led_set_value(int led, int value)
{
- if (unlikely(gpio >= TWL4030_GPIO_MAX))
- return -EPERM;
+ u8 mask = LEDEN_LEDAON | LEDEN_LEDAPWM;
+ int status;
- gpio_free(twl_gpiochip.base + gpio);
- return 0;
+ if (led)
+ mask <<= 1;
+
+ mutex_lock(&gpio_lock);
+ if (value)
+ cached_leden &= ~mask;
+ else
+ cached_leden |= mask;
+ status = twl4030_i2c_write_u8(TWL4030_MODULE_LED, cached_leden,
+ TWL4030_LED_LEDEN);
+ mutex_unlock(&gpio_lock);
}
-EXPORT_SYMBOL(twl4030_free_gpio);
static int twl4030_set_gpio_direction(int gpio, int is_input)
{
return gpio_twl4030_write(base, d_msk);
}
-int twl4030_get_gpio_datain(int gpio)
+static int twl4030_get_gpio_datain(int gpio)
{
u8 d_bnk = gpio >> 3;
u8 d_off = gpio & 0x7;
return ret;
}
-EXPORT_SYMBOL(twl4030_get_gpio_datain);
/*
* Configure debounce timing value for a GPIO pin on TWL4030
}
EXPORT_SYMBOL(twl4030_set_gpio_debounce);
-#if 0
-/*
- * Configure Card detect for GPIO pin on TWL4030
- *
- * This means: VMMC1 or VMMC2 is enabled or disabled based
- * on the status of GPIO-0 or GPIO-1 pins (respectively).
- */
-int twl4030_set_gpio_card_detect(int gpio, int enable)
-{
- u8 reg = 0;
- u8 msk = (1 << gpio);
- int ret = 0;
-
- /* Only GPIO 0 or 1 can be used for CD feature.. */
- if (unlikely((gpio >= TWL4030_GPIO_MAX)
- || !(gpio_usage_count & BIT(gpio))
- || (gpio >= TWL4030_GPIO_MAX_CD))) {
- return -EPERM;
- }
-
- mutex_lock(&gpio_lock);
- ret = gpio_twl4030_read(REG_GPIO_CTRL);
- if (ret >= 0) {
- if (enable)
- reg = (u8) (ret | msk);
- else
- reg = (u8) (ret & ~msk);
-
- ret = gpio_twl4030_write(REG_GPIO_CTRL, reg);
- }
- mutex_unlock(&gpio_lock);
- return ret;
-}
-#endif
-
/*----------------------------------------------------------------------*/
static int twl_request(struct gpio_chip *chip, unsigned offset)
mutex_lock(&gpio_lock);
+ /* 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 module = TWL4030_MODULE_PWMA;
+
+ offset -= TWL4030_GPIO_MAX;
+ if (offset) {
+ ledclr_mask <<= 1;
+ module = TWL4030_MODULE_PWMB;
+ }
+
+ /* initialize PWM to always-drive */
+ status = twl4030_i2c_write_u8(module, 0x7f,
+ TWL4030_PWMx_PWMxOFF);
+ if (status < 0)
+ goto done;
+ status = twl4030_i2c_write_u8(module, 0x7f,
+ TWL4030_PWMx_PWMxON);
+ if (status < 0)
+ goto done;
+
+ /* init LED to not-driven (high) */
+ module = TWL4030_MODULE_LED;
+ status = twl4030_i2c_read_u8(module, &cached_leden,
+ TWL4030_LED_LEDEN);
+ if (status < 0)
+ goto done;
+ cached_leden &= ~ledclr_mask;
+ status = twl4030_i2c_write_u8(module, cached_leden,
+ TWL4030_LED_LEDEN);
+ if (status < 0)
+ goto done;
+
+ status = 0;
+ goto done;
+ }
+
/* on first use, turn GPIO module "on" */
- if (!gpio_usage_count)
- status = gpio_twl4030_write(REG_GPIO_CTRL,
- MASK_GPIO_CTRL_GPIO_ON);
+ if (!gpio_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 = chip->dev->platform_data;
+ value |= pdata->mmc_cd & 0x03;
+
+ status = gpio_twl4030_write(REG_GPIO_CTRL, value);
+ }
if (!status)
gpio_usage_count |= (0x1 << offset);
static void twl_free(struct gpio_chip *chip, unsigned offset)
{
+ if (offset >= TWL4030_GPIO_MAX) {
+ twl4030_led_set_value(offset - TWL4030_GPIO_MAX, 1);
+ return;
+ }
+
mutex_lock(&gpio_lock);
gpio_usage_count &= ~BIT(offset);
static int twl_direction_in(struct gpio_chip *chip, unsigned offset)
{
- return twl4030_set_gpio_direction(offset, 1);
+ return (offset < TWL4030_GPIO_MAX)
+ ? twl4030_set_gpio_direction(offset, 1)
+ : -EINVAL;
}
static int twl_get(struct gpio_chip *chip, unsigned offset)
{
- int status = twl4030_get_gpio_datain(offset);
+ int status = 0;
+ if (offset < TWL4030_GPIO_MAX)
+ status = twl4030_get_gpio_datain(offset);
+ else if (offset == TWL4030_GPIO_MAX)
+ status = cached_leden & LEDEN_LEDAON;
+ else
+ status = cached_leden & LEDEN_LEDBON;
return (status < 0) ? 0 : status;
}
static int twl_direction_out(struct gpio_chip *chip, unsigned offset, int value)
{
- twl4030_set_gpio_dataout(offset, value);
- return twl4030_set_gpio_direction(offset, 0);
+ if (offset < TWL4030_GPIO_MAX) {
+ twl4030_set_gpio_dataout(offset, value);
+ return twl4030_set_gpio_direction(offset, 0);
+ } else {
+ twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);
+ return 0;
+ }
}
static void twl_set(struct gpio_chip *chip, unsigned offset, int value)
{
- twl4030_set_gpio_dataout(offset, value);
+ if (offset < TWL4030_GPIO_MAX)
+ twl4030_set_gpio_dataout(offset, value);
+ else
+ twl4030_led_set_value(offset - TWL4030_GPIO_MAX, value);
}
static int twl_to_irq(struct gpio_chip *chip, unsigned offset)
{
- return twl4030_gpio_irq_base
+ return (twl4030_gpio_irq_base && (offset < TWL4030_GPIO_MAX))
? (twl4030_gpio_irq_base + offset)
: -EINVAL;
}
twl_gpiochip.ngpio = TWL4030_GPIO_MAX;
twl_gpiochip.dev = &pdev->dev;
+ /* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
+ * is (still) clear if use_leds is set.
+ */
+ if (pdata->use_leds)
+ twl_gpiochip.ngpio += 2;
+
ret = gpiochip_add(&twl_gpiochip);
if (ret < 0) {
dev_err(&pdev->dev,