diff options
Diffstat (limited to 'drivers/mfd')
65 files changed, 7603 insertions, 2419 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 20895e7..011cb6c 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -17,230 +17,138 @@ #include <linux/platform_device.h> #include <linux/mfd/core.h> #include <linux/mfd/88pm860x.h> +#include <linux/regulator/machine.h> #define INT_STATUS_NUM 3 -char pm860x_backlight_name[][MFD_NAME_SIZE] = { - "backlight-0", - "backlight-1", - "backlight-2", +static struct resource bk_resources[] __initdata = { + {PM8606_BACKLIGHT1, PM8606_BACKLIGHT1, "backlight-0", IORESOURCE_IO,}, + {PM8606_BACKLIGHT2, PM8606_BACKLIGHT2, "backlight-1", IORESOURCE_IO,}, + {PM8606_BACKLIGHT3, PM8606_BACKLIGHT3, "backlight-2", IORESOURCE_IO,}, }; -EXPORT_SYMBOL(pm860x_backlight_name); - -char pm860x_led_name[][MFD_NAME_SIZE] = { - "led0-red", - "led0-green", - "led0-blue", - "led1-red", - "led1-green", - "led1-blue", -}; -EXPORT_SYMBOL(pm860x_led_name); - -#define PM8606_BACKLIGHT_RESOURCE(_i, _x) \ -{ \ - .name = pm860x_backlight_name[_i], \ - .start = PM8606_##_x, \ - .end = PM8606_##_x, \ - .flags = IORESOURCE_IO, \ -} -static struct resource backlight_resources[] = { - PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT1, WLED1A), - PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT2, WLED2A), - PM8606_BACKLIGHT_RESOURCE(PM8606_BACKLIGHT3, WLED3A), +static struct resource led_resources[] __initdata = { + {PM8606_LED1_RED, PM8606_LED1_RED, "led0-red", IORESOURCE_IO,}, + {PM8606_LED1_GREEN, PM8606_LED1_GREEN, "led0-green", IORESOURCE_IO,}, + {PM8606_LED1_BLUE, PM8606_LED1_BLUE, "led0-blue", IORESOURCE_IO,}, + {PM8606_LED2_RED, PM8606_LED2_RED, "led1-red", IORESOURCE_IO,}, + {PM8606_LED2_GREEN, PM8606_LED2_GREEN, "led1-green", IORESOURCE_IO,}, + {PM8606_LED2_BLUE, PM8606_LED2_BLUE, "led1-blue", IORESOURCE_IO,}, }; -#define PM8606_BACKLIGHT_DEVS(_i) \ -{ \ - .name = "88pm860x-backlight", \ - .num_resources = 1, \ - .resources = &backlight_resources[_i], \ - .id = _i, \ -} - -static struct mfd_cell backlight_devs[] = { - PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT1), - PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT2), - PM8606_BACKLIGHT_DEVS(PM8606_BACKLIGHT3), +static struct resource regulator_resources[] __initdata = { + {PM8607_ID_BUCK1, PM8607_ID_BUCK1, "buck-1", IORESOURCE_IO,}, + {PM8607_ID_BUCK2, PM8607_ID_BUCK2, "buck-2", IORESOURCE_IO,}, + {PM8607_ID_BUCK3, PM8607_ID_BUCK3, "buck-3", IORESOURCE_IO,}, + {PM8607_ID_LDO1, PM8607_ID_LDO1, "ldo-01", IORESOURCE_IO,}, + {PM8607_ID_LDO2, PM8607_ID_LDO2, "ldo-02", IORESOURCE_IO,}, + {PM8607_ID_LDO3, PM8607_ID_LDO3, "ldo-03", IORESOURCE_IO,}, + {PM8607_ID_LDO4, PM8607_ID_LDO4, "ldo-04", IORESOURCE_IO,}, + {PM8607_ID_LDO5, PM8607_ID_LDO5, "ldo-05", IORESOURCE_IO,}, + {PM8607_ID_LDO6, PM8607_ID_LDO6, "ldo-06", IORESOURCE_IO,}, + {PM8607_ID_LDO7, PM8607_ID_LDO7, "ldo-07", IORESOURCE_IO,}, + {PM8607_ID_LDO8, PM8607_ID_LDO8, "ldo-08", IORESOURCE_IO,}, + {PM8607_ID_LDO9, PM8607_ID_LDO9, "ldo-09", IORESOURCE_IO,}, + {PM8607_ID_LDO10, PM8607_ID_LDO10, "ldo-10", IORESOURCE_IO,}, + {PM8607_ID_LDO11, PM8607_ID_LDO11, "ldo-11", IORESOURCE_IO,}, + {PM8607_ID_LDO12, PM8607_ID_LDO12, "ldo-12", IORESOURCE_IO,}, + {PM8607_ID_LDO13, PM8607_ID_LDO13, "ldo-13", IORESOURCE_IO,}, + {PM8607_ID_LDO14, PM8607_ID_LDO14, "ldo-14", IORESOURCE_IO,}, + {PM8607_ID_LDO15, PM8607_ID_LDO15, "ldo-15", IORESOURCE_IO,}, }; -#define PM8606_LED_RESOURCE(_i, _x) \ -{ \ - .name = pm860x_led_name[_i], \ - .start = PM8606_##_x, \ - .end = PM8606_##_x, \ - .flags = IORESOURCE_IO, \ -} - -static struct resource led_resources[] = { - PM8606_LED_RESOURCE(PM8606_LED1_RED, RGB1B), - PM8606_LED_RESOURCE(PM8606_LED1_GREEN, RGB1C), - PM8606_LED_RESOURCE(PM8606_LED1_BLUE, RGB1D), - PM8606_LED_RESOURCE(PM8606_LED2_RED, RGB2B), - PM8606_LED_RESOURCE(PM8606_LED2_GREEN, RGB2C), - PM8606_LED_RESOURCE(PM8606_LED2_BLUE, RGB2D), +static struct resource touch_resources[] __initdata = { + {PM8607_IRQ_PEN, PM8607_IRQ_PEN, "touch", IORESOURCE_IRQ,}, }; -#define PM8606_LED_DEVS(_i) \ -{ \ - .name = "88pm860x-led", \ - .num_resources = 1, \ - .resources = &led_resources[_i], \ - .id = _i, \ -} - -static struct mfd_cell led_devs[] = { - PM8606_LED_DEVS(PM8606_LED1_RED), - PM8606_LED_DEVS(PM8606_LED1_GREEN), - PM8606_LED_DEVS(PM8606_LED1_BLUE), - PM8606_LED_DEVS(PM8606_LED2_RED), - PM8606_LED_DEVS(PM8606_LED2_GREEN), - PM8606_LED_DEVS(PM8606_LED2_BLUE), +static struct resource onkey_resources[] __initdata = { + {PM8607_IRQ_ONKEY, PM8607_IRQ_ONKEY, "onkey", IORESOURCE_IRQ,}, }; -static struct resource touch_resources[] = { - { - .start = PM8607_IRQ_PEN, - .end = PM8607_IRQ_PEN, - .flags = IORESOURCE_IRQ, - }, +static struct resource codec_resources[] __initdata = { + /* Headset microphone insertion or removal */ + {PM8607_IRQ_MICIN, PM8607_IRQ_MICIN, "micin", IORESOURCE_IRQ,}, + /* Hook-switch press or release */ + {PM8607_IRQ_HOOK, PM8607_IRQ_HOOK, "hook", IORESOURCE_IRQ,}, + /* Headset insertion or removal */ + {PM8607_IRQ_HEADSET, PM8607_IRQ_HEADSET, "headset", IORESOURCE_IRQ,}, + /* Audio short */ + {PM8607_IRQ_AUDIO_SHORT, PM8607_IRQ_AUDIO_SHORT, "audio-short", IORESOURCE_IRQ,}, }; -static struct mfd_cell touch_devs[] = { - { - .name = "88pm860x-touch", - .num_resources = 1, - .resources = &touch_resources[0], - }, +static struct resource battery_resources[] __initdata = { + {PM8607_IRQ_CC, PM8607_IRQ_CC, "columb counter", IORESOURCE_IRQ,}, + {PM8607_IRQ_BAT, PM8607_IRQ_BAT, "battery", IORESOURCE_IRQ,}, }; -#define PM8607_REG_RESOURCE(_start, _end) \ -{ \ - .start = PM8607_##_start, \ - .end = PM8607_##_end, \ - .flags = IORESOURCE_IO, \ -} - -static struct resource power_supply_resources[] = { - { - .name = "88pm860x-power", - .start = PM8607_IRQ_CHG, - .end = PM8607_IRQ_CHG, - .flags = IORESOURCE_IRQ, - }, +static struct resource charger_resources[] __initdata = { + {PM8607_IRQ_CHG, PM8607_IRQ_CHG, "charger detect", IORESOURCE_IRQ,}, + {PM8607_IRQ_CHG_DONE, PM8607_IRQ_CHG_DONE, "charging done", IORESOURCE_IRQ,}, + {PM8607_IRQ_CHG_FAULT, PM8607_IRQ_CHG_FAULT, "charging timeout", IORESOURCE_IRQ,}, + {PM8607_IRQ_GPADC1, PM8607_IRQ_GPADC1, "battery temperature", IORESOURCE_IRQ,}, + {PM8607_IRQ_VBAT, PM8607_IRQ_VBAT, "battery voltage", IORESOURCE_IRQ,}, + {PM8607_IRQ_VCHG, PM8607_IRQ_VCHG, "vchg voltage", IORESOURCE_IRQ,}, }; -static struct mfd_cell power_devs[] = { - { - .name = "88pm860x-power", - .num_resources = 1, - .resources = &power_supply_resources[0], - .id = -1, - }, +static struct mfd_cell bk_devs[] __initdata = { + {"88pm860x-backlight", 0,}, + {"88pm860x-backlight", 1,}, + {"88pm860x-backlight", 2,}, }; -static struct resource onkey_resources[] = { - { - .name = "88pm860x-onkey", - .start = PM8607_IRQ_ONKEY, - .end = PM8607_IRQ_ONKEY, - .flags = IORESOURCE_IRQ, - }, +static struct mfd_cell led_devs[] __initdata = { + {"88pm860x-led", 0,}, + {"88pm860x-led", 1,}, + {"88pm860x-led", 2,}, + {"88pm860x-led", 3,}, + {"88pm860x-led", 4,}, + {"88pm860x-led", 5,}, }; -static struct mfd_cell onkey_devs[] = { - { - .name = "88pm860x-onkey", - .num_resources = 1, - .resources = &onkey_resources[0], - .id = -1, - }, +static struct mfd_cell regulator_devs[] __initdata = { + {"88pm860x-regulator", 0,}, + {"88pm860x-regulator", 1,}, + {"88pm860x-regulator", 2,}, + {"88pm860x-regulator", 3,}, + {"88pm860x-regulator", 4,}, + {"88pm860x-regulator", 5,}, + {"88pm860x-regulator", 6,}, + {"88pm860x-regulator", 7,}, + {"88pm860x-regulator", 8,}, + {"88pm860x-regulator", 9,}, + {"88pm860x-regulator", 10,}, + {"88pm860x-regulator", 11,}, + {"88pm860x-regulator", 12,}, + {"88pm860x-regulator", 13,}, + {"88pm860x-regulator", 14,}, + {"88pm860x-regulator", 15,}, + {"88pm860x-regulator", 16,}, + {"88pm860x-regulator", 17,}, }; -static struct resource codec_resources[] = { - { - /* Headset microphone insertion or removal */ - .name = "micin", - .start = PM8607_IRQ_MICIN, - .end = PM8607_IRQ_MICIN, - .flags = IORESOURCE_IRQ, - }, { - /* Hook-switch press or release */ - .name = "hook", - .start = PM8607_IRQ_HOOK, - .end = PM8607_IRQ_HOOK, - .flags = IORESOURCE_IRQ, - }, { - /* Headset insertion or removal */ - .name = "headset", - .start = PM8607_IRQ_HEADSET, - .end = PM8607_IRQ_HEADSET, - .flags = IORESOURCE_IRQ, - }, { - /* Audio short */ - .name = "audio-short", - .start = PM8607_IRQ_AUDIO_SHORT, - .end = PM8607_IRQ_AUDIO_SHORT, - .flags = IORESOURCE_IRQ, - }, +static struct mfd_cell touch_devs[] __initdata = { + {"88pm860x-touch", -1,}, }; -static struct mfd_cell codec_devs[] = { - { - .name = "88pm860x-codec", - .num_resources = ARRAY_SIZE(codec_resources), - .resources = &codec_resources[0], - .id = -1, - }, +static struct mfd_cell onkey_devs[] __initdata = { + {"88pm860x-onkey", -1,}, }; -static struct resource regulator_resources[] = { - PM8607_REG_RESOURCE(BUCK1, BUCK1), - PM8607_REG_RESOURCE(BUCK2, BUCK2), - PM8607_REG_RESOURCE(BUCK3, BUCK3), - PM8607_REG_RESOURCE(LDO1, LDO1), - PM8607_REG_RESOURCE(LDO2, LDO2), - PM8607_REG_RESOURCE(LDO3, LDO3), - PM8607_REG_RESOURCE(LDO4, LDO4), - PM8607_REG_RESOURCE(LDO5, LDO5), - PM8607_REG_RESOURCE(LDO6, LDO6), - PM8607_REG_RESOURCE(LDO7, LDO7), - PM8607_REG_RESOURCE(LDO8, LDO8), - PM8607_REG_RESOURCE(LDO9, LDO9), - PM8607_REG_RESOURCE(LDO10, LDO10), - PM8607_REG_RESOURCE(LDO12, LDO12), - PM8607_REG_RESOURCE(VIBRATOR_SET, VIBRATOR_SET), - PM8607_REG_RESOURCE(LDO14, LDO14), +static struct mfd_cell codec_devs[] __initdata = { + {"88pm860x-codec", -1,}, }; -#define PM8607_REG_DEVS(_id) \ -{ \ - .name = "88pm860x-regulator", \ - .num_resources = 1, \ - .resources = ®ulator_resources[PM8607_ID_##_id], \ - .id = PM8607_ID_##_id, \ -} - -static struct mfd_cell regulator_devs[] = { - PM8607_REG_DEVS(BUCK1), - PM8607_REG_DEVS(BUCK2), - PM8607_REG_DEVS(BUCK3), - PM8607_REG_DEVS(LDO1), - PM8607_REG_DEVS(LDO2), - PM8607_REG_DEVS(LDO3), - PM8607_REG_DEVS(LDO4), - PM8607_REG_DEVS(LDO5), - PM8607_REG_DEVS(LDO6), - PM8607_REG_DEVS(LDO7), - PM8607_REG_DEVS(LDO8), - PM8607_REG_DEVS(LDO9), - PM8607_REG_DEVS(LDO10), - PM8607_REG_DEVS(LDO12), - PM8607_REG_DEVS(LDO13), - PM8607_REG_DEVS(LDO14), +static struct mfd_cell power_devs[] = { + {"88pm860x-battery", -1,}, + {"88pm860x-charger", -1,}, }; +static struct pm860x_backlight_pdata bk_pdata[ARRAY_SIZE(bk_devs)]; +static struct pm860x_led_pdata led_pdata[ARRAY_SIZE(led_devs)]; +static struct regulator_init_data regulator_pdata[ARRAY_SIZE(regulator_devs)]; +static struct pm860x_touch_pdata touch_pdata; +static struct pm860x_power_pdata power_pdata; + struct pm860x_irq_data { int reg; int mask_reg; @@ -361,12 +269,6 @@ static struct pm860x_irq_data pm860x_irqs[] = { }, }; -static inline struct pm860x_irq_data *irq_to_pm860x(struct pm860x_chip *chip, - int irq) -{ - return &pm860x_irqs[irq - chip->irq_base]; -} - static irqreturn_t pm860x_irq(int irq, void *data) { struct pm860x_chip *chip = data; @@ -388,16 +290,16 @@ static irqreturn_t pm860x_irq(int irq, void *data) return IRQ_HANDLED; } -static void pm860x_irq_lock(unsigned int irq) +static void pm860x_irq_lock(struct irq_data *data) { - struct pm860x_chip *chip = get_irq_chip_data(irq); + struct pm860x_chip *chip = irq_data_get_irq_chip_data(data); mutex_lock(&chip->irq_lock); } -static void pm860x_irq_sync_unlock(unsigned int irq) +static void pm860x_irq_sync_unlock(struct irq_data *data) { - struct pm860x_chip *chip = get_irq_chip_data(irq); + struct pm860x_chip *chip = irq_data_get_irq_chip_data(data); struct pm860x_irq_data *irq_data; struct i2c_client *i2c; static unsigned char cached[3] = {0x0, 0x0, 0x0}; @@ -439,25 +341,25 @@ static void pm860x_irq_sync_unlock(unsigned int irq) mutex_unlock(&chip->irq_lock); } -static void pm860x_irq_enable(unsigned int irq) +static void pm860x_irq_enable(struct irq_data *data) { - struct pm860x_chip *chip = get_irq_chip_data(irq); - pm860x_irqs[irq - chip->irq_base].enable - = pm860x_irqs[irq - chip->irq_base].offs; + struct pm860x_chip *chip = irq_data_get_irq_chip_data(data); + pm860x_irqs[data->irq - chip->irq_base].enable + = pm860x_irqs[data->irq - chip->irq_base].offs; } -static void pm860x_irq_disable(unsigned int irq) +static void pm860x_irq_disable(struct irq_data *data) { - struct pm860x_chip *chip = get_irq_chip_data(irq); - pm860x_irqs[irq - chip->irq_base].enable = 0; + struct pm860x_chip *chip = irq_data_get_irq_chip_data(data); + pm860x_irqs[data->irq - chip->irq_base].enable = 0; } static struct irq_chip pm860x_irq_chip = { .name = "88pm860x", - .bus_lock = pm860x_irq_lock, - .bus_sync_unlock = pm860x_irq_sync_unlock, - .enable = pm860x_irq_enable, - .disable = pm860x_irq_disable, + .irq_bus_lock = pm860x_irq_lock, + .irq_bus_sync_unlock = pm860x_irq_sync_unlock, + .irq_enable = pm860x_irq_enable, + .irq_disable = pm860x_irq_disable, }; static int __devinit device_gpadc_init(struct pm860x_chip *chip, @@ -514,7 +416,6 @@ static int __devinit device_irq_init(struct pm860x_chip *chip, : chip->companion; unsigned char status_buf[INT_STATUS_NUM]; unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; - struct irq_desc *desc; int i, data, mask, ret = -EINVAL; int __irq; @@ -566,19 +467,17 @@ static int __devinit device_irq_init(struct pm860x_chip *chip, if (!chip->core_irq) goto out; - desc = irq_to_desc(chip->core_irq); - /* register IRQ by genirq */ for (i = 0; i < ARRAY_SIZE(pm860x_irqs); i++) { __irq = i + chip->irq_base; - set_irq_chip_data(__irq, chip); - set_irq_chip_and_handler(__irq, &pm860x_irq_chip, + irq_set_chip_data(__irq, chip); + irq_set_chip_and_handler(__irq, &pm860x_irq_chip, handle_edge_irq); - set_irq_nested_thread(__irq, 1); + irq_set_nested_thread(__irq, 1); #ifdef CONFIG_ARM set_irq_flags(__irq, IRQF_VALID); #else - set_irq_noprobe(__irq); + irq_set_noprobe(__irq); #endif } @@ -601,37 +500,212 @@ static void device_irq_exit(struct pm860x_chip *chip) free_irq(chip->core_irq, chip); } -static void __devinit device_8606_init(struct pm860x_chip *chip, - struct i2c_client *i2c, - struct pm860x_platform_data *pdata) +static void __devinit device_bk_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) { int ret; + int i, j, id; + + if ((pdata == NULL) || (pdata->backlight == NULL)) + return; + + if (pdata->num_backlights > ARRAY_SIZE(bk_devs)) + pdata->num_backlights = ARRAY_SIZE(bk_devs); + + for (i = 0; i < pdata->num_backlights; i++) { + memcpy(&bk_pdata[i], &pdata->backlight[i], + sizeof(struct pm860x_backlight_pdata)); + bk_devs[i].mfd_data = &bk_pdata[i]; + + for (j = 0; j < ARRAY_SIZE(bk_devs); j++) { + id = bk_resources[j].start; + if (bk_pdata[i].flags != id) + continue; + + bk_devs[i].num_resources = 1; + bk_devs[i].resources = &bk_resources[j]; + ret = mfd_add_devices(chip->dev, 0, + &bk_devs[i], 1, + &bk_resources[j], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add " + "backlight subdev\n"); + return; + } + } + } +} - if (pdata && pdata->backlight) { - ret = mfd_add_devices(chip->dev, 0, &backlight_devs[0], - ARRAY_SIZE(backlight_devs), - &backlight_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add backlight " - "subdev\n"); - goto out_dev; +static void __devinit device_led_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + int i, j, id; + + if ((pdata == NULL) || (pdata->led == NULL)) + return; + + if (pdata->num_leds > ARRAY_SIZE(led_devs)) + pdata->num_leds = ARRAY_SIZE(led_devs); + + for (i = 0; i < pdata->num_leds; i++) { + memcpy(&led_pdata[i], &pdata->led[i], + sizeof(struct pm860x_led_pdata)); + led_devs[i].mfd_data = &led_pdata[i]; + + for (j = 0; j < ARRAY_SIZE(led_devs); j++) { + id = led_resources[j].start; + if (led_pdata[i].flags != id) + continue; + + led_devs[i].num_resources = 1; + led_devs[i].resources = &led_resources[j], + ret = mfd_add_devices(chip->dev, 0, + &led_devs[i], 1, + &led_resources[j], 0); + if (ret < 0) { + dev_err(chip->dev, "Failed to add " + "led subdev\n"); + return; + } } } +} - if (pdata && pdata->led) { - ret = mfd_add_devices(chip->dev, 0, &led_devs[0], - ARRAY_SIZE(led_devs), - &led_resources[0], 0); +static void __devinit device_regulator_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + struct regulator_init_data *initdata; + int ret; + int i, j; + + if ((pdata == NULL) || (pdata->regulator == NULL)) + return; + + if (pdata->num_regulators > ARRAY_SIZE(regulator_devs)) + pdata->num_regulators = ARRAY_SIZE(regulator_devs); + + for (i = 0, j = -1; i < pdata->num_regulators; i++) { + initdata = &pdata->regulator[i]; + if (strstr(initdata->constraints.name, "BUCK")) { + sscanf(initdata->constraints.name, "BUCK%d", &j); + /* BUCK1 ~ BUCK3 */ + if ((j < 1) || (j > 3)) { + dev_err(chip->dev, "Failed to add constraint " + "(%s)\n", initdata->constraints.name); + goto out; + } + j = (j - 1) + PM8607_ID_BUCK1; + } + if (strstr(initdata->constraints.name, "LDO")) { + sscanf(initdata->constraints.name, "LDO%d", &j); + /* LDO1 ~ LDO15 */ + if ((j < 1) || (j > 15)) { + dev_err(chip->dev, "Failed to add constraint " + "(%s)\n", initdata->constraints.name); + goto out; + } + j = (j - 1) + PM8607_ID_LDO1; + } + if (j == -1) { + dev_err(chip->dev, "Failed to add constraint (%s)\n", + initdata->constraints.name); + goto out; + } + memcpy(®ulator_pdata[i], &pdata->regulator[i], + sizeof(struct regulator_init_data)); + regulator_devs[i].mfd_data = ®ulator_pdata[i]; + regulator_devs[i].num_resources = 1; + regulator_devs[i].resources = ®ulator_resources[j]; + + ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[i], 1, + ®ulator_resources[j], 0); if (ret < 0) { - dev_err(chip->dev, "Failed to add led " - "subdev\n"); - goto out_dev; + dev_err(chip->dev, "Failed to add regulator subdev\n"); + goto out; } } +out: return; -out_dev: - mfd_remove_devices(chip->dev); - device_irq_exit(chip); +} + +static void __devinit device_touch_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + if ((pdata == NULL) || (pdata->touch == NULL)) + return; + + memcpy(&touch_pdata, pdata->touch, sizeof(struct pm860x_touch_pdata)); + touch_devs[0].mfd_data = &touch_pdata; + touch_devs[0].num_resources = ARRAY_SIZE(touch_resources); + touch_devs[0].resources = &touch_resources[0]; + ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], + ARRAY_SIZE(touch_devs), &touch_resources[0], + chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add touch subdev\n"); +} + +static void __devinit device_power_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + if ((pdata == NULL) || (pdata->power == NULL)) + return; + + memcpy(&power_pdata, pdata->power, sizeof(struct pm860x_power_pdata)); + power_devs[0].mfd_data = &power_pdata; + power_devs[0].num_resources = ARRAY_SIZE(battery_resources); + power_devs[0].resources = &battery_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[0], 1, + &battery_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add battery subdev\n"); + + power_devs[1].mfd_data = &power_pdata; + power_devs[1].num_resources = ARRAY_SIZE(charger_resources); + power_devs[1].resources = &charger_resources[0], + ret = mfd_add_devices(chip->dev, 0, &power_devs[1], 1, + &charger_resources[0], chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add charger subdev\n"); +} + +static void __devinit device_onkey_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + onkey_devs[0].num_resources = ARRAY_SIZE(onkey_resources); + onkey_devs[0].resources = &onkey_resources[0], + ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], + ARRAY_SIZE(onkey_devs), &onkey_resources[0], + chip->irq_base); + if (ret < 0) + dev_err(chip->dev, "Failed to add onkey subdev\n"); +} + +static void __devinit device_codec_init(struct pm860x_chip *chip, + struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ + int ret; + + codec_devs[0].num_resources = ARRAY_SIZE(codec_resources); + codec_devs[0].resources = &codec_resources[0], + ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], + ARRAY_SIZE(codec_devs), &codec_resources[0], 0); + if (ret < 0) + dev_err(chip->dev, "Failed to add codec subdev\n"); } static void __devinit device_8607_init(struct pm860x_chip *chip, @@ -689,55 +763,11 @@ static void __devinit device_8607_init(struct pm860x_chip *chip, if (ret < 0) goto out; - ret = mfd_add_devices(chip->dev, 0, ®ulator_devs[0], - ARRAY_SIZE(regulator_devs), - ®ulator_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add regulator subdev\n"); - goto out_dev; - } - - if (pdata && pdata->touch) { - ret = mfd_add_devices(chip->dev, 0, &touch_devs[0], - ARRAY_SIZE(touch_devs), - &touch_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add touch " - "subdev\n"); - goto out_dev; - } - } - - if (pdata && pdata->power) { - ret = mfd_add_devices(chip->dev, 0, &power_devs[0], - ARRAY_SIZE(power_devs), - &power_supply_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add power supply " - "subdev\n"); - goto out_dev; - } - } - - ret = mfd_add_devices(chip->dev, 0, &onkey_devs[0], - ARRAY_SIZE(onkey_devs), - &onkey_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add onkey subdev\n"); - goto out_dev; - } - - ret = mfd_add_devices(chip->dev, 0, &codec_devs[0], - ARRAY_SIZE(codec_devs), - &codec_resources[0], 0); - if (ret < 0) { - dev_err(chip->dev, "Failed to add codec subdev\n"); - goto out_dev; - } - return; -out_dev: - mfd_remove_devices(chip->dev); - device_irq_exit(chip); + device_regulator_init(chip, i2c, pdata); + device_onkey_init(chip, i2c, pdata); + device_touch_init(chip, i2c, pdata); + device_power_init(chip, i2c, pdata); + device_codec_init(chip, i2c, pdata); out: return; } @@ -749,7 +779,8 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, switch (chip->id) { case CHIP_PM8606: - device_8606_init(chip, chip->client, pdata); + device_bk_init(chip, chip->client, pdata); + device_led_init(chip, chip->client, pdata); break; case CHIP_PM8607: device_8607_init(chip, chip->client, pdata); @@ -759,7 +790,8 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip, if (chip->companion) { switch (chip->id) { case CHIP_PM8607: - device_8606_init(chip, chip->companion, pdata); + device_bk_init(chip, chip->companion, pdata); + device_led_init(chip, chip->companion, pdata); break; case CHIP_PM8606: device_8607_init(chip, chip->companion, pdata); diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c index bc02e6b..e017dc8 100644 --- a/drivers/mfd/88pm860x-i2c.c +++ b/drivers/mfd/88pm860x-i2c.c @@ -126,6 +126,109 @@ out: } EXPORT_SYMBOL(pm860x_set_bits); +int pm860x_page_reg_read(struct i2c_client *i2c, int reg) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero = 0; + unsigned char data; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_read_device(i2c, reg, 1, &data); + if (ret >= 0) + ret = (int)data; + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_reg_read); + +int pm860x_page_reg_write(struct i2c_client *i2c, int reg, + unsigned char data) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_write_device(i2c, reg, 1, &data); + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_reg_write); + +int pm860x_page_bulk_read(struct i2c_client *i2c, int reg, + int count, unsigned char *buf) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero = 0; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_read_device(i2c, reg, count, buf); + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_bulk_read); + +int pm860x_page_bulk_write(struct i2c_client *i2c, int reg, + int count, unsigned char *buf) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero = 0; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_write_device(i2c, reg, count, buf); + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_bulk_write); + +int pm860x_page_set_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data) +{ + struct pm860x_chip *chip = i2c_get_clientdata(i2c); + unsigned char zero; + unsigned char value; + int ret; + + mutex_lock(&chip->io_lock); + pm860x_write_device(i2c, 0xFA, 0, &zero); + pm860x_write_device(i2c, 0xFB, 0, &zero); + pm860x_write_device(i2c, 0xFF, 0, &zero); + ret = pm860x_read_device(i2c, reg, 1, &value); + if (ret < 0) + goto out; + value &= ~mask; + value |= data; + ret = pm860x_write_device(i2c, reg, 1, &value); +out: + pm860x_write_device(i2c, 0xFE, 0, &zero); + pm860x_write_device(i2c, 0xFC, 0, &zero); + mutex_unlock(&chip->io_lock); + return ret; +} +EXPORT_SYMBOL(pm860x_page_set_bits); static const struct i2c_device_id pm860x_id_table[] = { { "88PM860x", 0 }, diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3a1493b..3ed3ff0 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -60,15 +60,6 @@ config MFD_ASIC3 This driver supports the ASIC3 multifunction chip found on many PDAs (mainly iPAQ and HTC based ones) -config MFD_SH_MOBILE_SDHI - bool "Support for SuperH Mobile SDHI" - depends on SUPERH || ARCH_SHMOBILE - select MFD_CORE - select TMIO_MMC_DMA - ---help--- - This driver supports the SDHI hardware block found in many - SuperH Mobile SoCs. - config MFD_DAVINCI_VOICECODEC tristate select MFD_CORE @@ -81,6 +72,17 @@ config MFD_DM355EVM_MSP boards. MSP430 firmware manages resets and power sequencing, inputs from buttons and the IR remote, LEDs, an RTC, and more. +config MFD_TI_SSP + tristate "TI Sequencer Serial Port support" + depends on ARCH_DAVINCI_TNETV107X + select MFD_CORE + ---help--- + Say Y here if you want support for the Sequencer Serial Port + in a Texas Instruments TNETV107X SoC. + + To compile this driver as a module, choose M here: the + module will be called ti-ssp. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM @@ -118,6 +120,18 @@ config UCB1400_CORE To compile this driver as a module, choose M here: the module will be called ucb1400_core. +config TPS6105X + tristate "TPS61050/61052 Boost Converters" + depends on I2C + select REGULATOR + select MFD_CORE + select REGULATOR_FIXED_VOLTAGE + help + This option enables a driver for the TP61050/TPS61052 + high-power "white LED driver". This boost converter is + sometimes used for other things than white LEDs, and + also contains a GPIO pin. + config TPS65010 tristate "TPS6501x Power Management chips" depends on I2C && GPIOLIB @@ -167,6 +181,16 @@ config TWL4030_CORE high speed USB OTG transceiver, an audio codec (on most versions) and many other features. +config TWL4030_MADC + tristate "Texas Instruments TWL4030 MADC" + depends on TWL4030_CORE + help + This driver provides support for triton TWL4030-MADC. The + driver supports both RT and SW conversion methods. + + This driver can be built as a module. If so it will be + named twl4030-madc + config TWL4030_POWER bool "Support power resources on TWL4030 family chips" depends on TWL4030_CORE && ARM @@ -177,7 +201,7 @@ config TWL4030_POWER as clock request handshaking. This driver uses board-specific data to initialize the resources - and load scripts controling which resources are switched off/on + and load scripts controlling which resources are switched off/on or reset when a sleep, wakeup or warm reset event occurs. config TWL4030_CODEC @@ -218,12 +242,12 @@ config MFD_STMPE Keypad: stmpe-keypad Touchscreen: stmpe-ts -config MFD_TC35892 - bool "Support Toshiba TC35892" +config MFD_TC3589X + bool "Support Toshiba TC35892 and variants" depends on I2C=y && GENERIC_HARDIRQS select MFD_CORE help - Support for the Toshiba TC35892 I/O Expander. + Support for the Toshiba TC35892 and variants I/O Expander. This driver provides common support for accessing the device, additional drivers must be enabled in order to use the @@ -233,11 +257,6 @@ config MFD_TMIO bool default n -config TMIO_MMC_DMA - bool - select DMA_ENGINE - select DMADEVICES - config MFD_T7L66XB bool "Support Toshiba T7L66XB" depends on ARM && HAVE_CLK @@ -293,6 +312,18 @@ config MFD_MAX8925 accessing the device, additional drivers must be enabled in order to use the functionality of the device. +config MFD_MAX8997 + bool "Maxim Semiconductor MAX8997/8966 PMIC Support" + depends on I2C=y && GENERIC_HARDIRQS + select MFD_CORE + help + Say yes here to support for Maxim Semiconductor MAX8998/8966. + This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic, + MUIC controls on chip. + This driver provides common support for accessing the device; + additional drivers must be enabled in order to use the functionality + of the device. + config MFD_MAX8998 bool "Maxim Semiconductor MAX8998/National LP3974 PMIC Support" depends on I2C=y && GENERIC_HARDIRQS @@ -496,13 +527,13 @@ config EZX_PCAP config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" - depends on GENERIC_HARDIRQS && ABX500_CORE && SPI_MASTER && ARCH_U8500 + depends on GENERIC_HARDIRQS && ABX500_CORE select MFD_CORE help Select this option to enable access to AB8500 power management - chip. This connects to U8500 either on the SSP/SPI bus - or the I2C bus via PRCMU. It also adds the irq_chip - parts for handling the Mixed Signal chip events. + chip. This connects to U8500 either on the SSP/SPI bus (deprecated + since hardware version v1.0) or the I2C bus via PRCMU. It also adds + the irq_chip parts for handling the Mixed Signal chip events. This chip embeds various other multimedia funtionalities as well. config AB8500_I2C_CORE @@ -523,6 +554,13 @@ config AB8500_DEBUG Select this option if you want debug information using the debug filesystem, debugfs. +config AB8500_GPADC + bool "AB8500 GPADC driver" + depends on AB8500_CORE && REGULATOR_AB8500 + default y + help + AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage + config AB3550_CORE bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions" select MFD_CORE @@ -537,6 +575,14 @@ config AB3550_CORE LEDs, vibrator, system power and temperature, power management and ALSA sound. +config MFD_CS5535 + tristate "Support for CS5535 and CS5536 southbridge core functions" + select MFD_CORE + depends on PCI && X86 + ---help--- + This is the core driver for CS5535/CS5536 MFD functions. This is + necessary for using the board's GPIO and MFGPT functionality. + config MFD_TIMBERDALE tristate "Support for the Timberdale FPGA" select MFD_CORE @@ -606,6 +652,25 @@ config MFD_VX855 VIA VX855/VX875 south bridge. You will need to enable the vx855_spi and/or vx855_gpio drivers for this to do anything useful. +config MFD_WL1273_CORE + tristate "Support for TI WL1273 FM radio." + depends on I2C + select MFD_CORE + default n + help + This is the core driver for the TI WL1273 FM radio. This MFD + driver connects the radio-wl1273 V4L2 module and the wl1273 + audio codec. + +config MFD_OMAP_USB_HOST + bool "Support OMAP USBHS core driver" + depends on USB_EHCI_HCD_OMAP || USB_OHCI_HCD_OMAP3 + default y + help + This is the core driver for the OAMP EHCI and OHCI drivers. + This MFD driver does the required setup functionalities for + OMAP USB Host drivers. + endif # MFD_SUPPORT menu "Multimedia Capabilities Port drivers" diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f54b365..419caa9 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_MFD_88PM860X) += 88pm860x.o obj-$(CONFIG_MFD_SM501) += sm501.o obj-$(CONFIG_MFD_ASIC3) += asic3.o tmio_core.o -obj-$(CONFIG_MFD_SH_MOBILE_SDHI) += sh_mobile_sdhi.o obj-$(CONFIG_HTC_EGPIO) += htc-egpio.o obj-$(CONFIG_HTC_PASIC3) += htc-pasic3.o @@ -14,9 +13,10 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.o obj-$(CONFIG_MFD_DAVINCI_VOICECODEC) += davinci_voicecodec.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o +obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o obj-$(CONFIG_MFD_STMPE) += stmpe.o -obj-$(CONFIG_MFD_TC35892) += tc35892.o +obj-$(CONFIG_MFD_TC3589X) += tc3589x.o obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o tmio_core.o obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o tmio_core.o obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o tmio_core.o @@ -32,11 +32,13 @@ obj-$(CONFIG_MFD_WM8350) += wm8350.o obj-$(CONFIG_MFD_WM8350_I2C) += wm8350-i2c.o obj-$(CONFIG_MFD_WM8994) += wm8994-core.o wm8994-irq.o +obj-$(CONFIG_TPS6105X) += tps6105x.o obj-$(CONFIG_TPS65010) += tps65010.o obj-$(CONFIG_TPS6507X) += tps6507x.o obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o +obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL4030_POWER) += twl4030-power.o obj-$(CONFIG_TWL4030_CODEC) += twl4030-codec.o obj-$(CONFIG_TWL6030_PWM) += twl6030-pwm.o @@ -60,6 +62,7 @@ obj-$(CONFIG_UCB1400_CORE) += ucb1400_core.o obj-$(CONFIG_PMIC_DA903X) += da903x.o max8925-objs := max8925-core.o max8925-i2c.o obj-$(CONFIG_MFD_MAX8925) += max8925.o +obj-$(CONFIG_MFD_MAX8997) += max8997.o max8997-irq.o obj-$(CONFIG_MFD_MAX8998) += max8998.o max8998-irq.o pcf50633-objs := pcf50633-core.o pcf50633-irq.o @@ -70,9 +73,10 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o -obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-spi.o +obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_I2C_CORE) += ab8500-i2c.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o +obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o obj-$(CONFIG_PMIC_ADP5520) += adp5520.o obj-$(CONFIG_LPC_SCH) += lpc_sch.o @@ -81,3 +85,6 @@ obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o obj-$(CONFIG_MFD_TPS6586X) += tps6586x.o obj-$(CONFIG_MFD_VX855) += vx855.o +obj-$(CONFIG_MFD_WL1273_CORE) += wl1273-core.o +obj-$(CONFIG_MFD_CS5535) += cs5535-mfd.o +obj-$(CONFIG_MFD_OMAP_USB_HOST) += omap-usb-host.o diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 4193af5..a751927 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -613,7 +613,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_get_priv.ab3100 = ab3100; ab3100_get_priv.mode = false; ab3100_get_reg_file = debugfs_create_file("get_reg", - S_IWUGO, ab3100_dir, &ab3100_get_priv, + S_IWUSR, ab3100_dir, &ab3100_get_priv, &ab3100_get_set_reg_fops); if (!ab3100_get_reg_file) { err = -ENOMEM; @@ -623,7 +623,7 @@ static void ab3100_setup_debugfs(struct ab3100 *ab3100) ab3100_set_priv.ab3100 = ab3100; ab3100_set_priv.mode = true; ab3100_set_reg_file = debugfs_create_file("set_reg", - S_IWUGO, ab3100_dir, &ab3100_set_priv, + S_IWUSR, ab3100_dir, &ab3100_set_priv, &ab3100_get_set_reg_fops); if (!ab3100_set_reg_file) { err = -ENOMEM; @@ -949,10 +949,8 @@ static int __devinit ab3100_probe(struct i2c_client *client, goto exit_no_ops; /* Set up and register the platform devices. */ - for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) { - ab3100_devs[i].platform_data = ab3100_plf_data; - ab3100_devs[i].data_size = sizeof(struct ab3100_platform_data); - } + for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) + ab3100_devs[i].mfd_data = ab3100_plf_data; err = mfd_add_devices(&client->dev, 0, ab3100_devs, ARRAY_SIZE(ab3100_devs), NULL, 0); diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index 8a98739..ff86acf 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -668,7 +668,7 @@ static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) struct ab3550_platform_data *plf_data; bool val; - ab = get_irq_chip_data(irq); + ab = irq_get_chip_data(irq); plf_data = ab->i2c_client[0]->dev.platform_data; irq -= plf_data->irq.base; val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0); @@ -1053,17 +1053,17 @@ static inline void ab3550_setup_debugfs(struct ab3550 *ab) goto exit_destroy_dir; ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_bank_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops); if (!ab3550_bank_file) goto exit_destroy_reg; ab3550_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_address_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops); if (!ab3550_address_file) goto exit_destroy_bank; ab3550_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab3550_dir, ab, &ab3550_val_fops); + (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); if (!ab3550_val_file) goto exit_destroy_address; @@ -1159,15 +1159,16 @@ static void ab3550_mask_work(struct work_struct *work) } } -static void ab3550_mask(unsigned int irq) +static void ab3550_mask(struct irq_data *data) { unsigned long flags; struct ab3550 *ab; struct ab3550_platform_data *plf_data; + int irq; - ab = get_irq_chip_data(irq); + ab = irq_data_get_irq_chip_data(data); plf_data = ab->i2c_client[0]->dev.platform_data; - irq -= plf_data->irq.base; + irq = data->irq - plf_data->irq.base; spin_lock_irqsave(&ab->event_lock, flags); ab->event_mask[irq / 8] |= BIT(irq % 8); @@ -1176,15 +1177,16 @@ static void ab3550_mask(unsigned int irq) schedule_work(&ab->mask_work); } -static void ab3550_unmask(unsigned int irq) +static void ab3550_unmask(struct irq_data *data) { unsigned long flags; struct ab3550 *ab; struct ab3550_platform_data *plf_data; + int irq; - ab = get_irq_chip_data(irq); + ab = irq_data_get_irq_chip_data(data); plf_data = ab->i2c_client[0]->dev.platform_data; - irq -= plf_data->irq.base; + irq = data->irq - plf_data->irq.base; spin_lock_irqsave(&ab->event_lock, flags); ab->event_mask[irq / 8] &= ~BIT(irq % 8); @@ -1193,20 +1195,16 @@ static void ab3550_unmask(unsigned int irq) schedule_work(&ab->mask_work); } -static void noop(unsigned int irq) +static void noop(struct irq_data *data) { } static struct irq_chip ab3550_irq_chip = { .name = "ab3550-core", /* Keep the same name as the request */ - .startup = NULL, /* defaults to enable */ - .shutdown = NULL, /* defaults to disable */ - .enable = NULL, /* defaults to unmask */ - .disable = ab3550_mask, /* No default to mask in chip.c */ - .ack = noop, - .mask = ab3550_mask, - .unmask = ab3550_unmask, - .end = NULL, + .irq_disable = ab3550_mask, /* No default to mask in chip.c */ + .irq_ack = noop, + .irq_mask = ab3550_mask, + .irq_unmask = ab3550_unmask, }; struct ab_family_id { @@ -1298,14 +1296,14 @@ static int __init ab3550_probe(struct i2c_client *client, unsigned int irq; irq = ab3550_plf_data->irq.base + i; - set_irq_chip_data(irq, ab); - set_irq_chip_and_handler(irq, &ab3550_irq_chip, - handle_simple_irq); - set_irq_nested_thread(irq, 1); + irq_set_chip_data(irq, ab); + irq_set_chip_and_handler(irq, &ab3550_irq_chip, + handle_simple_irq); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -1322,10 +1320,8 @@ static int __init ab3550_probe(struct i2c_client *client, goto exit_no_ops; /* Set up and register the platform devices. */ - for (i = 0; i < AB3550_NUM_DEVICES; i++) { - ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; - ab3550_devs[i].data_size = ab3550_plf_data->dev_data_sz[i]; - } + for (i = 0; i < AB3550_NUM_DEVICES; i++) + ab3550_devs[i].mfd_data = ab3550_plf_data->dev_data[i]; err = mfd_add_devices(&client->dev, 0, ab3550_devs, ARRAY_SIZE(ab3550_devs), NULL, diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index dbe1c93..67d01c9 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -4,7 +4,7 @@ * License Terms: GNU General Public License v2 * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> * Author: Rabin Vincent <rabin.vincent@stericsson.com> - * Changes: Mattias Wallin <mattias.wallin@stericsson.com> + * Author: Mattias Wallin <mattias.wallin@stericsson.com> */ #include <linux/kernel.h> @@ -52,6 +52,7 @@ #define AB8500_IT_LATCH8_REG 0x27 #define AB8500_IT_LATCH9_REG 0x28 #define AB8500_IT_LATCH10_REG 0x29 +#define AB8500_IT_LATCH12_REG 0x2B #define AB8500_IT_LATCH19_REG 0x32 #define AB8500_IT_LATCH20_REG 0x33 #define AB8500_IT_LATCH21_REG 0x34 @@ -89,6 +90,7 @@ #define AB8500_IT_MASK24_REG 0x57 #define AB8500_REV_REG 0x80 +#define AB8500_SWITCH_OFF_STATUS 0x00 /* * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt @@ -98,13 +100,17 @@ * offset 0. */ static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = { - 0, 1, 2, 3, 4, 6, 7, 8, 9, 18, 19, 20, 21, + 0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, }; static int ab8500_get_chip_id(struct device *dev) { - struct ab8500 *ab8500 = dev_get_drvdata(dev->parent); - return (int)ab8500->chip_id; + struct ab8500 *ab8500; + + if (!dev) + return -EINVAL; + ab8500 = dev_get_drvdata(dev->parent); + return ab8500 ? (int)ab8500->chip_id : -EINVAL; } static int set_register_interruptible(struct ab8500 *ab8500, u8 bank, @@ -228,16 +234,16 @@ static struct abx500_ops ab8500_ops = { .startup_irq_enabled = NULL, }; -static void ab8500_irq_lock(unsigned int irq) +static void ab8500_irq_lock(struct irq_data *data) { - struct ab8500 *ab8500 = get_irq_chip_data(irq); + struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); mutex_lock(&ab8500->irq_lock); } -static void ab8500_irq_sync_unlock(unsigned int irq) +static void ab8500_irq_sync_unlock(struct irq_data *data) { - struct ab8500 *ab8500 = get_irq_chip_data(irq); + struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); int i; for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { @@ -248,6 +254,10 @@ static void ab8500_irq_sync_unlock(unsigned int irq) if (new == old) continue; + /* Interrupt register 12 does'nt exist prior to version 0x20 */ + if (ab8500_irq_regoffset[i] == 11 && ab8500->chip_id < 0x20) + continue; + ab8500->oldmask[i] = new; reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; @@ -257,20 +267,20 @@ static void ab8500_irq_sync_unlock(unsigned int irq) mutex_unlock(&ab8500->irq_lock); } -static void ab8500_irq_mask(unsigned int irq) +static void ab8500_irq_mask(struct irq_data *data) { - struct ab8500 *ab8500 = get_irq_chip_data(irq); - int offset = irq - ab8500->irq_base; + struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); + int offset = data->irq - ab8500->irq_base; int index = offset / 8; int mask = 1 << (offset % 8); ab8500->mask[index] |= mask; } -static void ab8500_irq_unmask(unsigned int irq) +static void ab8500_irq_unmask(struct irq_data *data) { - struct ab8500 *ab8500 = get_irq_chip_data(irq); - int offset = irq - ab8500->irq_base; + struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data); + int offset = data->irq - ab8500->irq_base; int index = offset / 8; int mask = 1 << (offset % 8); @@ -279,10 +289,10 @@ static void ab8500_irq_unmask(unsigned int irq) static struct irq_chip ab8500_irq_chip = { .name = "ab8500", - .bus_lock = ab8500_irq_lock, - .bus_sync_unlock = ab8500_irq_sync_unlock, - .mask = ab8500_irq_mask, - .unmask = ab8500_irq_unmask, + .irq_bus_lock = ab8500_irq_lock, + .irq_bus_sync_unlock = ab8500_irq_sync_unlock, + .irq_mask = ab8500_irq_mask, + .irq_unmask = ab8500_irq_unmask, }; static irqreturn_t ab8500_irq(int irq, void *dev) @@ -297,13 +307,17 @@ static irqreturn_t ab8500_irq(int irq, void *dev) int status; u8 value; + /* Interrupt register 12 does'nt exist prior to version 0x20 */ + if (regoffset == 11 && ab8500->chip_id < 0x20) + continue; + status = get_register_interruptible(ab8500, AB8500_INTERRUPT, AB8500_IT_LATCH1_REG + regoffset, &value); if (status < 0 || value == 0) continue; do { - int bit = __ffs(status); + int bit = __ffs(value); int line = i * 8 + bit; handle_nested_irq(ab8500->irq_base + line); @@ -320,14 +334,14 @@ static int ab8500_irq_init(struct ab8500 *ab8500) int irq; for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { - set_irq_chip_data(irq, ab8500); - set_irq_chip_and_handler(irq, &ab8500_irq_chip, + irq_set_chip_data(irq, ab8500); + irq_set_chip_and_handler(irq, &ab8500_irq_chip, handle_simple_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -343,11 +357,20 @@ static void ab8500_irq_remove(struct ab8500 *ab8500) #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } } +static struct resource ab8500_gpio_resources[] = { + { + .name = "GPIO_INT6", + .start = AB8500_INT_GPIO6R, + .end = AB8500_INT_GPIO41F, + .flags = IORESOURCE_IRQ, + } +}; + static struct resource ab8500_gpadc_resources[] = { { .name = "HW_CONV_END", @@ -393,13 +416,200 @@ static struct resource ab8500_poweronkey_db_resources[] = { }, }; +static struct resource ab8500_bm_resources[] = { + { + .name = "MAIN_EXT_CH_NOT_OK", + .start = AB8500_INT_MAIN_EXT_CH_NOT_OK, + .end = AB8500_INT_MAIN_EXT_CH_NOT_OK, + .flags = IORESOURCE_IRQ, + }, + { + .name = "BATT_OVV", + .start = AB8500_INT_BATT_OVV, + .end = AB8500_INT_BATT_OVV, + .flags = IORESOURCE_IRQ, + }, + { + .name = "MAIN_CH_UNPLUG_DET", + .start = AB8500_INT_MAIN_CH_UNPLUG_DET, + .end = AB8500_INT_MAIN_CH_UNPLUG_DET, + .flags = IORESOURCE_IRQ, + }, + { + .name = "MAIN_CHARGE_PLUG_DET", + .start = AB8500_INT_MAIN_CH_PLUG_DET, + .end = AB8500_INT_MAIN_CH_PLUG_DET, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_DET_F", + .start = AB8500_INT_VBUS_DET_F, + .end = AB8500_INT_VBUS_DET_F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_DET_R", + .start = AB8500_INT_VBUS_DET_R, + .end = AB8500_INT_VBUS_DET_R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "BAT_CTRL_INDB", + .start = AB8500_INT_BAT_CTRL_INDB, + .end = AB8500_INT_BAT_CTRL_INDB, + .flags = IORESOURCE_IRQ, + }, + { + .name = "CH_WD_EXP", + .start = AB8500_INT_CH_WD_EXP, + .end = AB8500_INT_CH_WD_EXP, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_OVV", + .start = AB8500_INT_VBUS_OVV, + .end = AB8500_INT_VBUS_OVV, + .flags = IORESOURCE_IRQ, + }, + { + .name = "NCONV_ACCU", + .start = AB8500_INT_CCN_CONV_ACC, + .end = AB8500_INT_CCN_CONV_ACC, + .flags = IORESOURCE_IRQ, + }, + { + .name = "LOW_BAT_F", + .start = AB8500_INT_LOW_BAT_F, + .end = AB8500_INT_LOW_BAT_F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "LOW_BAT_R", + .start = AB8500_INT_LOW_BAT_R, + .end = AB8500_INT_LOW_BAT_R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "BTEMP_LOW", + .start = AB8500_INT_BTEMP_LOW, + .end = AB8500_INT_BTEMP_LOW, + .flags = IORESOURCE_IRQ, + }, + { + .name = "BTEMP_HIGH", + .start = AB8500_INT_BTEMP_HIGH, + .end = AB8500_INT_BTEMP_HIGH, + .flags = IORESOURCE_IRQ, + }, + { + .name = "USB_CHARGER_NOT_OKR", + .start = AB8500_INT_USB_CHARGER_NOT_OK, + .end = AB8500_INT_USB_CHARGER_NOT_OK, + .flags = IORESOURCE_IRQ, + }, + { + .name = "USB_CHARGE_DET_DONE", + .start = AB8500_INT_USB_CHG_DET_DONE, + .end = AB8500_INT_USB_CHG_DET_DONE, + .flags = IORESOURCE_IRQ, + }, + { + .name = "USB_CH_TH_PROT_R", + .start = AB8500_INT_USB_CH_TH_PROT_R, + .end = AB8500_INT_USB_CH_TH_PROT_R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "MAIN_CH_TH_PROT_R", + .start = AB8500_INT_MAIN_CH_TH_PROT_R, + .end = AB8500_INT_MAIN_CH_TH_PROT_R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "USB_CHARGER_NOT_OKF", + .start = AB8500_INT_USB_CHARGER_NOT_OKF, + .end = AB8500_INT_USB_CHARGER_NOT_OKF, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource ab8500_debug_resources[] = { + { + .name = "IRQ_FIRST", + .start = AB8500_INT_MAIN_EXT_CH_NOT_OK, + .end = AB8500_INT_MAIN_EXT_CH_NOT_OK, + .flags = IORESOURCE_IRQ, + }, + { + .name = "IRQ_LAST", + .start = AB8500_INT_USB_CHARGER_NOT_OKF, + .end = AB8500_INT_USB_CHARGER_NOT_OKF, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource ab8500_usb_resources[] = { + { + .name = "ID_WAKEUP_R", + .start = AB8500_INT_ID_WAKEUP_R, + .end = AB8500_INT_ID_WAKEUP_R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "ID_WAKEUP_F", + .start = AB8500_INT_ID_WAKEUP_F, + .end = AB8500_INT_ID_WAKEUP_F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_DET_F", + .start = AB8500_INT_VBUS_DET_F, + .end = AB8500_INT_VBUS_DET_F, + .flags = IORESOURCE_IRQ, + }, + { + .name = "VBUS_DET_R", + .start = AB8500_INT_VBUS_DET_R, + .end = AB8500_INT_VBUS_DET_R, + .flags = IORESOURCE_IRQ, + }, + { + .name = "USB_LINK_STATUS", + .start = AB8500_INT_USB_LINK_STATUS, + .end = AB8500_INT_USB_LINK_STATUS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource ab8500_temp_resources[] = { + { + .name = "AB8500_TEMP_WARM", + .start = AB8500_INT_TEMP_WARM, + .end = AB8500_INT_TEMP_WARM, + .flags = IORESOURCE_IRQ, + }, +}; + static struct mfd_cell ab8500_devs[] = { #ifdef CONFIG_DEBUG_FS { .name = "ab8500-debug", + .num_resources = ARRAY_SIZE(ab8500_debug_resources), + .resources = ab8500_debug_resources, }, #endif { + .name = "ab8500-sysctrl", + }, + { + .name = "ab8500-regulator", + }, + { + .name = "ab8500-gpio", + .num_resources = ARRAY_SIZE(ab8500_gpio_resources), + .resources = ab8500_gpio_resources, + }, + { .name = "ab8500-gpadc", .num_resources = ARRAY_SIZE(ab8500_gpadc_resources), .resources = ab8500_gpadc_resources, @@ -410,6 +620,22 @@ static struct mfd_cell ab8500_devs[] = { .resources = ab8500_rtc_resources, }, { + .name = "ab8500-bm", + .num_resources = ARRAY_SIZE(ab8500_bm_resources), + .resources = ab8500_bm_resources, + }, + { .name = "ab8500-codec", }, + { + .name = "ab8500-usb", + .num_resources = ARRAY_SIZE(ab8500_usb_resources), + .resources = ab8500_usb_resources, + }, + { + .name = "ab8500-poweron-key", + .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), + .resources = ab8500_poweronkey_db_resources, + }, + { .name = "ab8500-pwm", .id = 1, }, @@ -421,17 +647,65 @@ static struct mfd_cell ab8500_devs[] = { .name = "ab8500-pwm", .id = 3, }, - { .name = "ab8500-charger", }, - { .name = "ab8500-audio", }, - { .name = "ab8500-usb", }, - { .name = "ab8500-regulator", }, + { .name = "ab8500-leds", }, { - .name = "ab8500-poweron-key", - .num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), - .resources = ab8500_poweronkey_db_resources, + .name = "ab8500-denc", + }, + { + .name = "ab8500-temp", + .num_resources = ARRAY_SIZE(ab8500_temp_resources), + .resources = ab8500_temp_resources, }, }; +static ssize_t show_chip_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct ab8500 *ab8500; + + ab8500 = dev_get_drvdata(dev); + return sprintf(buf, "%#x\n", ab8500 ? ab8500->chip_id : -EINVAL); +} + +/* + * ab8500 has switched off due to (SWITCH_OFF_STATUS): + * 0x01 Swoff bit programming + * 0x02 Thermal protection activation + * 0x04 Vbat lower then BattOk falling threshold + * 0x08 Watchdog expired + * 0x10 Non presence of 32kHz clock + * 0x20 Battery level lower than power on reset threshold + * 0x40 Power on key 1 pressed longer than 10 seconds + * 0x80 DB8500 thermal shutdown + */ +static ssize_t show_switch_off_status(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret; + u8 value; + struct ab8500 *ab8500; + + ab8500 = dev_get_drvdata(dev); + ret = get_register_interruptible(ab8500, AB8500_RTC, + AB8500_SWITCH_OFF_STATUS, &value); + if (ret < 0) + return ret; + return sprintf(buf, "%#x\n", value); +} + +static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL); +static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL); + +static struct attribute *ab8500_sysfs_entries[] = { + &dev_attr_chip_id.attr, + &dev_attr_switch_off_status.attr, + NULL, +}; + +static struct attribute_group ab8500_attr_group = { + .attrs = ab8500_sysfs_entries, +}; + int __devinit ab8500_init(struct ab8500 *ab8500) { struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev); @@ -454,9 +728,11 @@ int __devinit ab8500_init(struct ab8500 *ab8500) * 0x0 - Early Drop * 0x10 - Cut 1.0 * 0x11 - Cut 1.1 + * 0x20 - Cut 2.0 + * 0x30 - Cut 3.0 */ - if (value == 0x0 || value == 0x10 || value == 0x11) { - ab8500->revision = value; + if (value == 0x0 || value == 0x10 || value == 0x11 || value == 0x20 || + value == 0x30) { dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); } else { dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); @@ -464,22 +740,38 @@ int __devinit ab8500_init(struct ab8500 *ab8500) } ab8500->chip_id = value; + /* + * ab8500 has switched off due to (SWITCH_OFF_STATUS): + * 0x01 Swoff bit programming + * 0x02 Thermal protection activation + * 0x04 Vbat lower then BattOk falling threshold + * 0x08 Watchdog expired + * 0x10 Non presence of 32kHz clock + * 0x20 Battery level lower than power on reset threshold + * 0x40 Power on key 1 pressed longer than 10 seconds + * 0x80 DB8500 thermal shutdown + */ + + ret = get_register_interruptible(ab8500, AB8500_RTC, + AB8500_SWITCH_OFF_STATUS, &value); + if (ret < 0) + return ret; + dev_info(ab8500->dev, "switch off status: %#x", value); + if (plat && plat->init) plat->init(ab8500); /* Clear and mask all interrupts */ - for (i = 0; i < 10; i++) { - get_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_LATCH1_REG + i, &value); - set_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_MASK1_REG + i, 0xff); - } + for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { + /* Interrupt register 12 does'nt exist prior to version 0x20 */ + if (ab8500_irq_regoffset[i] == 11 && ab8500->chip_id < 0x20) + continue; - for (i = 18; i < 24; i++) { get_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_LATCH1_REG + i, &value); + AB8500_IT_LATCH1_REG + ab8500_irq_regoffset[i], + &value); set_register_interruptible(ab8500, AB8500_INTERRUPT, - AB8500_IT_MASK1_REG + i, 0xff); + AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i], 0xff); } ret = abx500_register_ops(ab8500->dev, &ab8500_ops); @@ -495,7 +787,8 @@ int __devinit ab8500_init(struct ab8500 *ab8500) return ret; ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq, - IRQF_ONESHOT, "ab8500", ab8500); + IRQF_ONESHOT | IRQF_NO_SUSPEND, + "ab8500", ab8500); if (ret) goto out_removeirq; } @@ -506,6 +799,10 @@ int __devinit ab8500_init(struct ab8500 *ab8500) if (ret) goto out_freeirq; + ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group); + if (ret) + dev_err(ab8500->dev, "error creating sysfs entries\n"); + return ret; out_freeirq: @@ -519,6 +816,7 @@ out_removeirq: int __devexit ab8500_exit(struct ab8500 *ab8500) { + sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); mfd_remove_devices(ab8500->dev); if (ab8500->irq_base) { free_irq(ab8500->irq, ab8500); @@ -528,6 +826,6 @@ int __devexit ab8500_exit(struct ab8500 *ab8500) return 0; } -MODULE_AUTHOR("Srinidhi Kasagar, Rabin Vincent"); +MODULE_AUTHOR("Mattias Wallin, Srinidhi Kasagar, Rabin Vincent"); MODULE_DESCRIPTION("AB8500 MFD core"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-debugfs.c b/drivers/mfd/ab8500-debugfs.c index 8d1e05a..64748e4 100644 --- a/drivers/mfd/ab8500-debugfs.c +++ b/drivers/mfd/ab8500-debugfs.c @@ -24,9 +24,9 @@ static u32 debug_address; * @perm: access permissions for the range */ struct ab8500_reg_range { - u8 first; - u8 last; - u8 perm; + u8 first; + u8 last; + u8 perm; }; /** @@ -36,9 +36,9 @@ struct ab8500_reg_range { * @range: the list of register ranges */ struct ab8500_i2c_ranges { - u8 num_ranges; - u8 bankid; - const struct ab8500_reg_range *range; + u8 num_ranges; + u8 bankid; + const struct ab8500_reg_range *range; }; #define AB8500_NAME_STRING "ab8500" @@ -47,521 +47,521 @@ struct ab8500_i2c_ranges { #define AB8500_REV_REG 0x80 static struct ab8500_i2c_ranges debug_ranges[AB8500_NUM_BANKS] = { - [0x0] = { - .num_ranges = 0, - .range = 0, - }, - [AB8500_SYS_CTRL1_BLOCK] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x02, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_SYS_CTRL2_BLOCK] = { - .num_ranges = 4, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0D, - }, - { - .first = 0x0F, - .last = 0x17, - }, - { - .first = 0x30, - .last = 0x30, - }, - { - .first = 0x32, - .last = 0x33, - }, - }, - }, - [AB8500_REGU_CTRL1] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x03, - .last = 0x10, - }, - { - .first = 0x80, - .last = 0x84, - }, - }, - }, - [AB8500_REGU_CTRL2] = { - .num_ranges = 5, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x15, - }, - { - .first = 0x17, - .last = 0x19, - }, - { - .first = 0x1B, - .last = 0x1D, - }, - { - .first = 0x1F, - .last = 0x22, - }, - { - .first = 0x40, - .last = 0x44, - }, - /* 0x80-0x8B is SIM registers and should - * not be accessed from here */ - }, - }, - [AB8500_USB] = { - .num_ranges = 2, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x83, - }, - { - .first = 0x87, - .last = 0x8A, - }, - }, - }, - [AB8500_TVOUT] = { - .num_ranges = 9, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x12, - }, - { - .first = 0x15, - .last = 0x17, - }, - { - .first = 0x19, - .last = 0x21, - }, - { - .first = 0x27, - .last = 0x2C, - }, - { - .first = 0x41, - .last = 0x41, - }, - { - .first = 0x45, - .last = 0x5B, - }, - { - .first = 0x5D, - .last = 0x5D, - }, - { - .first = 0x69, - .last = 0x69, - }, - { - .first = 0x80, - .last = 0x81, - }, - }, - }, - [AB8500_DBI] = { - .num_ranges = 0, - .range = 0, - }, - [AB8500_ECI_AV_ACC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x80, - .last = 0x82, - }, - }, - }, - [0x9] = { - .num_ranges = 0, - .range = 0, - }, - [AB8500_GPADC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x08, - }, - }, - }, - [AB8500_CHARGER] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x03, - }, - { - .first = 0x05, - .last = 0x05, - }, - { - .first = 0x40, - .last = 0x40, - }, - { - .first = 0x42, - .last = 0x42, - }, - { - .first = 0x44, - .last = 0x44, - }, - { - .first = 0x50, - .last = 0x55, - }, - { - .first = 0x80, - .last = 0x82, - }, - { - .first = 0xC0, - .last = 0xC2, - }, - }, - }, - [AB8500_GAS_GAUGE] = { - .num_ranges = 3, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x00, - }, - { - .first = 0x07, - .last = 0x0A, - }, - { - .first = 0x10, - .last = 0x14, - }, - }, - }, - [AB8500_AUDIO] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x6F, - }, - }, - }, - [AB8500_INTERRUPT] = { - .num_ranges = 0, - .range = 0, - }, - [AB8500_RTC] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x0F, - }, - }, - }, - [AB8500_MISC] = { - .num_ranges = 8, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x00, - .last = 0x05, - }, - { - .first = 0x10, - .last = 0x15, - }, - { - .first = 0x20, - .last = 0x25, - }, - { - .first = 0x30, - .last = 0x35, - }, - { - .first = 0x40, - .last = 0x45, - }, - { - .first = 0x50, - .last = 0x50, - }, - { - .first = 0x60, - .last = 0x67, - }, - { - .first = 0x80, - .last = 0x80, - }, - }, - }, - [0x11] = { - .num_ranges = 0, - .range = 0, - }, - [0x12] = { - .num_ranges = 0, - .range = 0, - }, - [0x13] = { - .num_ranges = 0, - .range = 0, - }, - [0x14] = { - .num_ranges = 0, - .range = 0, - }, - [AB8500_OTP_EMUL] = { - .num_ranges = 1, - .range = (struct ab8500_reg_range[]) { - { - .first = 0x01, - .last = 0x0F, - }, - }, - }, + [0x0] = { + .num_ranges = 0, + .range = 0, + }, + [AB8500_SYS_CTRL1_BLOCK] = { + .num_ranges = 3, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x02, + }, + { + .first = 0x42, + .last = 0x42, + }, + { + .first = 0x80, + .last = 0x81, + }, + }, + }, + [AB8500_SYS_CTRL2_BLOCK] = { + .num_ranges = 4, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x0D, + }, + { + .first = 0x0F, + .last = 0x17, + }, + { + .first = 0x30, + .last = 0x30, + }, + { + .first = 0x32, + .last = 0x33, + }, + }, + }, + [AB8500_REGU_CTRL1] = { + .num_ranges = 3, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x00, + }, + { + .first = 0x03, + .last = 0x10, + }, + { + .first = 0x80, + .last = 0x84, + }, + }, + }, + [AB8500_REGU_CTRL2] = { + .num_ranges = 5, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x15, + }, + { + .first = 0x17, + .last = 0x19, + }, + { + .first = 0x1B, + .last = 0x1D, + }, + { + .first = 0x1F, + .last = 0x22, + }, + { + .first = 0x40, + .last = 0x44, + }, + /* 0x80-0x8B is SIM registers and should + * not be accessed from here */ + }, + }, + [AB8500_USB] = { + .num_ranges = 2, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x80, + .last = 0x83, + }, + { + .first = 0x87, + .last = 0x8A, + }, + }, + }, + [AB8500_TVOUT] = { + .num_ranges = 9, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x12, + }, + { + .first = 0x15, + .last = 0x17, + }, + { + .first = 0x19, + .last = 0x21, + }, + { + .first = 0x27, + .last = 0x2C, + }, + { + .first = 0x41, + .last = 0x41, + }, + { + .first = 0x45, + .last = 0x5B, + }, + { + .first = 0x5D, + .last = 0x5D, + }, + { + .first = 0x69, + .last = 0x69, + }, + { + .first = 0x80, + .last = 0x81, + }, + }, + }, + [AB8500_DBI] = { + .num_ranges = 0, + .range = NULL, + }, + [AB8500_ECI_AV_ACC] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x80, + .last = 0x82, + }, + }, + }, + [0x9] = { + .num_ranges = 0, + .range = NULL, + }, + [AB8500_GPADC] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + }, + }, + }, + [AB8500_CHARGER] = { + .num_ranges = 8, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x03, + }, + { + .first = 0x05, + .last = 0x05, + }, + { + .first = 0x40, + .last = 0x40, + }, + { + .first = 0x42, + .last = 0x42, + }, + { + .first = 0x44, + .last = 0x44, + }, + { + .first = 0x50, + .last = 0x55, + }, + { + .first = 0x80, + .last = 0x82, + }, + { + .first = 0xC0, + .last = 0xC2, + }, + }, + }, + [AB8500_GAS_GAUGE] = { + .num_ranges = 3, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x00, + }, + { + .first = 0x07, + .last = 0x0A, + }, + { + .first = 0x10, + .last = 0x14, + }, + }, + }, + [AB8500_AUDIO] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x6F, + }, + }, + }, + [AB8500_INTERRUPT] = { + .num_ranges = 0, + .range = NULL, + }, + [AB8500_RTC] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x0F, + }, + }, + }, + [AB8500_MISC] = { + .num_ranges = 8, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x00, + .last = 0x05, + }, + { + .first = 0x10, + .last = 0x15, + }, + { + .first = 0x20, + .last = 0x25, + }, + { + .first = 0x30, + .last = 0x35, + }, + { + .first = 0x40, + .last = 0x45, + }, + { + .first = 0x50, + .last = 0x50, + }, + { + .first = 0x60, + .last = 0x67, + }, + { + .first = 0x80, + .last = 0x80, + }, + }, + }, + [0x11] = { + .num_ranges = 0, + .range = NULL, + }, + [0x12] = { + .num_ranges = 0, + .range = NULL, + }, + [0x13] = { + .num_ranges = 0, + .range = NULL, + }, + [0x14] = { + .num_ranges = 0, + .range = NULL, + }, + [AB8500_OTP_EMUL] = { + .num_ranges = 1, + .range = (struct ab8500_reg_range[]) { + { + .first = 0x01, + .last = 0x0F, + }, + }, + }, }; static int ab8500_registers_print(struct seq_file *s, void *p) { - struct device *dev = s->private; - unsigned int i; - u32 bank = debug_bank; - - seq_printf(s, AB8500_NAME_STRING " register values:\n"); - - seq_printf(s, " bank %u:\n", bank); - for (i = 0; i < debug_ranges[bank].num_ranges; i++) { - u32 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - int err; - - err = abx500_get_register_interruptible(dev, - (u8)bank, (u8)reg, &value); - if (err < 0) { - dev_err(dev, "ab->read fail %d\n", err); - return err; - } - - err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank, - reg, value); - if (err < 0) { - dev_err(dev, "seq_printf overflow\n"); - /* Error is not returned here since - * the output is wanted in any case */ - return 0; - } - } - } - return 0; + struct device *dev = s->private; + unsigned int i; + u32 bank = debug_bank; + + seq_printf(s, AB8500_NAME_STRING " register values:\n"); + + seq_printf(s, " bank %u:\n", bank); + for (i = 0; i < debug_ranges[bank].num_ranges; i++) { + u32 reg; + + for (reg = debug_ranges[bank].range[i].first; + reg <= debug_ranges[bank].range[i].last; + reg++) { + u8 value; + int err; + + err = abx500_get_register_interruptible(dev, + (u8)bank, (u8)reg, &value); + if (err < 0) { + dev_err(dev, "ab->read fail %d\n", err); + return err; + } + + err = seq_printf(s, " [%u/0x%02X]: 0x%02X\n", bank, + reg, value); + if (err < 0) { + dev_err(dev, "seq_printf overflow\n"); + /* Error is not returned here since + * the output is wanted in any case */ + return 0; + } + } + } + return 0; } static int ab8500_registers_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_registers_print, inode->i_private); + return single_open(file, ab8500_registers_print, inode->i_private); } static const struct file_operations ab8500_registers_fops = { - .open = ab8500_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, + .open = ab8500_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, }; static int ab8500_bank_print(struct seq_file *s, void *p) { - return seq_printf(s, "%d\n", debug_bank); + return seq_printf(s, "%d\n", debug_bank); } static int ab8500_bank_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_bank_print, inode->i_private); + return single_open(file, ab8500_bank_print, inode->i_private); } static ssize_t ab8500_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) + const char __user *user_buf, + size_t count, loff_t *ppos) { - struct device *dev = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_bank; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf) - 1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_bank); - if (err) - return -EINVAL; - - if (user_bank >= AB8500_NUM_BANKS) { - dev_err(dev, "debugfs error input > number of banks\n"); - return -EINVAL; - } - - debug_bank = user_bank; - - return buf_size; + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB8500_NUM_BANKS) { + dev_err(dev, "debugfs error input > number of banks\n"); + return -EINVAL; + } + + debug_bank = user_bank; + + return buf_size; } static int ab8500_address_print(struct seq_file *s, void *p) { - return seq_printf(s, "0x%02X\n", debug_address); + return seq_printf(s, "0x%02X\n", debug_address); } static int ab8500_address_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_address_print, inode->i_private); + return single_open(file, ab8500_address_print, inode->i_private); } static ssize_t ab8500_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) + const char __user *user_buf, + size_t count, loff_t *ppos) { - struct device *dev = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_address; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf) - 1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_address); - if (err) - return -EINVAL; - if (user_address > 0xff) { - dev_err(dev, "debugfs error input > 0xff\n"); - return -EINVAL; - } - debug_address = user_address; - return buf_size; + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(dev, "debugfs error input > 0xff\n"); + return -EINVAL; + } + debug_address = user_address; + return buf_size; } static int ab8500_val_print(struct seq_file *s, void *p) { - struct device *dev = s->private; - int ret; - u8 regvalue; - - ret = abx500_get_register_interruptible(dev, - (u8)debug_bank, (u8)debug_address, ®value); - if (ret < 0) { - dev_err(dev, "abx500_get_reg fail %d, %d\n", - ret, __LINE__); - return -EINVAL; - } - seq_printf(s, "0x%02X\n", regvalue); - - return 0; + struct device *dev = s->private; + int ret; + u8 regvalue; + + ret = abx500_get_register_interruptible(dev, + (u8)debug_bank, (u8)debug_address, ®value); + if (ret < 0) { + dev_err(dev, "abx500_get_reg fail %d, %d\n", + ret, __LINE__); + return -EINVAL; + } + seq_printf(s, "0x%02X\n", regvalue); + + return 0; } static int ab8500_val_open(struct inode *inode, struct file *file) { - return single_open(file, ab8500_val_print, inode->i_private); + return single_open(file, ab8500_val_print, inode->i_private); } static ssize_t ab8500_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) + const char __user *user_buf, + size_t count, loff_t *ppos) { - struct device *dev = ((struct seq_file *)(file->private_data))->private; - char buf[32]; - int buf_size; - unsigned long user_val; - int err; - - /* Get userspace string and assure termination */ - buf_size = min(count, (sizeof(buf)-1)); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - buf[buf_size] = 0; - - err = strict_strtoul(buf, 0, &user_val); - if (err) - return -EINVAL; - if (user_val > 0xff) { - dev_err(dev, "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = abx500_set_register_interruptible(dev, - (u8)debug_bank, debug_address, (u8)user_val); - if (err < 0) { - printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__); - return -EINVAL; - } - - return buf_size; + struct device *dev = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(dev, "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = abx500_set_register_interruptible(dev, + (u8)debug_bank, debug_address, (u8)user_val); + if (err < 0) { + printk(KERN_ERR "abx500_set_reg failed %d, %d", err, __LINE__); + return -EINVAL; + } + + return buf_size; } static const struct file_operations ab8500_bank_fops = { - .open = ab8500_bank_open, - .write = ab8500_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, + .open = ab8500_bank_open, + .write = ab8500_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, }; static const struct file_operations ab8500_address_fops = { - .open = ab8500_address_open, - .write = ab8500_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, + .open = ab8500_address_open, + .write = ab8500_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, }; static const struct file_operations ab8500_val_fops = { - .open = ab8500_val_open, - .write = ab8500_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, + .open = ab8500_val_open, + .write = ab8500_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, }; static struct dentry *ab8500_dir; @@ -572,77 +572,77 @@ static struct dentry *ab8500_val_file; static int __devinit ab8500_debug_probe(struct platform_device *plf) { - debug_bank = AB8500_MISC; - debug_address = AB8500_REV_REG & 0x00FF; + debug_bank = AB8500_MISC; + debug_address = AB8500_REV_REG & 0x00FF; - ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); - if (!ab8500_dir) - goto exit_no_debugfs; + ab8500_dir = debugfs_create_dir(AB8500_NAME_STRING, NULL); + if (!ab8500_dir) + goto exit_no_debugfs; - ab8500_reg_file = debugfs_create_file("all-bank-registers", - S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops); - if (!ab8500_reg_file) - goto exit_destroy_dir; + ab8500_reg_file = debugfs_create_file("all-bank-registers", + S_IRUGO, ab8500_dir, &plf->dev, &ab8500_registers_fops); + if (!ab8500_reg_file) + goto exit_destroy_dir; - ab8500_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_bank_fops); - if (!ab8500_bank_file) - goto exit_destroy_reg; + ab8500_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_bank_fops); + if (!ab8500_bank_file) + goto exit_destroy_reg; - ab8500_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, - &ab8500_address_fops); - if (!ab8500_address_file) - goto exit_destroy_bank; + ab8500_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, + &ab8500_address_fops); + if (!ab8500_address_file) + goto exit_destroy_bank; - ab8500_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUGO), ab8500_dir, &plf->dev, &ab8500_val_fops); - if (!ab8500_val_file) - goto exit_destroy_address; + ab8500_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUSR), ab8500_dir, &plf->dev, &ab8500_val_fops); + if (!ab8500_val_file) + goto exit_destroy_address; - return 0; + return 0; exit_destroy_address: - debugfs_remove(ab8500_address_file); + debugfs_remove(ab8500_address_file); exit_destroy_bank: - debugfs_remove(ab8500_bank_file); + debugfs_remove(ab8500_bank_file); exit_destroy_reg: - debugfs_remove(ab8500_reg_file); + debugfs_remove(ab8500_reg_file); exit_destroy_dir: - debugfs_remove(ab8500_dir); + debugfs_remove(ab8500_dir); exit_no_debugfs: - dev_err(&plf->dev, "failed to create debugfs entries.\n"); - return -ENOMEM; + dev_err(&plf->dev, "failed to create debugfs entries.\n"); + return -ENOMEM; } static int __devexit ab8500_debug_remove(struct platform_device *plf) { - debugfs_remove(ab8500_val_file); - debugfs_remove(ab8500_address_file); - debugfs_remove(ab8500_bank_file); - debugfs_remove(ab8500_reg_file); - debugfs_remove(ab8500_dir); + debugfs_remove(ab8500_val_file); + debugfs_remove(ab8500_address_file); + debugfs_remove(ab8500_bank_file); + debugfs_remove(ab8500_reg_file); + debugfs_remove(ab8500_dir); - return 0; + return 0; } static struct platform_driver ab8500_debug_driver = { - .driver = { - .name = "ab8500-debug", - .owner = THIS_MODULE, - }, - .probe = ab8500_debug_probe, - .remove = __devexit_p(ab8500_debug_remove) + .driver = { + .name = "ab8500-debug", + .owner = THIS_MODULE, + }, + .probe = ab8500_debug_probe, + .remove = __devexit_p(ab8500_debug_remove) }; static int __init ab8500_debug_init(void) { - return platform_driver_register(&ab8500_debug_driver); + return platform_driver_register(&ab8500_debug_driver); } static void __exit ab8500_debug_exit(void) { - platform_driver_unregister(&ab8500_debug_driver); + platform_driver_unregister(&ab8500_debug_driver); } subsys_initcall(ab8500_debug_init); module_exit(ab8500_debug_exit); diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c new file mode 100644 index 0000000..6421ad1 --- /dev/null +++ b/drivers/mfd/ab8500-gpadc.c @@ -0,0 +1,614 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License v2 + * Author: Arun R Murthy <arun.murthy@stericsson.com> + * Author: Daniel Willerud <daniel.willerud@stericsson.com> + * Author: Johan Palsson <johan.palsson@stericsson.com> + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <linux/regulator/consumer.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/list.h> +#include <linux/mfd/ab8500.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/ab8500/gpadc.h> + +/* + * GPADC register offsets + * Bank : 0x0A + */ +#define AB8500_GPADC_CTRL1_REG 0x00 +#define AB8500_GPADC_CTRL2_REG 0x01 +#define AB8500_GPADC_CTRL3_REG 0x02 +#define AB8500_GPADC_AUTO_TIMER_REG 0x03 +#define AB8500_GPADC_STAT_REG 0x04 +#define AB8500_GPADC_MANDATAL_REG 0x05 +#define AB8500_GPADC_MANDATAH_REG 0x06 +#define AB8500_GPADC_AUTODATAL_REG 0x07 +#define AB8500_GPADC_AUTODATAH_REG 0x08 +#define AB8500_GPADC_MUX_CTRL_REG 0x09 + +/* + * OTP register offsets + * Bank : 0x15 + */ +#define AB8500_GPADC_CAL_1 0x0F +#define AB8500_GPADC_CAL_2 0x10 +#define AB8500_GPADC_CAL_3 0x11 +#define AB8500_GPADC_CAL_4 0x12 +#define AB8500_GPADC_CAL_5 0x13 +#define AB8500_GPADC_CAL_6 0x14 +#define AB8500_GPADC_CAL_7 0x15 + +/* gpadc constants */ +#define EN_VINTCORE12 0x04 +#define EN_VTVOUT 0x02 +#define EN_GPADC 0x01 +#define DIS_GPADC 0x00 +#define SW_AVG_16 0x60 +#define ADC_SW_CONV 0x04 +#define EN_ICHAR 0x80 +#define EN_BUF 0x40 +#define DIS_ZERO 0x00 +#define GPADC_BUSY 0x01 + +/* GPADC constants from AB8500 spec, UM0836 */ +#define ADC_RESOLUTION 1024 +#define ADC_CH_BTEMP_MIN 0 +#define ADC_CH_BTEMP_MAX 1350 +#define ADC_CH_DIETEMP_MIN 0 +#define ADC_CH_DIETEMP_MAX 1350 +#define ADC_CH_CHG_V_MIN 0 +#define ADC_CH_CHG_V_MAX 20030 +#define ADC_CH_ACCDET2_MIN 0 +#define ADC_CH_ACCDET2_MAX 2500 +#define ADC_CH_VBAT_MIN 2300 +#define ADC_CH_VBAT_MAX 4800 +#define ADC_CH_CHG_I_MIN 0 +#define ADC_CH_CHG_I_MAX 1500 +#define ADC_CH_BKBAT_MIN 0 +#define ADC_CH_BKBAT_MAX 3200 + +/* This is used to not lose precision when dividing to get gain and offset */ +#define CALIB_SCALE 1000 + +enum cal_channels { + ADC_INPUT_VMAIN = 0, + ADC_INPUT_BTEMP, + ADC_INPUT_VBAT, + NBR_CAL_INPUTS, +}; + +/** + * struct adc_cal_data - Table for storing gain and offset for the calibrated + * ADC channels + * @gain: Gain of the ADC channel + * @offset: Offset of the ADC channel + */ +struct adc_cal_data { + u64 gain; + u64 offset; +}; + +/** + * struct ab8500_gpadc - AB8500 GPADC device information + * @dev: pointer to the struct device + * @node: a list of AB8500 GPADCs, hence prepared for + reentrance + * @ab8500_gpadc_complete: pointer to the struct completion, to indicate + * the completion of gpadc conversion + * @ab8500_gpadc_lock: structure of type mutex + * @regu: pointer to the struct regulator + * @irq: interrupt number that is used by gpadc + * @cal_data array of ADC calibration data structs + */ +struct ab8500_gpadc { + struct device *dev; + struct list_head node; + struct completion ab8500_gpadc_complete; + struct mutex ab8500_gpadc_lock; + struct regulator *regu; + int irq; + struct adc_cal_data cal_data[NBR_CAL_INPUTS]; +}; + +static LIST_HEAD(ab8500_gpadc_list); + +/** + * ab8500_gpadc_get() - returns a reference to the primary AB8500 GPADC + * (i.e. the first GPADC in the instance list) + */ +struct ab8500_gpadc *ab8500_gpadc_get(char *name) +{ + struct ab8500_gpadc *gpadc; + + list_for_each_entry(gpadc, &ab8500_gpadc_list, node) { + if (!strcmp(name, dev_name(gpadc->dev))) + return gpadc; + } + + return ERR_PTR(-ENOENT); +} +EXPORT_SYMBOL(ab8500_gpadc_get); + +static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input, + int ad_value) +{ + int res; + + switch (input) { + case MAIN_CHARGER_V: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) { + res = ADC_CH_CHG_V_MIN + (ADC_CH_CHG_V_MAX - + ADC_CH_CHG_V_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VMAIN].gain + + gpadc->cal_data[ADC_INPUT_VMAIN].offset) / CALIB_SCALE; + break; + + case BAT_CTRL: + case BTEMP_BALL: + case ACC_DETECT1: + case ADC_AUX1: + case ADC_AUX2: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_BTEMP].gain) { + res = ADC_CH_BTEMP_MIN + (ADC_CH_BTEMP_MAX - + ADC_CH_BTEMP_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_BTEMP].gain + + gpadc->cal_data[ADC_INPUT_BTEMP].offset) / CALIB_SCALE; + break; + + case MAIN_BAT_V: + /* For some reason we don't have calibrated data */ + if (!gpadc->cal_data[ADC_INPUT_VBAT].gain) { + res = ADC_CH_VBAT_MIN + (ADC_CH_VBAT_MAX - + ADC_CH_VBAT_MIN) * ad_value / + ADC_RESOLUTION; + break; + } + /* Here we can use the calibrated data */ + res = (int) (ad_value * gpadc->cal_data[ADC_INPUT_VBAT].gain + + gpadc->cal_data[ADC_INPUT_VBAT].offset) / CALIB_SCALE; + break; + + case DIE_TEMP: + res = ADC_CH_DIETEMP_MIN + + (ADC_CH_DIETEMP_MAX - ADC_CH_DIETEMP_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case ACC_DETECT2: + res = ADC_CH_ACCDET2_MIN + + (ADC_CH_ACCDET2_MAX - ADC_CH_ACCDET2_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case VBUS_V: + res = ADC_CH_CHG_V_MIN + + (ADC_CH_CHG_V_MAX - ADC_CH_CHG_V_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case MAIN_CHARGER_C: + case USB_CHARGER_C: + res = ADC_CH_CHG_I_MIN + + (ADC_CH_CHG_I_MAX - ADC_CH_CHG_I_MIN) * ad_value / + ADC_RESOLUTION; + break; + + case BK_BAT_V: + res = ADC_CH_BKBAT_MIN + + (ADC_CH_BKBAT_MAX - ADC_CH_BKBAT_MIN) * ad_value / + ADC_RESOLUTION; + break; + + default: + dev_err(gpadc->dev, + "unknown channel, not possible to convert\n"); + res = -EINVAL; + break; + + } + return res; +} + +/** + * ab8500_gpadc_convert() - gpadc conversion + * @input: analog input to be converted to digital data + * + * This function converts the selected analog i/p to digital + * data. + */ +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) +{ + int ret; + u16 data = 0; + int looplimit = 0; + u8 val, low_data, high_data; + + if (!gpadc) + return -ENODEV; + + mutex_lock(&gpadc->ab8500_gpadc_lock); + /* Enable VTVout LDO this is required for GPADC */ + regulator_enable(gpadc->regu); + + /* Check if ADC is not busy, lock and proceed */ + do { + ret = abx500_get_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_STAT_REG, &val); + if (ret < 0) + goto out; + if (!(val & GPADC_BUSY)) + break; + msleep(10); + } while (++looplimit < 10); + if (looplimit >= 10 && (val & GPADC_BUSY)) { + dev_err(gpadc->dev, "gpadc_conversion: GPADC busy"); + ret = -EINVAL; + goto out; + } + + /* Enable GPADC */ + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_GPADC, EN_GPADC); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: enable gpadc failed\n"); + goto out; + } + /* Select the input source and set average samples to 16 */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: set avg samples failed\n"); + goto out; + } + /* + * Enable ADC, buffering, select rising edge and enable ADC path + * charging current sense if it needed + */ + switch (input) { + case MAIN_CHARGER_C: + case USB_CHARGER_C: + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, + EN_BUF | EN_ICHAR, + EN_BUF | EN_ICHAR); + break; + default: + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, EN_BUF, EN_BUF); + break; + } + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: select falling edge failed\n"); + goto out; + } + ret = abx500_mask_and_set_register_interruptible(gpadc->dev, + AB8500_GPADC, AB8500_GPADC_CTRL1_REG, ADC_SW_CONV, ADC_SW_CONV); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: start s/w conversion failed\n"); + goto out; + } + /* wait for completion of conversion */ + if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) { + dev_err(gpadc->dev, + "timeout: didn't receive GPADC conversion interrupt\n"); + ret = -EINVAL; + goto out; + } + + /* Read the converted RAW data */ + ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_MANDATAL_REG, &low_data); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: read low data failed\n"); + goto out; + } + + ret = abx500_get_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_MANDATAH_REG, &high_data); + if (ret < 0) { + dev_err(gpadc->dev, + "gpadc_conversion: read high data failed\n"); + goto out; + } + + data = (high_data << 8) | low_data; + /* Disable GPADC */ + ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, DIS_GPADC); + if (ret < 0) { + dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n"); + goto out; + } + /* Disable VTVout LDO this is required for GPADC */ + regulator_disable(gpadc->regu); + mutex_unlock(&gpadc->ab8500_gpadc_lock); + ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data); + return ret; + +out: + /* + * It has shown to be needed to turn off the GPADC if an error occurs, + * otherwise we might have problem when waiting for the busy bit in the + * GPADC status register to go low. In V1.1 there wait_for_completion + * seems to timeout when waiting for an interrupt.. Not seen in V2.0 + */ + (void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, + AB8500_GPADC_CTRL1_REG, DIS_GPADC); + regulator_disable(gpadc->regu); + mutex_unlock(&gpadc->ab8500_gpadc_lock); + dev_err(gpadc->dev, + "gpadc_conversion: Failed to AD convert channel %d\n", input); + return ret; +} +EXPORT_SYMBOL(ab8500_gpadc_convert); + +/** + * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion + * @irq: irq number + * @data: pointer to the data passed during request irq + * + * This is a interrupt service routine for s/w gpadc conversion completion. + * Notifies the gpadc completion is completed and the converted raw value + * can be read from the registers. + * Returns IRQ status(IRQ_HANDLED) + */ +static irqreturn_t ab8500_bm_gpswadcconvend_handler(int irq, void *_gpadc) +{ + struct ab8500_gpadc *gpadc = _gpadc; + + complete(&gpadc->ab8500_gpadc_complete); + + return IRQ_HANDLED; +} + +static int otp_cal_regs[] = { + AB8500_GPADC_CAL_1, + AB8500_GPADC_CAL_2, + AB8500_GPADC_CAL_3, + AB8500_GPADC_CAL_4, + AB8500_GPADC_CAL_5, + AB8500_GPADC_CAL_6, + AB8500_GPADC_CAL_7, +}; + +static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc) +{ + int i; + int ret[ARRAY_SIZE(otp_cal_regs)]; + u8 gpadc_cal[ARRAY_SIZE(otp_cal_regs)]; + + int vmain_high, vmain_low; + int btemp_high, btemp_low; + int vbat_high, vbat_low; + + /* First we read all OTP registers and store the error code */ + for (i = 0; i < ARRAY_SIZE(otp_cal_regs); i++) { + ret[i] = abx500_get_register_interruptible(gpadc->dev, + AB8500_OTP_EMUL, otp_cal_regs[i], &gpadc_cal[i]); + if (ret[i] < 0) + dev_err(gpadc->dev, "%s: read otp reg 0x%02x failed\n", + __func__, otp_cal_regs[i]); + } + + /* + * The ADC calibration data is stored in OTP registers. + * The layout of the calibration data is outlined below and a more + * detailed description can be found in UM0836 + * + * vm_h/l = vmain_high/low + * bt_h/l = btemp_high/low + * vb_h/l = vbat_high/low + * + * Data bits: + * | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h9 | vm_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | | vm_h7 | vm_h6 | vm_h5 | vm_h4 | vm_h3 | vm_h2 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vm_h1 | vm_h0 | vm_l4 | vm_l3 | vm_l2 | vm_l1 | vm_l0 | bt_h9 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h8 | bt_h7 | bt_h6 | bt_h5 | bt_h4 | bt_h3 | bt_h2 | bt_h1 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | bt_h0 | bt_l4 | bt_l3 | bt_l2 | bt_l1 | bt_l0 | vb_h9 | vb_h8 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_h7 | vb_h6 | vb_h5 | vb_h4 | vb_h3 | vb_h2 | vb_h1 | vb_h0 + * |.......|.......|.......|.......|.......|.......|.......|....... + * | vb_l5 | vb_l4 | vb_l3 | vb_l2 | vb_l1 | vb_l0 | + * |.......|.......|.......|.......|.......|.......|.......|....... + * + * + * Ideal output ADC codes corresponding to injected input voltages + * during manufacturing is: + * + * vmain_high: Vin = 19500mV / ADC ideal code = 997 + * vmain_low: Vin = 315mV / ADC ideal code = 16 + * btemp_high: Vin = 1300mV / ADC ideal code = 985 + * btemp_low: Vin = 21mV / ADC ideal code = 16 + * vbat_high: Vin = 4700mV / ADC ideal code = 982 + * vbat_low: Vin = 2380mV / ADC ideal code = 33 + */ + + /* Calculate gain and offset for VMAIN if all reads succeeded */ + if (!(ret[0] < 0 || ret[1] < 0 || ret[2] < 0)) { + vmain_high = (((gpadc_cal[0] & 0x03) << 8) | + ((gpadc_cal[1] & 0x3F) << 2) | + ((gpadc_cal[2] & 0xC0) >> 6)); + + vmain_low = ((gpadc_cal[2] & 0x3E) >> 1); + + gpadc->cal_data[ADC_INPUT_VMAIN].gain = CALIB_SCALE * + (19500 - 315) / (vmain_high - vmain_low); + + gpadc->cal_data[ADC_INPUT_VMAIN].offset = CALIB_SCALE * 19500 - + (CALIB_SCALE * (19500 - 315) / + (vmain_high - vmain_low)) * vmain_high; + } else { + gpadc->cal_data[ADC_INPUT_VMAIN].gain = 0; + } + + /* Calculate gain and offset for BTEMP if all reads succeeded */ + if (!(ret[2] < 0 || ret[3] < 0 || ret[4] < 0)) { + btemp_high = (((gpadc_cal[2] & 0x01) << 9) | + (gpadc_cal[3] << 1) | + ((gpadc_cal[4] & 0x80) >> 7)); + + btemp_low = ((gpadc_cal[4] & 0x7C) >> 2); + + gpadc->cal_data[ADC_INPUT_BTEMP].gain = + CALIB_SCALE * (1300 - 21) / (btemp_high - btemp_low); + + gpadc->cal_data[ADC_INPUT_BTEMP].offset = CALIB_SCALE * 1300 - + (CALIB_SCALE * (1300 - 21) / + (btemp_high - btemp_low)) * btemp_high; + } else { + gpadc->cal_data[ADC_INPUT_BTEMP].gain = 0; + } + + /* Calculate gain and offset for VBAT if all reads succeeded */ + if (!(ret[4] < 0 || ret[5] < 0 || ret[6] < 0)) { + vbat_high = (((gpadc_cal[4] & 0x03) << 8) | gpadc_cal[5]); + vbat_low = ((gpadc_cal[6] & 0xFC) >> 2); + + gpadc->cal_data[ADC_INPUT_VBAT].gain = CALIB_SCALE * + (4700 - 2380) / (vbat_high - vbat_low); + + gpadc->cal_data[ADC_INPUT_VBAT].offset = CALIB_SCALE * 4700 - + (CALIB_SCALE * (4700 - 2380) / + (vbat_high - vbat_low)) * vbat_high; + } else { + gpadc->cal_data[ADC_INPUT_VBAT].gain = 0; + } + + dev_dbg(gpadc->dev, "VMAIN gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_VMAIN].gain, + gpadc->cal_data[ADC_INPUT_VMAIN].offset); + + dev_dbg(gpadc->dev, "BTEMP gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_BTEMP].gain, + gpadc->cal_data[ADC_INPUT_BTEMP].offset); + + dev_dbg(gpadc->dev, "VBAT gain %llu offset %llu\n", + gpadc->cal_data[ADC_INPUT_VBAT].gain, + gpadc->cal_data[ADC_INPUT_VBAT].offset); +} + +static int __devinit ab8500_gpadc_probe(struct platform_device *pdev) +{ + int ret = 0; + struct ab8500_gpadc *gpadc; + + gpadc = kzalloc(sizeof(struct ab8500_gpadc), GFP_KERNEL); + if (!gpadc) { + dev_err(&pdev->dev, "Error: No memory\n"); + return -ENOMEM; + } + + gpadc->irq = platform_get_irq_byname(pdev, "SW_CONV_END"); + if (gpadc->irq < 0) { + dev_err(gpadc->dev, "failed to get platform irq-%d\n", + gpadc->irq); + ret = gpadc->irq; + goto fail; + } + + gpadc->dev = &pdev->dev; + mutex_init(&gpadc->ab8500_gpadc_lock); + + /* Initialize completion used to notify completion of conversion */ + init_completion(&gpadc->ab8500_gpadc_complete); + + /* Register interrupt - SwAdcComplete */ + ret = request_threaded_irq(gpadc->irq, NULL, + ab8500_bm_gpswadcconvend_handler, + IRQF_NO_SUSPEND | IRQF_SHARED, "ab8500-gpadc", gpadc); + if (ret < 0) { + dev_err(gpadc->dev, "Failed to register interrupt, irq: %d\n", + gpadc->irq); + goto fail; + } + + /* VTVout LDO used to power up ab8500-GPADC */ + gpadc->regu = regulator_get(&pdev->dev, "vddadc"); + if (IS_ERR(gpadc->regu)) { + ret = PTR_ERR(gpadc->regu); + dev_err(gpadc->dev, "failed to get vtvout LDO\n"); + goto fail_irq; + } + ab8500_gpadc_read_calibration_data(gpadc); + list_add_tail(&gpadc->node, &ab8500_gpadc_list); + dev_dbg(gpadc->dev, "probe success\n"); + return 0; +fail_irq: + free_irq(gpadc->irq, gpadc); +fail: + kfree(gpadc); + gpadc = NULL; + return ret; +} + +static int __devexit ab8500_gpadc_remove(struct platform_device *pdev) +{ + struct ab8500_gpadc *gpadc = platform_get_drvdata(pdev); + + /* remove this gpadc entry from the list */ + list_del(&gpadc->node); + /* remove interrupt - completion of Sw ADC conversion */ + free_irq(gpadc->irq, gpadc); + /* disable VTVout LDO that is being used by GPADC */ + regulator_put(gpadc->regu); + kfree(gpadc); + gpadc = NULL; + return 0; +} + +static struct platform_driver ab8500_gpadc_driver = { + .probe = ab8500_gpadc_probe, + .remove = __devexit_p(ab8500_gpadc_remove), + .driver = { + .name = "ab8500-gpadc", + .owner = THIS_MODULE, + }, +}; + +static int __init ab8500_gpadc_init(void) +{ + return platform_driver_register(&ab8500_gpadc_driver); +} + +static void __exit ab8500_gpadc_exit(void) +{ + platform_driver_unregister(&ab8500_gpadc_driver); +} + +subsys_initcall_sync(ab8500_gpadc_init); +module_exit(ab8500_gpadc_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Arun R Murthy, Daniel Willerud, Johan Palsson"); +MODULE_ALIAS("platform:ab8500_gpadc"); +MODULE_DESCRIPTION("AB8500 GPADC driver"); diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c index 6820327..821e6b8 100644 --- a/drivers/mfd/ab8500-i2c.c +++ b/drivers/mfd/ab8500-i2c.c @@ -97,7 +97,7 @@ static void __exit ab8500_i2c_exit(void) { platform_driver_unregister(&ab8500_i2c_driver); } -subsys_initcall(ab8500_i2c_init); +arch_initcall(ab8500_i2c_init); module_exit(ab8500_i2c_exit); MODULE_AUTHOR("Mattias WALLIN <mattias.wallin@stericsson.com"); diff --git a/drivers/mfd/ab8500-spi.c b/drivers/mfd/ab8500-spi.c deleted file mode 100644 index b165342..0000000 --- a/drivers/mfd/ab8500-spi.c +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License v2 - * Author: Srinidhi Kasagar <srinidhi.kasagar@stericsson.com> - */ - -#include <linux/kernel.h> -#include <linux/slab.h> -#include <linux/init.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <linux/mfd/ab8500.h> - -/* - * This funtion writes to any AB8500 registers using - * SPI protocol & before it writes it packs the data - * in the below 24 bit frame format - * - * *|------------------------------------| - * *| 23|22...18|17.......10|9|8|7......0| - * *| r/w bank adr data | - * * ------------------------------------ - * - * This function shouldn't be called from interrupt - * context - */ -static int ab8500_spi_write(struct ab8500 *ab8500, u16 addr, u8 data) -{ - struct spi_device *spi = container_of(ab8500->dev, struct spi_device, - dev); - unsigned long spi_data = addr << 10 | data; - struct spi_transfer xfer; - struct spi_message msg; - - ab8500->tx_buf[0] = spi_data; - ab8500->rx_buf[0] = 0; - - xfer.tx_buf = ab8500->tx_buf; - xfer.rx_buf = NULL; - xfer.len = sizeof(unsigned long); - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - return spi_sync(spi, &msg); -} - -static int ab8500_spi_read(struct ab8500 *ab8500, u16 addr) -{ - struct spi_device *spi = container_of(ab8500->dev, struct spi_device, - dev); - unsigned long spi_data = 1 << 23 | addr << 10; - struct spi_transfer xfer; - struct spi_message msg; - int ret; - - ab8500->tx_buf[0] = spi_data; - ab8500->rx_buf[0] = 0; - - xfer.tx_buf = ab8500->tx_buf; - xfer.rx_buf = ab8500->rx_buf; - xfer.len = sizeof(unsigned long); - - spi_message_init(&msg); - spi_message_add_tail(&xfer, &msg); - - ret = spi_sync(spi, &msg); - if (!ret) - /* - * Only the 8 lowermost bytes are - * defined with value, the rest may - * vary depending on chip/board noise. - */ - ret = ab8500->rx_buf[0] & 0xFFU; - - return ret; -} - -static int __devinit ab8500_spi_probe(struct spi_device *spi) -{ - struct ab8500 *ab8500; - int ret; - - spi->bits_per_word = 24; - ret = spi_setup(spi); - if (ret < 0) - return ret; - - ab8500 = kzalloc(sizeof *ab8500, GFP_KERNEL); - if (!ab8500) - return -ENOMEM; - - ab8500->dev = &spi->dev; - ab8500->irq = spi->irq; - - ab8500->read = ab8500_spi_read; - ab8500->write = ab8500_spi_write; - - spi_set_drvdata(spi, ab8500); - - ret = ab8500_init(ab8500); - if (ret) - kfree(ab8500); - - return ret; -} - -static int __devexit ab8500_spi_remove(struct spi_device *spi) -{ - struct ab8500 *ab8500 = spi_get_drvdata(spi); - - ab8500_exit(ab8500); - kfree(ab8500); - - return 0; -} - -static struct spi_driver ab8500_spi_driver = { - .driver = { - .name = "ab8500-spi", - .owner = THIS_MODULE, - }, - .probe = ab8500_spi_probe, - .remove = __devexit_p(ab8500_spi_remove) -}; - -static int __init ab8500_spi_init(void) -{ - return spi_register_driver(&ab8500_spi_driver); -} -subsys_initcall(ab8500_spi_init); - -static void __exit ab8500_spi_exit(void) -{ - spi_unregister_driver(&ab8500_spi_driver); -} -module_exit(ab8500_spi_exit); - -MODULE_AUTHOR("Srinidhi KASAGAR <srinidhi.kasagar@stericsson.com"); -MODULE_DESCRIPTION("AB8500 SPI"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/ab8500-sysctrl.c b/drivers/mfd/ab8500-sysctrl.c new file mode 100644 index 0000000..3921859 --- /dev/null +++ b/drivers/mfd/ab8500-sysctrl.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * Author: Mattias Nilsson <mattias.i.nilsson@stericsson.com> for ST Ericsson. + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/mfd/ab8500.h> +#include <linux/mfd/abx500.h> +#include <linux/mfd/ab8500/sysctrl.h> + +static struct device *sysctrl_dev; + +static inline bool valid_bank(u8 bank) +{ + return ((bank == AB8500_SYS_CTRL1_BLOCK) || + (bank == AB8500_SYS_CTRL2_BLOCK)); +} + +int ab8500_sysctrl_read(u16 reg, u8 *value) +{ + u8 bank; + + if (sysctrl_dev == NULL) + return -EAGAIN; + + bank = (reg >> 8); + if (!valid_bank(bank)) + return -EINVAL; + + return abx500_get_register_interruptible(sysctrl_dev, bank, + (u8)(reg & 0xFF), value); +} + +int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value) +{ + u8 bank; + + if (sysctrl_dev == NULL) + return -EAGAIN; + + bank = (reg >> 8); + if (!valid_bank(bank)) + return -EINVAL; + + return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank, + (u8)(reg & 0xFF), mask, value); +} + +static int __devinit ab8500_sysctrl_probe(struct platform_device *pdev) +{ + sysctrl_dev = &pdev->dev; + return 0; +} + +static int __devexit ab8500_sysctrl_remove(struct platform_device *pdev) +{ + sysctrl_dev = NULL; + return 0; +} + +static struct platform_driver ab8500_sysctrl_driver = { + .driver = { + .name = "ab8500-sysctrl", + .owner = THIS_MODULE, + }, + .probe = ab8500_sysctrl_probe, + .remove = __devexit_p(ab8500_sysctrl_remove), +}; + +static int __init ab8500_sysctrl_init(void) +{ + return platform_driver_register(&ab8500_sysctrl_driver); +} +subsys_initcall(ab8500_sysctrl_init); + +MODULE_AUTHOR("Mattias Nilsson <mattias.i.nilsson@stericsson.com"); +MODULE_DESCRIPTION("AB8500 system control driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/adp5520.c b/drivers/mfd/adp5520.c index 3122139..f1d8848 100644 --- a/drivers/mfd/adp5520.c +++ b/drivers/mfd/adp5520.c @@ -321,27 +321,27 @@ static int __devexit adp5520_remove(struct i2c_client *client) } #ifdef CONFIG_PM -static int adp5520_suspend(struct i2c_client *client, - pm_message_t state) +static int adp5520_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct adp5520_chip *chip = dev_get_drvdata(&client->dev); adp5520_clr_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); return 0; } -static int adp5520_resume(struct i2c_client *client) +static int adp5520_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct adp5520_chip *chip = dev_get_drvdata(&client->dev); adp5520_set_bits(chip->dev, ADP5520_MODE_STATUS, ADP5520_nSTNBY); return 0; } -#else -#define adp5520_suspend NULL -#define adp5520_resume NULL #endif +static SIMPLE_DEV_PM_OPS(adp5520_pm, adp5520_suspend, adp5520_resume); + static const struct i2c_device_id adp5520_id[] = { { "pmic-adp5520", ID_ADP5520 }, { "pmic-adp5501", ID_ADP5501 }, @@ -353,11 +353,10 @@ static struct i2c_driver adp5520_driver = { .driver = { .name = "adp5520", .owner = THIS_MODULE, + .pm = &adp5520_pm, }, .probe = adp5520_probe, .remove = __devexit_p(adp5520_remove), - .suspend = adp5520_suspend, - .resume = adp5520_resume, .id_table = adp5520_id, }; diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index 7de708d..d4a851c 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -57,7 +57,7 @@ struct asic3_clk { .rate = _rate, \ } -struct asic3_clk asic3_clk_init[] __initdata = { +static struct asic3_clk asic3_clk_init[] __initdata = { INIT_CDEX(SPI, 0), INIT_CDEX(OWM, 5000000), INIT_CDEX(PWM0, 0), @@ -102,7 +102,7 @@ static inline u32 asic3_read_register(struct asic3 *asic, (reg >> asic->bus_shift)); } -void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) +static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set) { unsigned long flags; u32 val; @@ -139,13 +139,12 @@ static void asic3_irq_flip_edge(struct asic3 *asic, static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) { + struct asic3 *asic = irq_desc_get_handler_data(desc); + struct irq_data *data = irq_desc_get_irq_data(desc); int iter, i; unsigned long flags; - struct asic3 *asic; - - desc->chip->ack(irq); - asic = desc->handler_data; + data->chip->irq_ack(irq_data); for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { u32 status; @@ -188,8 +187,7 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) irqnr = asic->irq_base + (ASIC3_GPIOS_PER_BANK * bank) + i; - desc = irq_to_desc(irqnr); - desc->handle_irq(irqnr, desc); + generic_handle_irq(irqnr); if (asic->irq_bothedge[bank] & bit) asic3_irq_flip_edge(asic, base, bit); @@ -200,11 +198,8 @@ static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) /* Handle remaining IRQs in the status register */ for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) { /* They start at bit 4 and go up */ - if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) { - desc = irq_to_desc(asic->irq_base + i); - desc->handle_irq(asic->irq_base + i, - desc); - } + if (status & (1 << (i - ASIC3_NUM_GPIOS + 4))) + generic_handle_irq(asic->irq_base + i); } } @@ -226,14 +221,14 @@ static inline int asic3_irq_to_index(struct asic3 *asic, int irq) return (irq - asic->irq_base) & 0xf; } -static void asic3_mask_gpio_irq(unsigned int irq) +static void asic3_mask_gpio_irq(struct irq_data *data) { - struct asic3 *asic = get_irq_chip_data(irq); + struct asic3 *asic = irq_data_get_irq_chip_data(data); u32 val, bank, index; unsigned long flags; - bank = asic3_irq_to_bank(asic, irq); - index = asic3_irq_to_index(asic, irq); + bank = asic3_irq_to_bank(asic, data->irq); + index = asic3_irq_to_index(asic, data->irq); spin_lock_irqsave(&asic->lock, flags); val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); @@ -242,9 +237,9 @@ static void asic3_mask_gpio_irq(unsigned int irq) spin_unlock_irqrestore(&asic->lock, flags); } -static void asic3_mask_irq(unsigned int irq) +static void asic3_mask_irq(struct irq_data *data) { - struct asic3 *asic = get_irq_chip_data(irq); + struct asic3 *asic = irq_data_get_irq_chip_data(data); int regval; unsigned long flags; @@ -254,7 +249,7 @@ static void asic3_mask_irq(unsigned int irq) ASIC3_INTR_INT_MASK); regval &= ~(ASIC3_INTMASK_MASK0 << - (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); + (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS))); asic3_write_register(asic, ASIC3_INTR_BASE + @@ -263,14 +258,14 @@ static void asic3_mask_irq(unsigned int irq) spin_unlock_irqrestore(&asic->lock, flags); } -static void asic3_unmask_gpio_irq(unsigned int irq) +static void asic3_unmask_gpio_irq(struct irq_data *data) { - struct asic3 *asic = get_irq_chip_data(irq); + struct asic3 *asic = irq_data_get_irq_chip_data(data); u32 val, bank, index; unsigned long flags; - bank = asic3_irq_to_bank(asic, irq); - index = asic3_irq_to_index(asic, irq); + bank = asic3_irq_to_bank(asic, data->irq); + index = asic3_irq_to_index(asic, data->irq); spin_lock_irqsave(&asic->lock, flags); val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK); @@ -279,9 +274,9 @@ static void asic3_unmask_gpio_irq(unsigned int irq) spin_unlock_irqrestore(&asic->lock, flags); } -static void asic3_unmask_irq(unsigned int irq) +static void asic3_unmask_irq(struct irq_data *data) { - struct asic3 *asic = get_irq_chip_data(irq); + struct asic3 *asic = irq_data_get_irq_chip_data(data); int regval; unsigned long flags; @@ -291,7 +286,7 @@ static void asic3_unmask_irq(unsigned int irq) ASIC3_INTR_INT_MASK); regval |= (ASIC3_INTMASK_MASK0 << - (irq - (asic->irq_base + ASIC3_NUM_GPIOS))); + (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS))); asic3_write_register(asic, ASIC3_INTR_BASE + @@ -300,15 +295,15 @@ static void asic3_unmask_irq(unsigned int irq) spin_unlock_irqrestore(&asic->lock, flags); } -static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) +static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type) { - struct asic3 *asic = get_irq_chip_data(irq); + struct asic3 *asic = irq_data_get_irq_chip_data(data); u32 bank, index; u16 trigger, level, edge, bit; unsigned long flags; - bank = asic3_irq_to_bank(asic, irq); - index = asic3_irq_to_index(asic, irq); + bank = asic3_irq_to_bank(asic, data->irq); + index = asic3_irq_to_index(asic, data->irq); bit = 1<<index; spin_lock_irqsave(&asic->lock, flags); @@ -318,7 +313,7 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) bank + ASIC3_GPIO_EDGE_TRIGGER); trigger = asic3_read_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE); - asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit; + asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] &= ~bit; if (type == IRQ_TYPE_EDGE_RISING) { trigger |= bit; @@ -328,11 +323,11 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) edge &= ~bit; } else if (type == IRQ_TYPE_EDGE_BOTH) { trigger |= bit; - if (asic3_gpio_get(&asic->gpio, irq - asic->irq_base)) + if (asic3_gpio_get(&asic->gpio, data->irq - asic->irq_base)) edge &= ~bit; else edge |= bit; - asic->irq_bothedge[(irq - asic->irq_base) >> 4] |= bit; + asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] |= bit; } else if (type == IRQ_TYPE_LEVEL_LOW) { trigger &= ~bit; level &= ~bit; @@ -359,17 +354,17 @@ static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) static struct irq_chip asic3_gpio_irq_chip = { .name = "ASIC3-GPIO", - .ack = asic3_mask_gpio_irq, - .mask = asic3_mask_gpio_irq, - .unmask = asic3_unmask_gpio_irq, - .set_type = asic3_gpio_irq_type, + .irq_ack = asic3_mask_gpio_irq, + .irq_mask = asic3_mask_gpio_irq, + .irq_unmask = asic3_unmask_gpio_irq, + .irq_set_type = asic3_gpio_irq_type, }; static struct irq_chip asic3_irq_chip = { .name = "ASIC3", - .ack = asic3_mask_irq, - .mask = asic3_mask_irq, - .unmask = asic3_unmask_irq, + .irq_ack = asic3_mask_irq, + .irq_mask = asic3_mask_irq, + .irq_unmask = asic3_unmask_irq, }; static int __init asic3_irq_probe(struct platform_device *pdev) @@ -393,21 +388,21 @@ static int __init asic3_irq_probe(struct platform_device *pdev) for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { if (irq < asic->irq_base + ASIC3_NUM_GPIOS) - set_irq_chip(irq, &asic3_gpio_irq_chip); + irq_set_chip(irq, &asic3_gpio_irq_chip); else - set_irq_chip(irq, &asic3_irq_chip); + irq_set_chip(irq, &asic3_irq_chip); - set_irq_chip_data(irq, asic); - set_irq_handler(irq, handle_level_irq); + irq_set_chip_data(irq, asic); + irq_set_handler(irq, handle_level_irq); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK), ASIC3_INTMASK_GINTMASK); - set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); - set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); - set_irq_data(asic->irq_nr, asic); + irq_set_chained_handler(asic->irq_nr, asic3_irq_demux); + irq_set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(asic->irq_nr, asic); return 0; } @@ -421,11 +416,10 @@ static void asic3_irq_remove(struct platform_device *pdev) for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) { set_irq_flags(irq, 0); - set_irq_handler(irq, NULL); - set_irq_chip(irq, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } - set_irq_chained_handler(asic->irq_nr, NULL); + irq_set_chained_handler(asic->irq_nr, NULL); } /* GPIOs */ @@ -635,7 +629,7 @@ static struct resource ds1wm_resources[] = { }, { .start = ASIC3_IRQ_OWM, - .start = ASIC3_IRQ_OWM, + .end = ASIC3_IRQ_OWM, .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE, }, }; @@ -682,7 +676,7 @@ static struct mfd_cell asic3_cell_ds1wm = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .driver_data = &ds1wm_pdata, + .mfd_data = &ds1wm_pdata, .num_resources = ARRAY_SIZE(ds1wm_resources), .resources = ds1wm_resources, }; @@ -783,7 +777,7 @@ static struct mfd_cell asic3_cell_mmc = { .name = "tmio-mmc", .enable = asic3_mmc_enable, .disable = asic3_mmc_disable, - .driver_data = &asic3_mmc_data, + .mfd_data = &asic3_mmc_data, .num_resources = ARRAY_SIZE(asic3_mmc_resources), .resources = asic3_mmc_resources, }; @@ -810,9 +804,6 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, ds1wm_resources[0].start >>= asic->bus_shift; ds1wm_resources[0].end >>= asic->bus_shift; - asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm; - asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm); - /* MMC */ asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) + mem_sdio->start, 0x400 >> asic->bus_shift); @@ -824,9 +815,6 @@ static int __init asic3_mfd_probe(struct platform_device *pdev, asic3_mmc_resources[0].start >>= asic->bus_shift; asic3_mmc_resources[0].end >>= asic->bus_shift; - asic3_cell_mmc.platform_data = &asic3_cell_mmc; - asic3_cell_mmc.data_size = sizeof(asic3_cell_mmc); - ret = mfd_add_devices(&pdev->dev, pdev->id, &asic3_cell_ds1wm, 1, mem, asic->irq_base); if (ret < 0) diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c new file mode 100644 index 0000000..155fa04 --- /dev/null +++ b/drivers/mfd/cs5535-mfd.c @@ -0,0 +1,204 @@ +/* + * cs5535-mfd.c - core MFD driver for CS5535/CS5536 southbridges + * + * The CS5535 and CS5536 has an ISA bridge on the PCI bus that is + * used for accessing GPIOs, MFGPTs, ACPI, etc. Each subdevice has + * an IO range that's specified in a single BAR. The BAR order is + * hardcoded in the CS553x specifications. + * + * Copyright (c) 2010 Andres Salomon <dilinger@queued.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mfd/core.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <asm/olpc.h> + +#define DRV_NAME "cs5535-mfd" + +enum cs5535_mfd_bars { + SMB_BAR = 0, + GPIO_BAR = 1, + MFGPT_BAR = 2, + PMS_BAR = 4, + ACPI_BAR = 5, + NR_BARS, +}; + +static int cs5535_mfd_res_enable(struct platform_device *pdev) +{ + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -EIO; + } + + if (!request_region(res->start, resource_size(res), DRV_NAME)) { + dev_err(&pdev->dev, "can't request region\n"); + return -EIO; + } + + return 0; +} + +static int cs5535_mfd_res_disable(struct platform_device *pdev) +{ + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + if (!res) { + dev_err(&pdev->dev, "can't fetch device resource info\n"); + return -EIO; + } + + release_region(res->start, resource_size(res)); + return 0; +} + +static __devinitdata struct resource cs5535_mfd_resources[NR_BARS]; + +static __devinitdata struct mfd_cell cs5535_mfd_cells[] = { + { + .id = SMB_BAR, + .name = "cs5535-smb", + .num_resources = 1, + .resources = &cs5535_mfd_resources[SMB_BAR], + }, + { + .id = GPIO_BAR, + .name = "cs5535-gpio", + .num_resources = 1, + .resources = &cs5535_mfd_resources[GPIO_BAR], + }, + { + .id = MFGPT_BAR, + .name = "cs5535-mfgpt", + .num_resources = 1, + .resources = &cs5535_mfd_resources[MFGPT_BAR], + }, + { + .id = PMS_BAR, + .name = "cs5535-pms", + .num_resources = 1, + .resources = &cs5535_mfd_resources[PMS_BAR], + + .enable = cs5535_mfd_res_enable, + .disable = cs5535_mfd_res_disable, + }, + { + .id = ACPI_BAR, + .name = "cs5535-acpi", + .num_resources = 1, + .resources = &cs5535_mfd_resources[ACPI_BAR], + + .enable = cs5535_mfd_res_enable, + .disable = cs5535_mfd_res_disable, + }, +}; + +#ifdef CONFIG_OLPC +static void __devinit cs5535_clone_olpc_cells(void) +{ + const char *acpi_clones[] = { "olpc-xo1-pm-acpi", "olpc-xo1-sci-acpi" }; + + if (!machine_is_olpc()) + return; + + mfd_clone_cell("cs5535-acpi", acpi_clones, ARRAY_SIZE(acpi_clones)); +} +#else +static void cs5535_clone_olpc_cells(void) { } +#endif + +static int __devinit cs5535_mfd_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + int err, i; + + err = pci_enable_device(pdev); + if (err) + return err; + + /* fill in IO range for each cell; subdrivers handle the region */ + for (i = 0; i < ARRAY_SIZE(cs5535_mfd_cells); i++) { + int bar = cs5535_mfd_cells[i].id; + struct resource *r = &cs5535_mfd_resources[bar]; + + r->flags = IORESOURCE_IO; + r->start = pci_resource_start(pdev, bar); + r->end = pci_resource_end(pdev, bar); + + /* id is used for temporarily storing BAR; unset it now */ + cs5535_mfd_cells[i].id = 0; + } + + err = mfd_add_devices(&pdev->dev, -1, cs5535_mfd_cells, + ARRAY_SIZE(cs5535_mfd_cells), NULL, 0); + if (err) { + dev_err(&pdev->dev, "MFD add devices failed: %d\n", err); + goto err_disable; + } + cs5535_clone_olpc_cells(); + + dev_info(&pdev->dev, "%zu devices registered.\n", + ARRAY_SIZE(cs5535_mfd_cells)); + + return 0; + +err_disable: + pci_disable_device(pdev); + return err; +} + +static void __devexit cs5535_mfd_remove(struct pci_dev *pdev) +{ + mfd_remove_devices(&pdev->dev); + pci_disable_device(pdev); +} + +static struct pci_device_id cs5535_mfd_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_CS5535_ISA) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_ISA) }, + { 0, } +}; +MODULE_DEVICE_TABLE(pci, cs5535_mfd_pci_tbl); + +static struct pci_driver cs5535_mfd_drv = { + .name = DRV_NAME, + .id_table = cs5535_mfd_pci_tbl, + .probe = cs5535_mfd_probe, + .remove = __devexit_p(cs5535_mfd_remove), +}; + +static int __init cs5535_mfd_init(void) +{ + return pci_register_driver(&cs5535_mfd_drv); +} + +static void __exit cs5535_mfd_exit(void) +{ + pci_unregister_driver(&cs5535_mfd_drv); +} + +module_init(cs5535_mfd_init); +module_exit(cs5535_mfd_exit); + +MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>"); +MODULE_DESCRIPTION("MFD driver for CS5535/CS5536 southbridge's ISA PCI device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/davinci_voicecodec.c b/drivers/mfd/davinci_voicecodec.c index 33c923d..414783b 100644 --- a/drivers/mfd/davinci_voicecodec.c +++ b/drivers/mfd/davinci_voicecodec.c @@ -118,13 +118,13 @@ static int __init davinci_vc_probe(struct platform_device *pdev) /* Voice codec interface client */ cell = &davinci_vc->cells[DAVINCI_VC_VCIF_CELL]; - cell->name = "davinci_vcif"; - cell->driver_data = davinci_vc; + cell->name = "davinci-vcif"; + cell->mfd_data = davinci_vc; /* Voice codec CQ93VC client */ cell = &davinci_vc->cells[DAVINCI_VC_CQ93VC_CELL]; - cell->name = "cq93vc"; - cell->driver_data = davinci_vc; + cell->name = "cq93vc-codec"; + cell->mfd_data = davinci_vc; ret = mfd_add_devices(&pdev->dev, pdev->id, davinci_vc->cells, DAVINCI_VC_CELLS, NULL, 0); diff --git a/drivers/mfd/ezx-pcap.c b/drivers/mfd/ezx-pcap.c index c2b698d..43a76c4 100644 --- a/drivers/mfd/ezx-pcap.c +++ b/drivers/mfd/ezx-pcap.c @@ -144,26 +144,27 @@ int pcap_to_irq(struct pcap_chip *pcap, int irq) } EXPORT_SYMBOL_GPL(pcap_to_irq); -static void pcap_mask_irq(unsigned int irq) +static void pcap_mask_irq(struct irq_data *d) { - struct pcap_chip *pcap = get_irq_chip_data(irq); + struct pcap_chip *pcap = irq_data_get_irq_chip_data(d); - pcap->msr |= 1 << irq_to_pcap(pcap, irq); + pcap->msr |= 1 << irq_to_pcap(pcap, d->irq); queue_work(pcap->workqueue, &pcap->msr_work); } -static void pcap_unmask_irq(unsigned int irq) +static void pcap_unmask_irq(struct irq_data *d) { - struct pcap_chip *pcap = get_irq_chip_data(irq); + struct pcap_chip *pcap = irq_data_get_irq_chip_data(d); - pcap->msr &= ~(1 << irq_to_pcap(pcap, irq)); + pcap->msr &= ~(1 << irq_to_pcap(pcap, d->irq)); queue_work(pcap->workqueue, &pcap->msr_work); } static struct irq_chip pcap_irq_chip = { - .name = "pcap", - .mask = pcap_mask_irq, - .unmask = pcap_unmask_irq, + .name = "pcap", + .irq_disable = pcap_mask_irq, + .irq_mask = pcap_mask_irq, + .irq_unmask = pcap_unmask_irq, }; static void pcap_msr_work(struct work_struct *work) @@ -184,7 +185,7 @@ static void pcap_isr_work(struct work_struct *work) ezx_pcap_read(pcap, PCAP_REG_MSR, &msr); ezx_pcap_read(pcap, PCAP_REG_ISR, &isr); - /* We cant service/ack irqs that are assigned to port 2 */ + /* We can't service/ack irqs that are assigned to port 2 */ if (!(pdata->config & PCAP_SECOND_PORT)) { ezx_pcap_read(pcap, PCAP_REG_INT_SEL, &int_sel); isr &= ~int_sel; @@ -196,18 +197,8 @@ static void pcap_isr_work(struct work_struct *work) local_irq_disable(); service = isr & ~msr; for (irq = pcap->irq_base; service; service >>= 1, irq++) { - if (service & 1) { - struct irq_desc *desc = irq_to_desc(irq); - - if (WARN(!desc, KERN_WARNING - "Invalid PCAP IRQ %d\n", irq)) - break; - - if (desc->status & IRQ_DISABLED) - note_interrupt(irq, desc, IRQ_NONE); - else - desc->handle_irq(irq, desc); - } + if (service & 1) + generic_handle_irq(irq); } local_irq_enable(); ezx_pcap_write(pcap, PCAP_REG_MSR, pcap->msr); @@ -216,9 +207,9 @@ static void pcap_isr_work(struct work_struct *work) static void pcap_irq_handler(unsigned int irq, struct irq_desc *desc) { - struct pcap_chip *pcap = get_irq_data(irq); + struct pcap_chip *pcap = irq_get_handler_data(irq); - desc->chip->ack(irq); + desc->irq_data.chip->irq_ack(&desc->irq_data); queue_work(pcap->workqueue, &pcap->isr_work); return; } @@ -282,7 +273,7 @@ static irqreturn_t pcap_adc_irq(int irq, void *_pcap) mutex_lock(&pcap->adc_mutex); req = pcap->adc_queue[pcap->adc_head]; - if (WARN(!req, KERN_WARNING "adc irq without pending request\n")) { + if (WARN(!req, "adc irq without pending request\n")) { mutex_unlock(&pcap->adc_mutex); return IRQ_HANDLED; } @@ -420,7 +411,7 @@ static int __devexit ezx_pcap_remove(struct spi_device *spi) /* cleanup irqchip */ for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) - set_irq_chip_and_handler(i, NULL, NULL); + irq_set_chip_and_handler(i, NULL, NULL); destroy_workqueue(pcap->workqueue); @@ -466,7 +457,7 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi) pcap->workqueue = create_singlethread_workqueue("pcapd"); if (!pcap->workqueue) { ret = -ENOMEM; - dev_err(&spi->dev, "cant create pcap thread\n"); + dev_err(&spi->dev, "can't create pcap thread\n"); goto free_pcap; } @@ -477,12 +468,12 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi) /* setup irq chip */ for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) { - set_irq_chip_and_handler(i, &pcap_irq_chip, handle_simple_irq); - set_irq_chip_data(i, pcap); + irq_set_chip_and_handler(i, &pcap_irq_chip, handle_simple_irq); + irq_set_chip_data(i, pcap); #ifdef CONFIG_ARM set_irq_flags(i, IRQF_VALID); #else - set_irq_noprobe(i); + irq_set_noprobe(i); #endif } @@ -491,10 +482,10 @@ static int __devinit ezx_pcap_probe(struct spi_device *spi) ezx_pcap_write(pcap, PCAP_REG_ISR, PCAP_CLEAR_INTERRUPT_REGISTER); pcap->msr = PCAP_MASK_ALL_INTERRUPT; - set_irq_type(spi->irq, IRQ_TYPE_EDGE_RISING); - set_irq_data(spi->irq, pcap); - set_irq_chained_handler(spi->irq, pcap_irq_handler); - set_irq_wake(spi->irq, 1); + irq_set_irq_type(spi->irq, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(spi->irq, pcap); + irq_set_chained_handler(spi->irq, pcap_irq_handler); + irq_set_irq_wake(spi->irq, 1); /* ADC */ adc_irq = pcap_to_irq(pcap, (pdata->config & PCAP_SECOND_PORT) ? @@ -523,7 +514,7 @@ remove_subdevs: free_irq(adc_irq, pcap); free_irqchip: for (i = pcap->irq_base; i < (pcap->irq_base + PCAP_NIRQS); i++) - set_irq_chip_and_handler(i, NULL, NULL); + irq_set_chip_and_handler(i, NULL, NULL); /* destroy_workqueue: */ destroy_workqueue(pcap->workqueue); free_pcap: diff --git a/drivers/mfd/htc-egpio.c b/drivers/mfd/htc-egpio.c index d3e74f8..bbaec0c 100644 --- a/drivers/mfd/htc-egpio.c +++ b/drivers/mfd/htc-egpio.c @@ -70,36 +70,37 @@ static inline void ack_irqs(struct egpio_info *ei) ei->ack_write, ei->ack_register << ei->bus_shift); } -static void egpio_ack(unsigned int irq) +static void egpio_ack(struct irq_data *data) { } /* There does not appear to be a way to proactively mask interrupts * on the egpio chip itself. So, we simply ignore interrupts that * aren't desired. */ -static void egpio_mask(unsigned int irq) +static void egpio_mask(struct irq_data *data) { - struct egpio_info *ei = get_irq_chip_data(irq); - ei->irqs_enabled &= ~(1 << (irq - ei->irq_start)); - pr_debug("EGPIO mask %d %04x\n", irq, ei->irqs_enabled); + struct egpio_info *ei = irq_data_get_irq_chip_data(data); + ei->irqs_enabled &= ~(1 << (data->irq - ei->irq_start)); + pr_debug("EGPIO mask %d %04x\n", data->irq, ei->irqs_enabled); } -static void egpio_unmask(unsigned int irq) + +static void egpio_unmask(struct irq_data *data) { - struct egpio_info *ei = get_irq_chip_data(irq); - ei->irqs_enabled |= 1 << (irq - ei->irq_start); - pr_debug("EGPIO unmask %d %04x\n", irq, ei->irqs_enabled); + struct egpio_info *ei = irq_data_get_irq_chip_data(data); + ei->irqs_enabled |= 1 << (data->irq - ei->irq_start); + pr_debug("EGPIO unmask %d %04x\n", data->irq, ei->irqs_enabled); } static struct irq_chip egpio_muxed_chip = { - .name = "htc-egpio", - .ack = egpio_ack, - .mask = egpio_mask, - .unmask = egpio_unmask, + .name = "htc-egpio", + .irq_ack = egpio_ack, + .irq_mask = egpio_mask, + .irq_unmask = egpio_unmask, }; static void egpio_handler(unsigned int irq, struct irq_desc *desc) { - struct egpio_info *ei = get_irq_data(irq); + struct egpio_info *ei = irq_desc_get_handler_data(desc); int irqpin; /* Read current pins. */ @@ -112,9 +113,7 @@ static void egpio_handler(unsigned int irq, struct irq_desc *desc) for_each_set_bit(irqpin, &readval, ei->nirqs) { /* Run irq handler */ pr_debug("got IRQ %d\n", irqpin); - irq = ei->irq_start + irqpin; - desc = irq_to_desc(irq); - desc->handle_irq(irq, desc); + generic_handle_irq(ei->irq_start + irqpin); } } @@ -345,14 +344,14 @@ static int __init egpio_probe(struct platform_device *pdev) ei->ack_write = 0; irq_end = ei->irq_start + ei->nirqs; for (irq = ei->irq_start; irq < irq_end; irq++) { - set_irq_chip(irq, &egpio_muxed_chip); - set_irq_chip_data(irq, ei); - set_irq_handler(irq, handle_simple_irq); + irq_set_chip_and_handler(irq, &egpio_muxed_chip, + handle_simple_irq); + irq_set_chip_data(irq, ei); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING); - set_irq_data(ei->chained_irq, ei); - set_irq_chained_handler(ei->chained_irq, egpio_handler); + irq_set_irq_type(ei->chained_irq, IRQ_TYPE_EDGE_RISING); + irq_set_handler_data(ei->chained_irq, ei); + irq_set_chained_handler(ei->chained_irq, egpio_handler); ack_irqs(ei); device_init_wakeup(&pdev->dev, 1); @@ -374,11 +373,10 @@ static int __exit egpio_remove(struct platform_device *pdev) if (ei->chained_irq) { irq_end = ei->irq_start + ei->nirqs; for (irq = ei->irq_start; irq < irq_end; irq++) { - set_irq_chip(irq, NULL); - set_irq_handler(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); set_irq_flags(irq, 0); } - set_irq_chained_handler(ei->chained_irq, NULL); + irq_set_chained_handler(ei->chained_irq, NULL); device_init_wakeup(&pdev->dev, 0); } iounmap(ei->base_addr); diff --git a/drivers/mfd/htc-i2cpld.c b/drivers/mfd/htc-i2cpld.c index 594c9a8..d55065c 100644 --- a/drivers/mfd/htc-i2cpld.c +++ b/drivers/mfd/htc-i2cpld.c @@ -58,6 +58,7 @@ struct htcpld_chip { uint irq_start; int nirqs; + unsigned int flow_type; /* * Work structure to allow for setting values outside of any * possible interrupt context @@ -82,27 +83,22 @@ struct htcpld_data { /* There does not appear to be a way to proactively mask interrupts * on the htcpld chip itself. So, we simply ignore interrupts that * aren't desired. */ -static void htcpld_mask(unsigned int irq) +static void htcpld_mask(struct irq_data *data) { - struct htcpld_chip *chip = get_irq_chip_data(irq); - chip->irqs_enabled &= ~(1 << (irq - chip->irq_start)); - pr_debug("HTCPLD mask %d %04x\n", irq, chip->irqs_enabled); + struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); + chip->irqs_enabled &= ~(1 << (data->irq - chip->irq_start)); + pr_debug("HTCPLD mask %d %04x\n", data->irq, chip->irqs_enabled); } -static void htcpld_unmask(unsigned int irq) +static void htcpld_unmask(struct irq_data *data) { - struct htcpld_chip *chip = get_irq_chip_data(irq); - chip->irqs_enabled |= 1 << (irq - chip->irq_start); - pr_debug("HTCPLD unmask %d %04x\n", irq, chip->irqs_enabled); + struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); + chip->irqs_enabled |= 1 << (data->irq - chip->irq_start); + pr_debug("HTCPLD unmask %d %04x\n", data->irq, chip->irqs_enabled); } -static int htcpld_set_type(unsigned int irq, unsigned int flags) +static int htcpld_set_type(struct irq_data *data, unsigned int flags) { - struct irq_desc *d = irq_to_desc(irq); - - if (!d) { - pr_err("HTCPLD invalid IRQ: %d\n", irq); - return -EINVAL; - } + struct htcpld_chip *chip = irq_data_get_irq_chip_data(data); if (flags & ~IRQ_TYPE_SENSE_MASK) return -EINVAL; @@ -111,17 +107,15 @@ static int htcpld_set_type(unsigned int irq, unsigned int flags) if (flags & (IRQ_TYPE_LEVEL_LOW|IRQ_TYPE_LEVEL_HIGH)) return -EINVAL; - d->status &= ~IRQ_TYPE_SENSE_MASK; - d->status |= flags; - + chip->flow_type = flags; return 0; } static struct irq_chip htcpld_muxed_chip = { - .name = "htcpld", - .mask = htcpld_mask, - .unmask = htcpld_unmask, - .set_type = htcpld_set_type, + .name = "htcpld", + .irq_mask = htcpld_mask, + .irq_unmask = htcpld_unmask, + .irq_set_type = htcpld_set_type, }; /* To properly dispatch IRQ events, we need to read from the @@ -135,7 +129,6 @@ static irqreturn_t htcpld_handler(int irq, void *dev) unsigned int i; unsigned long flags; int irqpin; - struct irq_desc *desc; if (!htcpld) { pr_debug("htcpld is null in ISR\n"); @@ -195,23 +188,19 @@ static irqreturn_t htcpld_handler(int irq, void *dev) * associated interrupts. */ for (irqpin = 0; irqpin < chip->nirqs; irqpin++) { - unsigned oldb, newb; - int flags; + unsigned oldb, newb, type = chip->flow_type; irq = chip->irq_start + irqpin; - desc = irq_to_desc(irq); - flags = desc->status; /* Run the IRQ handler, but only if the bit value * changed, and the proper flags are set */ oldb = (old_val >> irqpin) & 1; newb = (uval >> irqpin) & 1; - if ((!oldb && newb && (flags & IRQ_TYPE_EDGE_RISING)) || - (oldb && !newb && - (flags & IRQ_TYPE_EDGE_FALLING))) { + if ((!oldb && newb && (type & IRQ_TYPE_EDGE_RISING)) || + (oldb && !newb && (type & IRQ_TYPE_EDGE_FALLING))) { pr_debug("fire IRQ %d\n", irqpin); - desc->handle_irq(irq, desc); + generic_handle_irq(irq); } } } @@ -235,7 +224,7 @@ static irqreturn_t htcpld_handler(int irq, void *dev) * and that work is scheduled in the set routine. The kernel can then run * the I2C functions, which will sleep, in process context. */ -void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val) +static void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val) { struct i2c_client *client; struct htcpld_chip *chip_data; @@ -259,7 +248,7 @@ void htcpld_chip_set(struct gpio_chip *chip, unsigned offset, int val) schedule_work(&(chip_data->set_val_work)); } -void htcpld_chip_set_ni(struct work_struct *work) +static void htcpld_chip_set_ni(struct work_struct *work) { struct htcpld_chip *chip_data; struct i2c_client *client; @@ -269,7 +258,7 @@ void htcpld_chip_set_ni(struct work_struct *work) i2c_smbus_read_byte_data(client, chip_data->cache_out); } -int htcpld_chip_get(struct gpio_chip *chip, unsigned offset) +static int htcpld_chip_get(struct gpio_chip *chip, unsigned offset) { struct htcpld_chip *chip_data; int val = 0; @@ -316,7 +305,7 @@ static int htcpld_direction_input(struct gpio_chip *chip, return (offset < chip->ngpio) ? 0 : -EINVAL; } -int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset) +static int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset) { struct htcpld_chip *chip_data; @@ -328,7 +317,7 @@ int htcpld_chip_to_irq(struct gpio_chip *chip, unsigned offset) return -EINVAL; } -void htcpld_chip_reset(struct i2c_client *client) +static void htcpld_chip_reset(struct i2c_client *client) { struct htcpld_chip *chip_data = i2c_get_clientdata(client); if (!chip_data) @@ -359,13 +348,13 @@ static int __devinit htcpld_setup_chip_irq( /* Setup irq handlers */ irq_end = chip->irq_start + chip->nirqs; for (irq = chip->irq_start; irq < irq_end; irq++) { - set_irq_chip(irq, &htcpld_muxed_chip); - set_irq_chip_data(irq, chip); - set_irq_handler(irq, handle_simple_irq); + irq_set_chip_and_handler(irq, &htcpld_muxed_chip, + handle_simple_irq); + irq_set_chip_data(irq, chip); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); #else - set_irq_probe(irq); + irq_set_probe(irq); #endif } diff --git a/drivers/mfd/htc-pasic3.c b/drivers/mfd/htc-pasic3.c index 7bc7522..fb9770b 100644 --- a/drivers/mfd/htc-pasic3.c +++ b/drivers/mfd/htc-pasic3.c @@ -117,7 +117,7 @@ static struct mfd_cell ds1wm_cell __initdata = { .name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, - .driver_data = &ds1wm_pdata, + .mfd_data = &ds1wm_pdata, .num_resources = 2, .resources = ds1wm_resources, }; @@ -165,8 +165,6 @@ static int __init pasic3_probe(struct platform_device *pdev) ds1wm_pdata.clock_rate = pdata->clock_rate; /* the first 5 PASIC3 registers control the DS1WM */ ds1wm_resources[0].end = (5 << asic->bus_shift) - 1; - ds1wm_cell.platform_data = &ds1wm_cell; - ds1wm_cell.data_size = sizeof(ds1wm_cell); ret = mfd_add_devices(&pdev->dev, pdev->id, &ds1wm_cell, 1, r, irq); if (ret < 0) @@ -174,9 +172,6 @@ static int __init pasic3_probe(struct platform_device *pdev) } if (pdata && pdata->led_pdata) { - led_cell.driver_data = pdata->led_pdata; - led_cell.platform_data = &led_cell; - led_cell.data_size = sizeof(ds1wm_cell); ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0); if (ret < 0) dev_warn(dev, "failed to register LED device\n"); diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 36a166b..fc41911 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -86,8 +86,7 @@ static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv, /* Add platform data */ pdata->modno = modno; - cell->platform_data = pdata; - cell->data_size = sizeof(*pdata); + cell->mfd_data = pdata; /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ res->flags = IORESOURCE_MEM; diff --git a/drivers/mfd/jz4740-adc.c b/drivers/mfd/jz4740-adc.c index 9dd1b33..a0bd0cf 100644 --- a/drivers/mfd/jz4740-adc.c +++ b/drivers/mfd/jz4740-adc.c @@ -84,36 +84,35 @@ static inline void jz4740_adc_irq_set_masked(struct jz4740_adc *adc, int irq, spin_unlock_irqrestore(&adc->lock, flags); } -static void jz4740_adc_irq_mask(unsigned int irq) +static void jz4740_adc_irq_mask(struct irq_data *data) { - struct jz4740_adc *adc = get_irq_chip_data(irq); - jz4740_adc_irq_set_masked(adc, irq, true); + struct jz4740_adc *adc = irq_data_get_irq_chip_data(data); + jz4740_adc_irq_set_masked(adc, data->irq, true); } -static void jz4740_adc_irq_unmask(unsigned int irq) +static void jz4740_adc_irq_unmask(struct irq_data *data) { - struct jz4740_adc *adc = get_irq_chip_data(irq); - jz4740_adc_irq_set_masked(adc, irq, false); + struct jz4740_adc *adc = irq_data_get_irq_chip_data(data); + jz4740_adc_irq_set_masked(adc, data->irq, false); } -static void jz4740_adc_irq_ack(unsigned int irq) +static void jz4740_adc_irq_ack(struct irq_data *data) { - struct jz4740_adc *adc = get_irq_chip_data(irq); - - irq -= adc->irq_base; + struct jz4740_adc *adc = irq_data_get_irq_chip_data(data); + unsigned int irq = data->irq - adc->irq_base; writeb(BIT(irq), adc->base + JZ_REG_ADC_STATUS); } static struct irq_chip jz4740_adc_irq_chip = { .name = "jz4740-adc", - .mask = jz4740_adc_irq_mask, - .unmask = jz4740_adc_irq_unmask, - .ack = jz4740_adc_irq_ack, + .irq_mask = jz4740_adc_irq_mask, + .irq_unmask = jz4740_adc_irq_unmask, + .irq_ack = jz4740_adc_irq_ack, }; static void jz4740_adc_irq_demux(unsigned int irq, struct irq_desc *desc) { - struct jz4740_adc *adc = get_irq_desc_data(desc); + struct jz4740_adc *adc = irq_desc_get_handler_data(desc); uint8_t status; unsigned int i; @@ -233,8 +232,6 @@ const struct mfd_cell jz4740_adc_cells[] = { .name = "jz4740-hwmon", .num_resources = ARRAY_SIZE(jz4740_hwmon_resources), .resources = jz4740_hwmon_resources, - .platform_data = (void *)&jz4740_adc_cells[0], - .data_size = sizeof(struct mfd_cell), .enable = jz4740_adc_cell_enable, .disable = jz4740_adc_cell_disable, @@ -244,8 +241,6 @@ const struct mfd_cell jz4740_adc_cells[] = { .name = "jz4740-battery", .num_resources = ARRAY_SIZE(jz4740_battery_resources), .resources = jz4740_battery_resources, - .platform_data = (void *)&jz4740_adc_cells[1], - .data_size = sizeof(struct mfd_cell), .enable = jz4740_adc_cell_enable, .disable = jz4740_adc_cell_disable, @@ -315,13 +310,13 @@ static int __devinit jz4740_adc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, adc); for (irq = adc->irq_base; irq < adc->irq_base + 5; ++irq) { - set_irq_chip_data(irq, adc); - set_irq_chip_and_handler(irq, &jz4740_adc_irq_chip, - handle_level_irq); + irq_set_chip_data(irq, adc); + irq_set_chip_and_handler(irq, &jz4740_adc_irq_chip, + handle_level_irq); } - set_irq_data(adc->irq, adc); - set_irq_chained_handler(adc->irq, jz4740_adc_irq_demux); + irq_set_handler_data(adc->irq, adc); + irq_set_chained_handler(adc->irq, jz4740_adc_irq_demux); writeb(0x00, adc->base + JZ_REG_ADC_ENABLE); writeb(0xff, adc->base + JZ_REG_ADC_CTRL); @@ -352,8 +347,8 @@ static int __devexit jz4740_adc_remove(struct platform_device *pdev) mfd_remove_devices(&pdev->dev); - set_irq_data(adc->irq, NULL); - set_irq_chained_handler(adc->irq, NULL); + irq_set_handler_data(adc->irq, NULL); + irq_set_chained_handler(adc->irq, NULL); iounmap(adc->base); release_mem_region(adc->mem->start, resource_size(adc->mem)); diff --git a/drivers/mfd/lpc_sch.c b/drivers/mfd/lpc_sch.c index 51b2f60..ea3f52c 100644 --- a/drivers/mfd/lpc_sch.c +++ b/drivers/mfd/lpc_sch.c @@ -61,6 +61,7 @@ static struct mfd_cell lpc_sch_cells[] = { static struct pci_device_id lpc_sch_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) }, { 0, } }; MODULE_DEVICE_TABLE(pci, lpc_sch_ids); @@ -70,6 +71,7 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, { unsigned int base_addr_cfg; unsigned short base_addr; + int i; pci_read_config_dword(dev, SMBASE, &base_addr_cfg); if (!(base_addr_cfg & (1 << 31))) { @@ -99,7 +101,10 @@ static int __devinit lpc_sch_probe(struct pci_dev *dev, gpio_sch_resource.start = base_addr; gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1; - return mfd_add_devices(&dev->dev, -1, + for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++) + lpc_sch_cells[i].id = id->device; + + return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0); } diff --git a/drivers/mfd/max8925-core.c b/drivers/mfd/max8925-core.c index 44695f5..58cc5fd 100644 --- a/drivers/mfd/max8925-core.c +++ b/drivers/mfd/max8925-core.c @@ -407,16 +407,16 @@ static irqreturn_t max8925_tsc_irq(int irq, void *data) return IRQ_HANDLED; } -static void max8925_irq_lock(unsigned int irq) +static void max8925_irq_lock(struct irq_data *data) { - struct max8925_chip *chip = get_irq_chip_data(irq); + struct max8925_chip *chip = irq_data_get_irq_chip_data(data); mutex_lock(&chip->irq_lock); } -static void max8925_irq_sync_unlock(unsigned int irq) +static void max8925_irq_sync_unlock(struct irq_data *data) { - struct max8925_chip *chip = get_irq_chip_data(irq); + struct max8925_chip *chip = irq_data_get_irq_chip_data(data); struct max8925_irq_data *irq_data; static unsigned char cache_chg[2] = {0xff, 0xff}; static unsigned char cache_on[2] = {0xff, 0xff}; @@ -492,32 +492,31 @@ static void max8925_irq_sync_unlock(unsigned int irq) mutex_unlock(&chip->irq_lock); } -static void max8925_irq_enable(unsigned int irq) +static void max8925_irq_enable(struct irq_data *data) { - struct max8925_chip *chip = get_irq_chip_data(irq); - max8925_irqs[irq - chip->irq_base].enable - = max8925_irqs[irq - chip->irq_base].offs; + struct max8925_chip *chip = irq_data_get_irq_chip_data(data); + max8925_irqs[data->irq - chip->irq_base].enable + = max8925_irqs[data->irq - chip->irq_base].offs; } -static void max8925_irq_disable(unsigned int irq) +static void max8925_irq_disable(struct irq_data *data) { - struct max8925_chip *chip = get_irq_chip_data(irq); - max8925_irqs[irq - chip->irq_base].enable = 0; + struct max8925_chip *chip = irq_data_get_irq_chip_data(data); + max8925_irqs[data->irq - chip->irq_base].enable = 0; } static struct irq_chip max8925_irq_chip = { .name = "max8925", - .bus_lock = max8925_irq_lock, - .bus_sync_unlock = max8925_irq_sync_unlock, - .enable = max8925_irq_enable, - .disable = max8925_irq_disable, + .irq_bus_lock = max8925_irq_lock, + .irq_bus_sync_unlock = max8925_irq_sync_unlock, + .irq_enable = max8925_irq_enable, + .irq_disable = max8925_irq_disable, }; static int max8925_irq_init(struct max8925_chip *chip, int irq, struct max8925_platform_data *pdata) { unsigned long flags = IRQF_TRIGGER_FALLING | IRQF_ONESHOT; - struct irq_desc *desc; int i, ret; int __irq; @@ -544,19 +543,18 @@ static int max8925_irq_init(struct max8925_chip *chip, int irq, mutex_init(&chip->irq_lock); chip->core_irq = irq; chip->irq_base = pdata->irq_base; - desc = irq_to_desc(chip->core_irq); /* register with genirq */ for (i = 0; i < ARRAY_SIZE(max8925_irqs); i++) { __irq = i + chip->irq_base; - set_irq_chip_data(__irq, chip); - set_irq_chip_and_handler(__irq, &max8925_irq_chip, + irq_set_chip_data(__irq, chip); + irq_set_chip_and_handler(__irq, &max8925_irq_chip, handle_edge_irq); - set_irq_nested_thread(__irq, 1); + irq_set_nested_thread(__irq, 1); #ifdef CONFIG_ARM set_irq_flags(__irq, IRQF_VALID); #else - set_irq_noprobe(__irq); + irq_set_noprobe(__irq); #endif } if (!irq) { diff --git a/drivers/mfd/max8997-irq.c b/drivers/mfd/max8997-irq.c new file mode 100644 index 0000000..638bf7e --- /dev/null +++ b/drivers/mfd/max8997-irq.c @@ -0,0 +1,377 @@ +/* + * max8997-irq.c - Interrupt controller support for MAX8997 + * + * Copyright (C) 2011 Samsung Electronics Co.Ltd + * MyungJoo Ham <myungjoo.ham@samsung.com> + * + * 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 + * + * This driver is based on max8998-irq.c + */ + +#include <linux/err.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/mfd/max8997.h> +#include <linux/mfd/max8997-private.h> + +static const u8 max8997_mask_reg[] = { + [PMIC_INT1] = MAX8997_REG_INT1MSK, + [PMIC_INT2] = MAX8997_REG_INT2MSK, + [PMIC_INT3] = MAX8997_REG_INT3MSK, + [PMIC_INT4] = MAX8997_REG_INT4MSK, + [FUEL_GAUGE] = MAX8997_REG_INVALID, + [MUIC_INT1] = MAX8997_MUIC_REG_INTMASK1, + [MUIC_INT2] = MAX8997_MUIC_REG_INTMASK2, + [MUIC_INT3] = MAX8997_MUIC_REG_INTMASK3, + [GPIO_LOW] = MAX8997_REG_INVALID, + [GPIO_HI] = MAX8997_REG_INVALID, + [FLASH_STATUS] = MAX8997_REG_INVALID, +}; + +static struct i2c_client *get_i2c(struct max8997_dev *max8997, + enum max8997_irq_source src) +{ + switch (src) { + case PMIC_INT1 ... PMIC_INT4: + return max8997->i2c; + case FUEL_GAUGE: + return NULL; + case MUIC_INT1 ... MUIC_INT3: + return max8997->muic; + case GPIO_LOW ... GPIO_HI: + return max8997->i2c; + case FLASH_STATUS: + return max8997->i2c; + default: + return ERR_PTR(-EINVAL); + } + + return ERR_PTR(-EINVAL); +} + +struct max8997_irq_data { + int mask; + enum max8997_irq_source group; +}; + +#define DECLARE_IRQ(idx, _group, _mask) \ + [(idx)] = { .group = (_group), .mask = (_mask) } +static const struct max8997_irq_data max8997_irqs[] = { + DECLARE_IRQ(MAX8997_PMICIRQ_PWRONR, PMIC_INT1, 1 << 0), + DECLARE_IRQ(MAX8997_PMICIRQ_PWRONF, PMIC_INT1, 1 << 1), + DECLARE_IRQ(MAX8997_PMICIRQ_PWRON1SEC, PMIC_INT1, 1 << 3), + DECLARE_IRQ(MAX8997_PMICIRQ_JIGONR, PMIC_INT1, 1 << 4), + DECLARE_IRQ(MAX8997_PMICIRQ_JIGONF, PMIC_INT1, 1 << 5), + DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT2, PMIC_INT1, 1 << 6), + DECLARE_IRQ(MAX8997_PMICIRQ_LOWBAT1, PMIC_INT1, 1 << 7), + + DECLARE_IRQ(MAX8997_PMICIRQ_JIGR, PMIC_INT2, 1 << 0), + DECLARE_IRQ(MAX8997_PMICIRQ_JIGF, PMIC_INT2, 1 << 1), + DECLARE_IRQ(MAX8997_PMICIRQ_MR, PMIC_INT2, 1 << 2), + DECLARE_IRQ(MAX8997_PMICIRQ_DVS1OK, PMIC_INT2, 1 << 3), + DECLARE_IRQ(MAX8997_PMICIRQ_DVS2OK, PMIC_INT2, 1 << 4), + DECLARE_IRQ(MAX8997_PMICIRQ_DVS3OK, PMIC_INT2, 1 << 5), + DECLARE_IRQ(MAX8997_PMICIRQ_DVS4OK, PMIC_INT2, 1 << 6), + + DECLARE_IRQ(MAX8997_PMICIRQ_CHGINS, PMIC_INT3, 1 << 0), + DECLARE_IRQ(MAX8997_PMICIRQ_CHGRM, PMIC_INT3, 1 << 1), + DECLARE_IRQ(MAX8997_PMICIRQ_DCINOVP, PMIC_INT3, 1 << 2), + DECLARE_IRQ(MAX8997_PMICIRQ_TOPOFFR, PMIC_INT3, 1 << 3), + DECLARE_IRQ(MAX8997_PMICIRQ_CHGRSTF, PMIC_INT3, 1 << 5), + DECLARE_IRQ(MAX8997_PMICIRQ_MBCHGTMEXPD, PMIC_INT3, 1 << 7), + + DECLARE_IRQ(MAX8997_PMICIRQ_RTC60S, PMIC_INT4, 1 << 0), + DECLARE_IRQ(MAX8997_PMICIRQ_RTCA1, PMIC_INT4, 1 << 1), + DECLARE_IRQ(MAX8997_PMICIRQ_RTCA2, PMIC_INT4, 1 << 2), + DECLARE_IRQ(MAX8997_PMICIRQ_SMPL_INT, PMIC_INT4, 1 << 3), + DECLARE_IRQ(MAX8997_PMICIRQ_RTC1S, PMIC_INT4, 1 << 4), + DECLARE_IRQ(MAX8997_PMICIRQ_WTSR, PMIC_INT4, 1 << 5), + + DECLARE_IRQ(MAX8997_MUICIRQ_ADCError, MUIC_INT1, 1 << 2), + DECLARE_IRQ(MAX8997_MUICIRQ_ADCLow, MUIC_INT1, 1 << 1), + DECLARE_IRQ(MAX8997_MUICIRQ_ADC, MUIC_INT1, 1 << 0), + + DECLARE_IRQ(MAX8997_MUICIRQ_VBVolt, MUIC_INT2, 1 << 4), + DECLARE_IRQ(MAX8997_MUICIRQ_DBChg, MUIC_INT2, 1 << 3), + DECLARE_IRQ(MAX8997_MUICIRQ_DCDTmr, MUIC_INT2, 1 << 2), + DECLARE_IRQ(MAX8997_MUICIRQ_ChgDetRun, MUIC_INT2, 1 << 1), + DECLARE_IRQ(MAX8997_MUICIRQ_ChgTyp, MUIC_INT2, 1 << 0), + + DECLARE_IRQ(MAX8997_MUICIRQ_OVP, MUIC_INT3, 1 << 2), +}; + +static void max8997_irq_lock(struct irq_data *data) +{ + struct max8997_dev *max8997 = irq_get_chip_data(data->irq); + + mutex_lock(&max8997->irqlock); +} + +static void max8997_irq_sync_unlock(struct irq_data *data) +{ + struct max8997_dev *max8997 = irq_get_chip_data(data->irq); + int i; + + for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) { + u8 mask_reg = max8997_mask_reg[i]; + struct i2c_client *i2c = get_i2c(max8997, i); + + if (mask_reg == MAX8997_REG_INVALID || + IS_ERR_OR_NULL(i2c)) + continue; + max8997->irq_masks_cache[i] = max8997->irq_masks_cur[i]; + + max8997_write_reg(i2c, max8997_mask_reg[i], + max8997->irq_masks_cur[i]); + } + + mutex_unlock(&max8997->irqlock); +} + +static const inline struct max8997_irq_data * +irq_to_max8997_irq(struct max8997_dev *max8997, int irq) +{ + return &max8997_irqs[irq - max8997->irq_base]; +} + +static void max8997_irq_mask(struct irq_data *data) +{ + struct max8997_dev *max8997 = irq_get_chip_data(data->irq); + const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997, + data->irq); + + max8997->irq_masks_cur[irq_data->group] |= irq_data->mask; +} + +static void max8997_irq_unmask(struct irq_data *data) +{ + struct max8997_dev *max8997 = irq_get_chip_data(data->irq); + const struct max8997_irq_data *irq_data = irq_to_max8997_irq(max8997, + data->irq); + + max8997->irq_masks_cur[irq_data->group] &= ~irq_data->mask; +} + +static struct irq_chip max8997_irq_chip = { + .name = "max8997", + .irq_bus_lock = max8997_irq_lock, + .irq_bus_sync_unlock = max8997_irq_sync_unlock, + .irq_mask = max8997_irq_mask, + .irq_unmask = max8997_irq_unmask, +}; + +#define MAX8997_IRQSRC_PMIC (1 << 1) +#define MAX8997_IRQSRC_FUELGAUGE (1 << 2) +#define MAX8997_IRQSRC_MUIC (1 << 3) +#define MAX8997_IRQSRC_GPIO (1 << 4) +#define MAX8997_IRQSRC_FLASH (1 << 5) +static irqreturn_t max8997_irq_thread(int irq, void *data) +{ + struct max8997_dev *max8997 = data; + u8 irq_reg[MAX8997_IRQ_GROUP_NR] = {}; + u8 irq_src; + int ret; + int i; + + ret = max8997_read_reg(max8997->i2c, MAX8997_REG_INTSRC, &irq_src); + if (ret < 0) { + dev_err(max8997->dev, "Failed to read interrupt source: %d\n", + ret); + return IRQ_NONE; + } + + if (irq_src & MAX8997_IRQSRC_PMIC) { + /* PMIC INT1 ~ INT4 */ + max8997_bulk_read(max8997->i2c, MAX8997_REG_INT1, 4, + &irq_reg[PMIC_INT1]); + } + if (irq_src & MAX8997_IRQSRC_FUELGAUGE) { + /* + * TODO: FUEL GAUGE + * + * This is to be supported by Max17042 driver. When + * an interrupt incurs here, it should be relayed to a + * Max17042 device that is connected (probably by + * platform-data). However, we do not have interrupt + * handling in Max17042 driver currently. The Max17042 IRQ + * driver should be ready to be used as a stand-alone device and + * a Max8997-dependent device. Because it is not ready in + * Max17042-side and it is not too critical in operating + * Max8997, we do not implement this in initial releases. + */ + irq_reg[FUEL_GAUGE] = 0; + } + if (irq_src & MAX8997_IRQSRC_MUIC) { + /* MUIC INT1 ~ INT3 */ + max8997_bulk_read(max8997->muic, MAX8997_MUIC_REG_INT1, 3, + &irq_reg[MUIC_INT1]); + } + if (irq_src & MAX8997_IRQSRC_GPIO) { + /* GPIO Interrupt */ + u8 gpio_info[MAX8997_NUM_GPIO]; + + irq_reg[GPIO_LOW] = 0; + irq_reg[GPIO_HI] = 0; + + max8997_bulk_read(max8997->i2c, MAX8997_REG_GPIOCNTL1, + MAX8997_NUM_GPIO, gpio_info); + for (i = 0; i < MAX8997_NUM_GPIO; i++) { + bool interrupt = false; + + switch (gpio_info[i] & MAX8997_GPIO_INT_MASK) { + case MAX8997_GPIO_INT_BOTH: + if (max8997->gpio_status[i] != gpio_info[i]) + interrupt = true; + break; + case MAX8997_GPIO_INT_RISE: + if ((max8997->gpio_status[i] != gpio_info[i]) && + (gpio_info[i] & MAX8997_GPIO_DATA_MASK)) + interrupt = true; + break; + case MAX8997_GPIO_INT_FALL: + if ((max8997->gpio_status[i] != gpio_info[i]) && + !(gpio_info[i] & MAX8997_GPIO_DATA_MASK)) + interrupt = true; + break; + default: + break; + } + + if (interrupt) { + if (i < 8) + irq_reg[GPIO_LOW] |= (1 << i); + else + irq_reg[GPIO_HI] |= (1 << (i - 8)); + } + + } + } + if (irq_src & MAX8997_IRQSRC_FLASH) { + /* Flash Status Interrupt */ + ret = max8997_read_reg(max8997->i2c, MAX8997_REG_FLASHSTATUS, + &irq_reg[FLASH_STATUS]); + } + + /* Apply masking */ + for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) + irq_reg[i] &= ~max8997->irq_masks_cur[i]; + + /* Report */ + for (i = 0; i < MAX8997_IRQ_NR; i++) { + if (irq_reg[max8997_irqs[i].group] & max8997_irqs[i].mask) + handle_nested_irq(max8997->irq_base + i); + } + + return IRQ_HANDLED; +} + +int max8997_irq_resume(struct max8997_dev *max8997) +{ + if (max8997->irq && max8997->irq_base) + max8997_irq_thread(max8997->irq_base, max8997); + return 0; +} + +int max8997_irq_init(struct max8997_dev *max8997) +{ + int i; + int cur_irq; + int ret; + u8 val; + + if (!max8997->irq) { + dev_warn(max8997->dev, "No interrupt specified.\n"); + max8997->irq_base = 0; + return 0; + } + + if (!max8997->irq_base) { + dev_err(max8997->dev, "No interrupt base specified.\n"); + return 0; + } + + mutex_init(&max8997->irqlock); + + /* Mask individual interrupt sources */ + for (i = 0; i < MAX8997_IRQ_GROUP_NR; i++) { + struct i2c_client *i2c; + + max8997->irq_masks_cur[i] = 0xff; + max8997->irq_masks_cache[i] = 0xff; + i2c = get_i2c(max8997, i); + + if (IS_ERR_OR_NULL(i2c)) + continue; + if (max8997_mask_reg[i] == MAX8997_REG_INVALID) + continue; + + max8997_write_reg(i2c, max8997_mask_reg[i], 0xff); + } + + for (i = 0; i < MAX8997_NUM_GPIO; i++) { + max8997->gpio_status[i] = (max8997_read_reg(max8997->i2c, + MAX8997_REG_GPIOCNTL1 + i, + &val) + & MAX8997_GPIO_DATA_MASK) ? + true : false; + } + + /* Register with genirq */ + for (i = 0; i < MAX8997_IRQ_NR; i++) { + cur_irq = i + max8997->irq_base; + irq_set_chip_data(cur_irq, max8997); + irq_set_chip_and_handler(cur_irq, &max8997_irq_chip, + handle_edge_irq); + irq_set_nested_thread(cur_irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(cur_irq, IRQF_VALID); +#else + irq_set_noprobe(cur_irq); +#endif + } + + ret = request_threaded_irq(max8997->irq, NULL, max8997_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "max8997-irq", max8997); + + if (ret) { + dev_err(max8997->dev, "Failed to request IRQ %d: %d\n", + max8997->irq, ret); + return ret; + } + + if (!max8997->ono) + return 0; + + ret = request_threaded_irq(max8997->ono, NULL, max8997_irq_thread, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | + IRQF_ONESHOT, "max8997-ono", max8997); + + if (ret) + dev_err(max8997->dev, "Failed to request ono-IRQ %d: %d\n", + max8997->ono, ret); + + return 0; +} + +void max8997_irq_exit(struct max8997_dev *max8997) +{ + if (max8997->ono) + free_irq(max8997->ono, max8997); + + if (max8997->irq) + free_irq(max8997->irq, max8997); +} diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c new file mode 100644 index 0000000..5d1fca0 --- /dev/null +++ b/drivers/mfd/max8997.c @@ -0,0 +1,427 @@ +/* + * max8997.c - mfd core driver for the Maxim 8966 and 8997 + * + * Copyright (C) 2011 Samsung Electronics + * MyungJoo Ham <myungjoo.ham@smasung.com> + * + * 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 + * + * This driver is based on max8998.c + */ + +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/pm_runtime.h> +#include <linux/mutex.h> +#include <linux/mfd/core.h> +#include <linux/mfd/max8997.h> +#include <linux/mfd/max8997-private.h> + +#define I2C_ADDR_PMIC (0xCC >> 1) +#define I2C_ADDR_MUIC (0x4A >> 1) +#define I2C_ADDR_BATTERY (0x6C >> 1) +#define I2C_ADDR_RTC (0x0C >> 1) +#define I2C_ADDR_HAPTIC (0x90 >> 1) + +static struct mfd_cell max8997_devs[] = { + { .name = "max8997-pmic", }, + { .name = "max8997-rtc", }, + { .name = "max8997-battery", }, + { .name = "max8997-haptic", }, + { .name = "max8997-muic", }, + { .name = "max8997-flash", }, +}; + +int max8997_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_read_byte_data(i2c, reg); + mutex_unlock(&max8997->iolock); + if (ret < 0) + return ret; + + ret &= 0xff; + *dest = ret; + return 0; +} +EXPORT_SYMBOL_GPL(max8997_read_reg); + +int max8997_bulk_read(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_read_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&max8997->iolock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(max8997_bulk_read); + +int max8997_write_reg(struct i2c_client *i2c, u8 reg, u8 value) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_write_byte_data(i2c, reg, value); + mutex_unlock(&max8997->iolock); + return ret; +} +EXPORT_SYMBOL_GPL(max8997_write_reg); + +int max8997_bulk_write(struct i2c_client *i2c, u8 reg, int count, u8 *buf) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_write_i2c_block_data(i2c, reg, count, buf); + mutex_unlock(&max8997->iolock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL_GPL(max8997_bulk_write); + +int max8997_update_reg(struct i2c_client *i2c, u8 reg, u8 val, u8 mask) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int ret; + + mutex_lock(&max8997->iolock); + ret = i2c_smbus_read_byte_data(i2c, reg); + if (ret >= 0) { + u8 old_val = ret & 0xff; + u8 new_val = (val & mask) | (old_val & (~mask)); + ret = i2c_smbus_write_byte_data(i2c, reg, new_val); + } + mutex_unlock(&max8997->iolock); + return ret; +} +EXPORT_SYMBOL_GPL(max8997_update_reg); + +static int max8997_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct max8997_dev *max8997; + struct max8997_platform_data *pdata = i2c->dev.platform_data; + int ret = 0; + + max8997 = kzalloc(sizeof(struct max8997_dev), GFP_KERNEL); + if (max8997 == NULL) + return -ENOMEM; + + i2c_set_clientdata(i2c, max8997); + max8997->dev = &i2c->dev; + max8997->i2c = i2c; + max8997->type = id->driver_data; + + if (!pdata) + goto err; + + max8997->wakeup = pdata->wakeup; + + mutex_init(&max8997->iolock); + + max8997->rtc = i2c_new_dummy(i2c->adapter, I2C_ADDR_RTC); + i2c_set_clientdata(max8997->rtc, max8997); + max8997->haptic = i2c_new_dummy(i2c->adapter, I2C_ADDR_HAPTIC); + i2c_set_clientdata(max8997->haptic, max8997); + max8997->muic = i2c_new_dummy(i2c->adapter, I2C_ADDR_MUIC); + i2c_set_clientdata(max8997->muic, max8997); + + pm_runtime_set_active(max8997->dev); + + mfd_add_devices(max8997->dev, -1, max8997_devs, + ARRAY_SIZE(max8997_devs), + NULL, 0); + + /* + * TODO: enable others (flash, muic, rtc, battery, ...) and + * check the return value + */ + + if (ret < 0) + goto err_mfd; + + return ret; + +err_mfd: + mfd_remove_devices(max8997->dev); + i2c_unregister_device(max8997->muic); + i2c_unregister_device(max8997->haptic); + i2c_unregister_device(max8997->rtc); +err: + kfree(max8997); + return ret; +} + +static int max8997_i2c_remove(struct i2c_client *i2c) +{ + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + + mfd_remove_devices(max8997->dev); + i2c_unregister_device(max8997->muic); + i2c_unregister_device(max8997->haptic); + i2c_unregister_device(max8997->rtc); + kfree(max8997); + + return 0; +} + +static const struct i2c_device_id max8997_i2c_id[] = { + { "max8997", TYPE_MAX8997 }, + { "max8966", TYPE_MAX8966 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); + +u8 max8997_dumpaddr_pmic[] = { + MAX8997_REG_INT1MSK, + MAX8997_REG_INT2MSK, + MAX8997_REG_INT3MSK, + MAX8997_REG_INT4MSK, + MAX8997_REG_MAINCON1, + MAX8997_REG_MAINCON2, + MAX8997_REG_BUCKRAMP, + MAX8997_REG_BUCK1CTRL, + MAX8997_REG_BUCK1DVS1, + MAX8997_REG_BUCK1DVS2, + MAX8997_REG_BUCK1DVS3, + MAX8997_REG_BUCK1DVS4, + MAX8997_REG_BUCK1DVS5, + MAX8997_REG_BUCK1DVS6, + MAX8997_REG_BUCK1DVS7, + MAX8997_REG_BUCK1DVS8, + MAX8997_REG_BUCK2CTRL, + MAX8997_REG_BUCK2DVS1, + MAX8997_REG_BUCK2DVS2, + MAX8997_REG_BUCK2DVS3, + MAX8997_REG_BUCK2DVS4, + MAX8997_REG_BUCK2DVS5, + MAX8997_REG_BUCK2DVS6, + MAX8997_REG_BUCK2DVS7, + MAX8997_REG_BUCK2DVS8, + MAX8997_REG_BUCK3CTRL, + MAX8997_REG_BUCK3DVS, + MAX8997_REG_BUCK4CTRL, + MAX8997_REG_BUCK4DVS, + MAX8997_REG_BUCK5CTRL, + MAX8997_REG_BUCK5DVS1, + MAX8997_REG_BUCK5DVS2, + MAX8997_REG_BUCK5DVS3, + MAX8997_REG_BUCK5DVS4, + MAX8997_REG_BUCK5DVS5, + MAX8997_REG_BUCK5DVS6, + MAX8997_REG_BUCK5DVS7, + MAX8997_REG_BUCK5DVS8, + MAX8997_REG_BUCK6CTRL, + MAX8997_REG_BUCK6BPSKIPCTRL, + MAX8997_REG_BUCK7CTRL, + MAX8997_REG_BUCK7DVS, + MAX8997_REG_LDO1CTRL, + MAX8997_REG_LDO2CTRL, + MAX8997_REG_LDO3CTRL, + MAX8997_REG_LDO4CTRL, + MAX8997_REG_LDO5CTRL, + MAX8997_REG_LDO6CTRL, + MAX8997_REG_LDO7CTRL, + MAX8997_REG_LDO8CTRL, + MAX8997_REG_LDO9CTRL, + MAX8997_REG_LDO10CTRL, + MAX8997_REG_LDO11CTRL, + MAX8997_REG_LDO12CTRL, + MAX8997_REG_LDO13CTRL, + MAX8997_REG_LDO14CTRL, + MAX8997_REG_LDO15CTRL, + MAX8997_REG_LDO16CTRL, + MAX8997_REG_LDO17CTRL, + MAX8997_REG_LDO18CTRL, + MAX8997_REG_LDO21CTRL, + MAX8997_REG_MBCCTRL1, + MAX8997_REG_MBCCTRL2, + MAX8997_REG_MBCCTRL3, + MAX8997_REG_MBCCTRL4, + MAX8997_REG_MBCCTRL5, + MAX8997_REG_MBCCTRL6, + MAX8997_REG_OTPCGHCVS, + MAX8997_REG_SAFEOUTCTRL, + MAX8997_REG_LBCNFG1, + MAX8997_REG_LBCNFG2, + MAX8997_REG_BBCCTRL, + + MAX8997_REG_FLASH1_CUR, + MAX8997_REG_FLASH2_CUR, + MAX8997_REG_MOVIE_CUR, + MAX8997_REG_GSMB_CUR, + MAX8997_REG_BOOST_CNTL, + MAX8997_REG_LEN_CNTL, + MAX8997_REG_FLASH_CNTL, + MAX8997_REG_WDT_CNTL, + MAX8997_REG_MAXFLASH1, + MAX8997_REG_MAXFLASH2, + MAX8997_REG_FLASHSTATUSMASK, + + MAX8997_REG_GPIOCNTL1, + MAX8997_REG_GPIOCNTL2, + MAX8997_REG_GPIOCNTL3, + MAX8997_REG_GPIOCNTL4, + MAX8997_REG_GPIOCNTL5, + MAX8997_REG_GPIOCNTL6, + MAX8997_REG_GPIOCNTL7, + MAX8997_REG_GPIOCNTL8, + MAX8997_REG_GPIOCNTL9, + MAX8997_REG_GPIOCNTL10, + MAX8997_REG_GPIOCNTL11, + MAX8997_REG_GPIOCNTL12, + + MAX8997_REG_LDO1CONFIG, + MAX8997_REG_LDO2CONFIG, + MAX8997_REG_LDO3CONFIG, + MAX8997_REG_LDO4CONFIG, + MAX8997_REG_LDO5CONFIG, + MAX8997_REG_LDO6CONFIG, + MAX8997_REG_LDO7CONFIG, + MAX8997_REG_LDO8CONFIG, + MAX8997_REG_LDO9CONFIG, + MAX8997_REG_LDO10CONFIG, + MAX8997_REG_LDO11CONFIG, + MAX8997_REG_LDO12CONFIG, + MAX8997_REG_LDO13CONFIG, + MAX8997_REG_LDO14CONFIG, + MAX8997_REG_LDO15CONFIG, + MAX8997_REG_LDO16CONFIG, + MAX8997_REG_LDO17CONFIG, + MAX8997_REG_LDO18CONFIG, + MAX8997_REG_LDO21CONFIG, + + MAX8997_REG_DVSOKTIMER1, + MAX8997_REG_DVSOKTIMER2, + MAX8997_REG_DVSOKTIMER4, + MAX8997_REG_DVSOKTIMER5, +}; + +u8 max8997_dumpaddr_muic[] = { + MAX8997_MUIC_REG_INTMASK1, + MAX8997_MUIC_REG_INTMASK2, + MAX8997_MUIC_REG_INTMASK3, + MAX8997_MUIC_REG_CDETCTRL, + MAX8997_MUIC_REG_CONTROL1, + MAX8997_MUIC_REG_CONTROL2, + MAX8997_MUIC_REG_CONTROL3, +}; + +u8 max8997_dumpaddr_haptic[] = { + MAX8997_HAPTIC_REG_CONF1, + MAX8997_HAPTIC_REG_CONF2, + MAX8997_HAPTIC_REG_DRVCONF, + MAX8997_HAPTIC_REG_CYCLECONF1, + MAX8997_HAPTIC_REG_CYCLECONF2, + MAX8997_HAPTIC_REG_SIGCONF1, + MAX8997_HAPTIC_REG_SIGCONF2, + MAX8997_HAPTIC_REG_SIGCONF3, + MAX8997_HAPTIC_REG_SIGCONF4, + MAX8997_HAPTIC_REG_SIGDC1, + MAX8997_HAPTIC_REG_SIGDC2, + MAX8997_HAPTIC_REG_SIGPWMDC1, + MAX8997_HAPTIC_REG_SIGPWMDC2, + MAX8997_HAPTIC_REG_SIGPWMDC3, + MAX8997_HAPTIC_REG_SIGPWMDC4, +}; + +static int max8997_freeze(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++) + max8997_read_reg(i2c, max8997_dumpaddr_pmic[i], + &max8997->reg_dump[i]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++) + max8997_read_reg(i2c, max8997_dumpaddr_muic[i], + &max8997->reg_dump[i + MAX8997_REG_PMIC_END]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++) + max8997_read_reg(i2c, max8997_dumpaddr_haptic[i], + &max8997->reg_dump[i + MAX8997_REG_PMIC_END + + MAX8997_MUIC_REG_END]); + + return 0; +} + +static int max8997_restore(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + int i; + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_pmic); i++) + max8997_write_reg(i2c, max8997_dumpaddr_pmic[i], + max8997->reg_dump[i]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_muic); i++) + max8997_write_reg(i2c, max8997_dumpaddr_muic[i], + max8997->reg_dump[i + MAX8997_REG_PMIC_END]); + + for (i = 0; i < ARRAY_SIZE(max8997_dumpaddr_haptic); i++) + max8997_write_reg(i2c, max8997_dumpaddr_haptic[i], + max8997->reg_dump[i + MAX8997_REG_PMIC_END + + MAX8997_MUIC_REG_END]); + + return 0; +} + +const struct dev_pm_ops max8997_pm = { + .freeze = max8997_freeze, + .restore = max8997_restore, +}; + +static struct i2c_driver max8997_i2c_driver = { + .driver = { + .name = "max8997", + .owner = THIS_MODULE, + .pm = &max8997_pm, + }, + .probe = max8997_i2c_probe, + .remove = max8997_i2c_remove, + .id_table = max8997_i2c_id, +}; + +static int __init max8997_i2c_init(void) +{ + return i2c_add_driver(&max8997_i2c_driver); +} +/* init early so consumer devices can complete system boot */ +subsys_initcall(max8997_i2c_init); + +static void __exit max8997_i2c_exit(void) +{ + i2c_del_driver(&max8997_i2c_driver); +} +module_exit(max8997_i2c_exit); + +MODULE_DESCRIPTION("MAXIM 8997 multi-function core driver"); +MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/max8998-irq.c b/drivers/mfd/max8998-irq.c index 45bfe77..5919710 100644 --- a/drivers/mfd/max8998-irq.c +++ b/drivers/mfd/max8998-irq.c @@ -102,16 +102,16 @@ irq_to_max8998_irq(struct max8998_dev *max8998, int irq) return &max8998_irqs[irq - max8998->irq_base]; } -static void max8998_irq_lock(unsigned int irq) +static void max8998_irq_lock(struct irq_data *data) { - struct max8998_dev *max8998 = get_irq_chip_data(irq); + struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data); mutex_lock(&max8998->irqlock); } -static void max8998_irq_sync_unlock(unsigned int irq) +static void max8998_irq_sync_unlock(struct irq_data *data) { - struct max8998_dev *max8998 = get_irq_chip_data(irq); + struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data); int i; for (i = 0; i < ARRAY_SIZE(max8998->irq_masks_cur); i++) { @@ -129,28 +129,30 @@ static void max8998_irq_sync_unlock(unsigned int irq) mutex_unlock(&max8998->irqlock); } -static void max8998_irq_unmask(unsigned int irq) +static void max8998_irq_unmask(struct irq_data *data) { - struct max8998_dev *max8998 = get_irq_chip_data(irq); - struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq); + struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data); + struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, + data->irq); max8998->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } -static void max8998_irq_mask(unsigned int irq) +static void max8998_irq_mask(struct irq_data *data) { - struct max8998_dev *max8998 = get_irq_chip_data(irq); - struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, irq); + struct max8998_dev *max8998 = irq_data_get_irq_chip_data(data); + struct max8998_irq_data *irq_data = irq_to_max8998_irq(max8998, + data->irq); max8998->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; } static struct irq_chip max8998_irq_chip = { .name = "max8998", - .bus_lock = max8998_irq_lock, - .bus_sync_unlock = max8998_irq_sync_unlock, - .mask = max8998_irq_mask, - .unmask = max8998_irq_unmask, + .irq_bus_lock = max8998_irq_lock, + .irq_bus_sync_unlock = max8998_irq_sync_unlock, + .irq_mask = max8998_irq_mask, + .irq_unmask = max8998_irq_unmask, }; static irqreturn_t max8998_irq_thread(int irq, void *data) @@ -181,6 +183,13 @@ static irqreturn_t max8998_irq_thread(int irq, void *data) return IRQ_HANDLED; } +int max8998_irq_resume(struct max8998_dev *max8998) +{ + if (max8998->irq && max8998->irq_base) + max8998_irq_thread(max8998->irq_base, max8998); + return 0; +} + int max8998_irq_init(struct max8998_dev *max8998) { int i; @@ -215,14 +224,14 @@ int max8998_irq_init(struct max8998_dev *max8998) /* register with genirq */ for (i = 0; i < MAX8998_IRQ_NR; i++) { cur_irq = i + max8998->irq_base; - set_irq_chip_data(cur_irq, max8998); - set_irq_chip_and_handler(cur_irq, &max8998_irq_chip, + irq_set_chip_data(cur_irq, max8998); + irq_set_chip_and_handler(cur_irq, &max8998_irq_chip, handle_edge_irq); - set_irq_nested_thread(cur_irq, 1); + irq_set_nested_thread(cur_irq, 1); #ifdef CONFIG_ARM set_irq_flags(cur_irq, IRQF_VALID); #else - set_irq_noprobe(cur_irq); + irq_set_noprobe(cur_irq); #endif } diff --git a/drivers/mfd/max8998.c b/drivers/mfd/max8998.c index bb9977b..9ec7570 100644 --- a/drivers/mfd/max8998.c +++ b/drivers/mfd/max8998.c @@ -25,6 +25,8 @@ #include <linux/init.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/pm_runtime.h> #include <linux/mutex.h> #include <linux/mfd/core.h> #include <linux/mfd/max8998.h> @@ -40,6 +42,14 @@ static struct mfd_cell max8998_devs[] = { }, }; +static struct mfd_cell lp3974_devs[] = { + { + .name = "lp3974-pmic", + }, { + .name = "lp3974-rtc", + }, +}; + int max8998_read_reg(struct i2c_client *i2c, u8 reg, u8 *dest) { struct max8998_dev *max8998 = i2c_get_clientdata(i2c); @@ -135,6 +145,7 @@ static int max8998_i2c_probe(struct i2c_client *i2c, if (pdata) { max8998->ono = pdata->ono; max8998->irq_base = pdata->irq_base; + max8998->wakeup = pdata->wakeup; } mutex_init(&max8998->iolock); @@ -143,9 +154,23 @@ static int max8998_i2c_probe(struct i2c_client *i2c, max8998_irq_init(max8998); - ret = mfd_add_devices(max8998->dev, -1, - max8998_devs, ARRAY_SIZE(max8998_devs), - NULL, 0); + pm_runtime_set_active(max8998->dev); + + switch (id->driver_data) { + case TYPE_LP3974: + ret = mfd_add_devices(max8998->dev, -1, + lp3974_devs, ARRAY_SIZE(lp3974_devs), + NULL, 0); + break; + case TYPE_MAX8998: + ret = mfd_add_devices(max8998->dev, -1, + max8998_devs, ARRAY_SIZE(max8998_devs), + NULL, 0); + break; + default: + ret = -EINVAL; + } + if (ret < 0) goto err; @@ -178,10 +203,113 @@ static const struct i2c_device_id max8998_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max8998_i2c_id); +static int max8998_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); + + if (max8998->wakeup) + irq_set_irq_wake(max8998->irq, 1); + return 0; +} + +static int max8998_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8998_dev *max8998 = i2c_get_clientdata(i2c); + + if (max8998->wakeup) + irq_set_irq_wake(max8998->irq, 0); + /* + * In LP3974, if IRQ registers are not "read & clear" + * when it's set during sleep, the interrupt becomes + * disabled. + */ + return max8998_irq_resume(i2c_get_clientdata(i2c)); +} + +struct max8998_reg_dump { + u8 addr; + u8 val; +}; +#define SAVE_ITEM(x) { .addr = (x), .val = 0x0, } +static struct max8998_reg_dump max8998_dump[] = { + SAVE_ITEM(MAX8998_REG_IRQM1), + SAVE_ITEM(MAX8998_REG_IRQM2), + SAVE_ITEM(MAX8998_REG_IRQM3), + SAVE_ITEM(MAX8998_REG_IRQM4), + SAVE_ITEM(MAX8998_REG_STATUSM1), + SAVE_ITEM(MAX8998_REG_STATUSM2), + SAVE_ITEM(MAX8998_REG_CHGR1), + SAVE_ITEM(MAX8998_REG_CHGR2), + SAVE_ITEM(MAX8998_REG_LDO_ACTIVE_DISCHARGE1), + SAVE_ITEM(MAX8998_REG_LDO_ACTIVE_DISCHARGE1), + SAVE_ITEM(MAX8998_REG_BUCK_ACTIVE_DISCHARGE3), + SAVE_ITEM(MAX8998_REG_ONOFF1), + SAVE_ITEM(MAX8998_REG_ONOFF2), + SAVE_ITEM(MAX8998_REG_ONOFF3), + SAVE_ITEM(MAX8998_REG_ONOFF4), + SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE1), + SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE2), + SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE3), + SAVE_ITEM(MAX8998_REG_BUCK1_VOLTAGE4), + SAVE_ITEM(MAX8998_REG_BUCK2_VOLTAGE1), + SAVE_ITEM(MAX8998_REG_BUCK2_VOLTAGE2), + SAVE_ITEM(MAX8998_REG_LDO2_LDO3), + SAVE_ITEM(MAX8998_REG_LDO4), + SAVE_ITEM(MAX8998_REG_LDO5), + SAVE_ITEM(MAX8998_REG_LDO6), + SAVE_ITEM(MAX8998_REG_LDO7), + SAVE_ITEM(MAX8998_REG_LDO8_LDO9), + SAVE_ITEM(MAX8998_REG_LDO10_LDO11), + SAVE_ITEM(MAX8998_REG_LDO12), + SAVE_ITEM(MAX8998_REG_LDO13), + SAVE_ITEM(MAX8998_REG_LDO14), + SAVE_ITEM(MAX8998_REG_LDO15), + SAVE_ITEM(MAX8998_REG_LDO16), + SAVE_ITEM(MAX8998_REG_LDO17), + SAVE_ITEM(MAX8998_REG_BKCHR), + SAVE_ITEM(MAX8998_REG_LBCNFG1), + SAVE_ITEM(MAX8998_REG_LBCNFG2), +}; +/* Save registers before hibernation */ +static int max8998_freeze(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + int i; + + for (i = 0; i < ARRAY_SIZE(max8998_dump); i++) + max8998_read_reg(i2c, max8998_dump[i].addr, + &max8998_dump[i].val); + + return 0; +} + +/* Restore registers after hibernation */ +static int max8998_restore(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + int i; + + for (i = 0; i < ARRAY_SIZE(max8998_dump); i++) + max8998_write_reg(i2c, max8998_dump[i].addr, + max8998_dump[i].val); + + return 0; +} + +static const struct dev_pm_ops max8998_pm = { + .suspend = max8998_suspend, + .resume = max8998_resume, + .freeze = max8998_freeze, + .restore = max8998_restore, +}; + static struct i2c_driver max8998_i2c_driver = { .driver = { .name = "max8998", .owner = THIS_MODULE, + .pm = &max8998_pm, }, .probe = max8998_i2c_probe, .remove = max8998_i2c_remove, diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index a2ac2ed..668634e 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -683,14 +683,13 @@ out: EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, - const char *format, void *pdata, size_t pdata_size) + const char *format, void *pdata) { char buf[30]; const char *name = mc13xxx_get_chipname(mc13xxx); struct mfd_cell cell = { - .platform_data = pdata, - .data_size = pdata_size, + .mfd_data = pdata, }; /* there is no asnprintf in the kernel :-( */ @@ -706,7 +705,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, static int mc13xxx_add_subdevice(struct mc13xxx *mc13xxx, const char *format) { - return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL, 0); + return mc13xxx_add_subdevice_pdata(mc13xxx, format, NULL); } static int mc13xxx_probe(struct spi_device *spi) @@ -749,7 +748,7 @@ static int mc13xxx_probe(struct spi_device *spi) if (ret) { err_mask: err_revision: - mutex_unlock(&mc13xxx->lock); + mc13xxx_unlock(mc13xxx); dev_set_drvdata(&spi->dev, NULL); kfree(mc13xxx); return ret; @@ -764,13 +763,8 @@ err_revision: mc13xxx_add_subdevice(mc13xxx, "%s-codec"); if (pdata->flags & MC13XXX_USE_REGULATOR) { - struct mc13xxx_regulator_platform_data regulator_pdata = { - .num_regulators = pdata->num_regulators, - .regulators = pdata->regulators, - }; - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", - ®ulator_pdata, sizeof(regulator_pdata)); + &pdata->regulators); } if (pdata->flags & MC13XXX_USE_RTC) @@ -779,10 +773,8 @@ err_revision: if (pdata->flags & MC13XXX_USE_TOUCHSCREEN) mc13xxx_add_subdevice(mc13xxx, "%s-ts"); - if (pdata->flags & MC13XXX_USE_LED) { - mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", - pdata->leds, sizeof(*pdata->leds)); - } + if (pdata->flags & MC13XXX_USE_LED) + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds); return 0; } @@ -811,6 +803,7 @@ static const struct spi_device_id mc13xxx_device_id[] = { /* sentinel */ } }; +MODULE_DEVICE_TABLE(spi, mc13xxx_device_id); static struct spi_driver mc13xxx_driver = { .id_table = mc13xxx_device_id, diff --git a/drivers/mfd/menelaus.c b/drivers/mfd/menelaus.c index 4ba85bb..9cee8e7 100644 --- a/drivers/mfd/menelaus.c +++ b/drivers/mfd/menelaus.c @@ -1259,7 +1259,7 @@ static int menelaus_probe(struct i2c_client *client, return 0; fail2: free_irq(client->irq, menelaus); - flush_scheduled_work(); + flush_work_sync(&menelaus->work); fail1: kfree(menelaus); return err; @@ -1270,6 +1270,7 @@ static int __exit menelaus_remove(struct i2c_client *client) struct menelaus_chip *menelaus = i2c_get_clientdata(client); free_irq(client->irq, menelaus); + flush_work_sync(&menelaus->work); kfree(menelaus); the_menelaus = NULL; return 0; diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ec99f68..f4c8c84 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -15,8 +15,59 @@ #include <linux/platform_device.h> #include <linux/acpi.h> #include <linux/mfd/core.h> +#include <linux/pm_runtime.h> #include <linux/slab.h> +int mfd_cell_enable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + int err = 0; + + /* only call enable hook if the cell wasn't previously enabled */ + if (atomic_inc_return(cell->usage_count) == 1) + err = cell->enable(pdev); + + /* if the enable hook failed, decrement counter to allow retries */ + if (err) + atomic_dec(cell->usage_count); + + return err; +} +EXPORT_SYMBOL(mfd_cell_enable); + +int mfd_cell_disable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + int err = 0; + + /* only disable if no other clients are using it */ + if (atomic_dec_return(cell->usage_count) == 0) + err = cell->disable(pdev); + + /* if the disable hook failed, increment to allow retries */ + if (err) + atomic_inc(cell->usage_count); + + /* sanity check; did someone call disable too many times? */ + WARN_ON(atomic_read(cell->usage_count) < 0); + + return err; +} +EXPORT_SYMBOL(mfd_cell_disable); + +static int mfd_platform_add_cell(struct platform_device *pdev, + const struct mfd_cell *cell) +{ + if (!cell) + return 0; + + pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL); + if (!pdev->mfd_cell) + return -ENOMEM; + + return 0; +} + static int mfd_add_device(struct device *parent, int id, const struct mfd_cell *cell, struct resource *mem_base, @@ -36,14 +87,10 @@ static int mfd_add_device(struct device *parent, int id, goto fail_device; pdev->dev.parent = parent; - platform_set_drvdata(pdev, cell->driver_data); - if (cell->data_size) { - ret = platform_device_add_data(pdev, - cell->platform_data, cell->data_size); - if (ret) - goto fail_res; - } + ret = mfd_platform_add_cell(pdev, cell); + if (ret) + goto fail_res; for (r = 0; r < cell->num_resources; r++) { res[r].name = cell->resources[r].name; @@ -82,11 +129,13 @@ static int mfd_add_device(struct device *parent, int id, if (ret) goto fail_res; + if (cell->pm_runtime_no_callbacks) + pm_runtime_no_callbacks(&pdev->dev); + kfree(res); return 0; -/* platform_device_del(pdev); */ fail_res: kfree(res); fail_device: @@ -96,14 +145,22 @@ fail_alloc: } int mfd_add_devices(struct device *parent, int id, - const struct mfd_cell *cells, int n_devs, + struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base) { int i; int ret = 0; + atomic_t *cnts; + + /* initialize reference counting for all cells */ + cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL); + if (!cnts) + return -ENOMEM; for (i = 0; i < n_devs; i++) { + atomic_set(&cnts[i], 0); + cells[i].usage_count = &cnts[i]; ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); if (ret) break; @@ -116,17 +173,58 @@ int mfd_add_devices(struct device *parent, int id, } EXPORT_SYMBOL(mfd_add_devices); -static int mfd_remove_devices_fn(struct device *dev, void *unused) +static int mfd_remove_devices_fn(struct device *dev, void *c) { - platform_device_unregister(to_platform_device(dev)); + struct platform_device *pdev = to_platform_device(dev); + const struct mfd_cell *cell = mfd_get_cell(pdev); + atomic_t **usage_count = c; + + /* find the base address of usage_count pointers (for freeing) */ + if (!*usage_count || (cell->usage_count < *usage_count)) + *usage_count = cell->usage_count; + + platform_device_unregister(pdev); return 0; } void mfd_remove_devices(struct device *parent) { - device_for_each_child(parent, NULL, mfd_remove_devices_fn); + atomic_t *cnts = NULL; + + device_for_each_child(parent, &cnts, mfd_remove_devices_fn); + kfree(cnts); } EXPORT_SYMBOL(mfd_remove_devices); +int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones) +{ + struct mfd_cell cell_entry; + struct device *dev; + struct platform_device *pdev; + int i; + + /* fetch the parent cell's device (should already be registered!) */ + dev = bus_find_device_by_name(&platform_bus_type, NULL, cell); + if (!dev) { + printk(KERN_ERR "failed to find device for cell %s\n", cell); + return -ENODEV; + } + pdev = to_platform_device(dev); + memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry)); + + WARN_ON(!cell_entry.enable); + + for (i = 0; i < n_clones; i++) { + cell_entry.name = clones[i]; + /* don't give up if a single call fails; just report error */ + if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0)) + dev_err(dev, "failed to create platform device '%s'\n", + clones[i]); + } + + return 0; +} +EXPORT_SYMBOL(mfd_clone_cell); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c new file mode 100644 index 0000000..2e16511 --- /dev/null +++ b/drivers/mfd/omap-usb-host.c @@ -0,0 +1,1058 @@ +/** + * omap-usb-host.c - The USBHS core driver for OMAP EHCI & OHCI + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com + * Author: Keshava Munegowda <keshava_mgowda@ti.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * 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, see <http://www.gnu.org/licenses/>. + */ +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/spinlock.h> +#include <linux/gpio.h> +#include <plat/usb.h> + +#define USBHS_DRIVER_NAME "usbhs-omap" +#define OMAP_EHCI_DEVICE "ehci-omap" +#define OMAP_OHCI_DEVICE "ohci-omap3" + +/* OMAP USBHOST Register addresses */ + +/* TLL Register Set */ +#define OMAP_USBTLL_REVISION (0x00) +#define OMAP_USBTLL_SYSCONFIG (0x10) +#define OMAP_USBTLL_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_USBTLL_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_USBTLL_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_USBTLL_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_USBTLL_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_USBTLL_SYSSTATUS (0x14) +#define OMAP_USBTLL_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP_USBTLL_IRQSTATUS (0x18) +#define OMAP_USBTLL_IRQENABLE (0x1C) + +#define OMAP_TLL_SHARED_CONF (0x30) +#define OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN (1 << 6) +#define OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN (1 << 5) +#define OMAP_TLL_SHARED_CONF_USB_DIVRATION (1 << 2) +#define OMAP_TLL_SHARED_CONF_FCLK_REQ (1 << 1) +#define OMAP_TLL_SHARED_CONF_FCLK_IS_ON (1 << 0) + +#define OMAP_TLL_CHANNEL_CONF(num) (0x040 + 0x004 * num) +#define OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT 24 +#define OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF (1 << 11) +#define OMAP_TLL_CHANNEL_CONF_ULPI_ULPIAUTOIDLE (1 << 10) +#define OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE (1 << 9) +#define OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE (1 << 8) +#define OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS (1 << 1) +#define OMAP_TLL_CHANNEL_CONF_CHANEN (1 << 0) + +#define OMAP_TLL_FSLSMODE_6PIN_PHY_DAT_SE0 0x0 +#define OMAP_TLL_FSLSMODE_6PIN_PHY_DP_DM 0x1 +#define OMAP_TLL_FSLSMODE_3PIN_PHY 0x2 +#define OMAP_TLL_FSLSMODE_4PIN_PHY 0x3 +#define OMAP_TLL_FSLSMODE_6PIN_TLL_DAT_SE0 0x4 +#define OMAP_TLL_FSLSMODE_6PIN_TLL_DP_DM 0x5 +#define OMAP_TLL_FSLSMODE_3PIN_TLL 0x6 +#define OMAP_TLL_FSLSMODE_4PIN_TLL 0x7 +#define OMAP_TLL_FSLSMODE_2PIN_TLL_DAT_SE0 0xA +#define OMAP_TLL_FSLSMODE_2PIN_DAT_DP_DM 0xB + +#define OMAP_TLL_ULPI_FUNCTION_CTRL(num) (0x804 + 0x100 * num) +#define OMAP_TLL_ULPI_INTERFACE_CTRL(num) (0x807 + 0x100 * num) +#define OMAP_TLL_ULPI_OTG_CTRL(num) (0x80A + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_RISE(num) (0x80D + 0x100 * num) +#define OMAP_TLL_ULPI_INT_EN_FALL(num) (0x810 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_STATUS(num) (0x813 + 0x100 * num) +#define OMAP_TLL_ULPI_INT_LATCH(num) (0x814 + 0x100 * num) +#define OMAP_TLL_ULPI_DEBUG(num) (0x815 + 0x100 * num) +#define OMAP_TLL_ULPI_SCRATCH_REGISTER(num) (0x816 + 0x100 * num) + +#define OMAP_TLL_CHANNEL_COUNT 3 +#define OMAP_TLL_CHANNEL_1_EN_MASK (1 << 0) +#define OMAP_TLL_CHANNEL_2_EN_MASK (1 << 1) +#define OMAP_TLL_CHANNEL_3_EN_MASK (1 << 2) + +/* UHH Register Set */ +#define OMAP_UHH_REVISION (0x00) +#define OMAP_UHH_SYSCONFIG (0x10) +#define OMAP_UHH_SYSCONFIG_MIDLEMODE (1 << 12) +#define OMAP_UHH_SYSCONFIG_CACTIVITY (1 << 8) +#define OMAP_UHH_SYSCONFIG_SIDLEMODE (1 << 3) +#define OMAP_UHH_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP_UHH_SYSCONFIG_SOFTRESET (1 << 1) +#define OMAP_UHH_SYSCONFIG_AUTOIDLE (1 << 0) + +#define OMAP_UHH_SYSSTATUS (0x14) +#define OMAP_UHH_HOSTCONFIG (0x40) +#define OMAP_UHH_HOSTCONFIG_ULPI_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS (1 << 0) +#define OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS (1 << 11) +#define OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS (1 << 12) +#define OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN (1 << 2) +#define OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN (1 << 3) +#define OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN (1 << 4) +#define OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN (1 << 5) +#define OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS (1 << 8) +#define OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS (1 << 9) +#define OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS (1 << 10) +#define OMAP4_UHH_HOSTCONFIG_APP_START_CLK (1 << 31) + +/* OMAP4-specific defines */ +#define OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR (3 << 2) +#define OMAP4_UHH_SYSCONFIG_NOIDLE (1 << 2) +#define OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR (3 << 4) +#define OMAP4_UHH_SYSCONFIG_NOSTDBY (1 << 4) +#define OMAP4_UHH_SYSCONFIG_SOFTRESET (1 << 0) + +#define OMAP4_P1_MODE_CLEAR (3 << 16) +#define OMAP4_P1_MODE_TLL (1 << 16) +#define OMAP4_P1_MODE_HSIC (3 << 16) +#define OMAP4_P2_MODE_CLEAR (3 << 18) +#define OMAP4_P2_MODE_TLL (1 << 18) +#define OMAP4_P2_MODE_HSIC (3 << 18) + +#define OMAP_REV2_TLL_CHANNEL_COUNT 2 + +#define OMAP_UHH_DEBUG_CSR (0x44) + +/* Values of UHH_REVISION - Note: these are not given in the TRM */ +#define OMAP_USBHS_REV1 0x00000010 /* OMAP3 */ +#define OMAP_USBHS_REV2 0x50700100 /* OMAP4 */ + +#define is_omap_usbhs_rev1(x) (x->usbhs_rev == OMAP_USBHS_REV1) +#define is_omap_usbhs_rev2(x) (x->usbhs_rev == OMAP_USBHS_REV2) + +#define is_ehci_phy_mode(x) (x == OMAP_EHCI_PORT_MODE_PHY) +#define is_ehci_tll_mode(x) (x == OMAP_EHCI_PORT_MODE_TLL) +#define is_ehci_hsic_mode(x) (x == OMAP_EHCI_PORT_MODE_HSIC) + + +struct usbhs_hcd_omap { + struct clk *usbhost_ick; + struct clk *usbhost_hs_fck; + struct clk *usbhost_fs_fck; + struct clk *xclk60mhsp1_ck; + struct clk *xclk60mhsp2_ck; + struct clk *utmi_p1_fck; + struct clk *usbhost_p1_fck; + struct clk *usbtll_p1_fck; + struct clk *utmi_p2_fck; + struct clk *usbhost_p2_fck; + struct clk *usbtll_p2_fck; + struct clk *init_60m_fclk; + struct clk *usbtll_fck; + struct clk *usbtll_ick; + + void __iomem *uhh_base; + void __iomem *tll_base; + + struct usbhs_omap_platform_data platdata; + + u32 usbhs_rev; + spinlock_t lock; + int count; +}; +/*-------------------------------------------------------------------------*/ + +const char usbhs_driver_name[] = USBHS_DRIVER_NAME; +static u64 usbhs_dmamask = ~(u32)0; + +/*-------------------------------------------------------------------------*/ + +static inline void usbhs_write(void __iomem *base, u32 reg, u32 val) +{ + __raw_writel(val, base + reg); +} + +static inline u32 usbhs_read(void __iomem *base, u32 reg) +{ + return __raw_readl(base + reg); +} + +static inline void usbhs_writeb(void __iomem *base, u8 reg, u8 val) +{ + __raw_writeb(val, base + reg); +} + +static inline u8 usbhs_readb(void __iomem *base, u8 reg) +{ + return __raw_readb(base + reg); +} + +/*-------------------------------------------------------------------------*/ + +static struct platform_device *omap_usbhs_alloc_child(const char *name, + struct resource *res, int num_resources, void *pdata, + size_t pdata_size, struct device *dev) +{ + struct platform_device *child; + int ret; + + child = platform_device_alloc(name, 0); + + if (!child) { + dev_err(dev, "platform_device_alloc %s failed\n", name); + goto err_end; + } + + ret = platform_device_add_resources(child, res, num_resources); + if (ret) { + dev_err(dev, "platform_device_add_resources failed\n"); + goto err_alloc; + } + + ret = platform_device_add_data(child, pdata, pdata_size); + if (ret) { + dev_err(dev, "platform_device_add_data failed\n"); + goto err_alloc; + } + + child->dev.dma_mask = &usbhs_dmamask; + child->dev.coherent_dma_mask = 0xffffffff; + child->dev.parent = dev; + + ret = platform_device_add(child); + if (ret) { + dev_err(dev, "platform_device_add failed\n"); + goto err_alloc; + } + + return child; + +err_alloc: + platform_device_put(child); + +err_end: + return NULL; +} + +static int omap_usbhs_alloc_children(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usbhs_hcd_omap *omap; + struct ehci_hcd_omap_platform_data *ehci_data; + struct ohci_hcd_omap_platform_data *ohci_data; + struct platform_device *ehci; + struct platform_device *ohci; + struct resource *res; + struct resource resources[2]; + int ret; + + omap = platform_get_drvdata(pdev); + ehci_data = omap->platdata.ehci_data; + ohci_data = omap->platdata.ohci_data; + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ehci"); + if (!res) { + dev_err(dev, "EHCI get resource IORESOURCE_MEM failed\n"); + ret = -ENODEV; + goto err_end; + } + resources[0] = *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ehci-irq"); + if (!res) { + dev_err(dev, " EHCI get resource IORESOURCE_IRQ failed\n"); + ret = -ENODEV; + goto err_end; + } + resources[1] = *res; + + ehci = omap_usbhs_alloc_child(OMAP_EHCI_DEVICE, resources, 2, ehci_data, + sizeof(*ehci_data), dev); + + if (!ehci) { + dev_err(dev, "omap_usbhs_alloc_child failed\n"); + goto err_end; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ohci"); + if (!res) { + dev_err(dev, "OHCI get resource IORESOURCE_MEM failed\n"); + ret = -ENODEV; + goto err_ehci; + } + resources[0] = *res; + + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "ohci-irq"); + if (!res) { + dev_err(dev, "OHCI get resource IORESOURCE_IRQ failed\n"); + ret = -ENODEV; + goto err_ehci; + } + resources[1] = *res; + + ohci = omap_usbhs_alloc_child(OMAP_OHCI_DEVICE, resources, 2, ohci_data, + sizeof(*ohci_data), dev); + if (!ohci) { + dev_err(dev, "omap_usbhs_alloc_child failed\n"); + goto err_ehci; + } + + return 0; + +err_ehci: + platform_device_put(ehci); + +err_end: + return ret; +} + +/** + * usbhs_omap_probe - initialize TI-based HCDs + * + * Allocates basic resources for this USB host controller. + */ +static int __devinit usbhs_omap_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct usbhs_omap_platform_data *pdata = dev->platform_data; + struct usbhs_hcd_omap *omap; + struct resource *res; + int ret = 0; + int i; + + if (!pdata) { + dev_err(dev, "Missing platform data\n"); + ret = -ENOMEM; + goto end_probe; + } + + omap = kzalloc(sizeof(*omap), GFP_KERNEL); + if (!omap) { + dev_err(dev, "Memory allocation failed\n"); + ret = -ENOMEM; + goto end_probe; + } + + spin_lock_init(&omap->lock); + + for (i = 0; i < OMAP3_HS_USB_PORTS; i++) + omap->platdata.port_mode[i] = pdata->port_mode[i]; + + omap->platdata.ehci_data = pdata->ehci_data; + omap->platdata.ohci_data = pdata->ohci_data; + + omap->usbhost_ick = clk_get(dev, "usbhost_ick"); + if (IS_ERR(omap->usbhost_ick)) { + ret = PTR_ERR(omap->usbhost_ick); + dev_err(dev, "usbhost_ick failed error:%d\n", ret); + goto err_end; + } + + omap->usbhost_hs_fck = clk_get(dev, "hs_fck"); + if (IS_ERR(omap->usbhost_hs_fck)) { + ret = PTR_ERR(omap->usbhost_hs_fck); + dev_err(dev, "usbhost_hs_fck failed error:%d\n", ret); + goto err_usbhost_ick; + } + + omap->usbhost_fs_fck = clk_get(dev, "fs_fck"); + if (IS_ERR(omap->usbhost_fs_fck)) { + ret = PTR_ERR(omap->usbhost_fs_fck); + dev_err(dev, "usbhost_fs_fck failed error:%d\n", ret); + goto err_usbhost_hs_fck; + } + + omap->usbtll_fck = clk_get(dev, "usbtll_fck"); + if (IS_ERR(omap->usbtll_fck)) { + ret = PTR_ERR(omap->usbtll_fck); + dev_err(dev, "usbtll_fck failed error:%d\n", ret); + goto err_usbhost_fs_fck; + } + + omap->usbtll_ick = clk_get(dev, "usbtll_ick"); + if (IS_ERR(omap->usbtll_ick)) { + ret = PTR_ERR(omap->usbtll_ick); + dev_err(dev, "usbtll_ick failed error:%d\n", ret); + goto err_usbtll_fck; + } + + omap->utmi_p1_fck = clk_get(dev, "utmi_p1_gfclk"); + if (IS_ERR(omap->utmi_p1_fck)) { + ret = PTR_ERR(omap->utmi_p1_fck); + dev_err(dev, "utmi_p1_gfclk failed error:%d\n", ret); + goto err_usbtll_ick; + } + + omap->xclk60mhsp1_ck = clk_get(dev, "xclk60mhsp1_ck"); + if (IS_ERR(omap->xclk60mhsp1_ck)) { + ret = PTR_ERR(omap->xclk60mhsp1_ck); + dev_err(dev, "xclk60mhsp1_ck failed error:%d\n", ret); + goto err_utmi_p1_fck; + } + + omap->utmi_p2_fck = clk_get(dev, "utmi_p2_gfclk"); + if (IS_ERR(omap->utmi_p2_fck)) { + ret = PTR_ERR(omap->utmi_p2_fck); + dev_err(dev, "utmi_p2_gfclk failed error:%d\n", ret); + goto err_xclk60mhsp1_ck; + } + + omap->xclk60mhsp2_ck = clk_get(dev, "xclk60mhsp2_ck"); + if (IS_ERR(omap->xclk60mhsp2_ck)) { + ret = PTR_ERR(omap->xclk60mhsp2_ck); + dev_err(dev, "xclk60mhsp2_ck failed error:%d\n", ret); + goto err_utmi_p2_fck; + } + + omap->usbhost_p1_fck = clk_get(dev, "usb_host_hs_utmi_p1_clk"); + if (IS_ERR(omap->usbhost_p1_fck)) { + ret = PTR_ERR(omap->usbhost_p1_fck); + dev_err(dev, "usbhost_p1_fck failed error:%d\n", ret); + goto err_xclk60mhsp2_ck; + } + + omap->usbtll_p1_fck = clk_get(dev, "usb_tll_hs_usb_ch0_clk"); + if (IS_ERR(omap->usbtll_p1_fck)) { + ret = PTR_ERR(omap->usbtll_p1_fck); + dev_err(dev, "usbtll_p1_fck failed error:%d\n", ret); + goto err_usbhost_p1_fck; + } + + omap->usbhost_p2_fck = clk_get(dev, "usb_host_hs_utmi_p2_clk"); + if (IS_ERR(omap->usbhost_p2_fck)) { + ret = PTR_ERR(omap->usbhost_p2_fck); + dev_err(dev, "usbhost_p2_fck failed error:%d\n", ret); + goto err_usbtll_p1_fck; + } + + omap->usbtll_p2_fck = clk_get(dev, "usb_tll_hs_usb_ch1_clk"); + if (IS_ERR(omap->usbtll_p2_fck)) { + ret = PTR_ERR(omap->usbtll_p2_fck); + dev_err(dev, "usbtll_p2_fck failed error:%d\n", ret); + goto err_usbhost_p2_fck; + } + + omap->init_60m_fclk = clk_get(dev, "init_60m_fclk"); + if (IS_ERR(omap->init_60m_fclk)) { + ret = PTR_ERR(omap->init_60m_fclk); + dev_err(dev, "init_60m_fclk failed error:%d\n", ret); + goto err_usbtll_p2_fck; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "uhh"); + if (!res) { + dev_err(dev, "UHH EHCI get resource failed\n"); + ret = -ENODEV; + goto err_init_60m_fclk; + } + + omap->uhh_base = ioremap(res->start, resource_size(res)); + if (!omap->uhh_base) { + dev_err(dev, "UHH ioremap failed\n"); + ret = -ENOMEM; + goto err_init_60m_fclk; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "tll"); + if (!res) { + dev_err(dev, "UHH EHCI get resource failed\n"); + ret = -ENODEV; + goto err_tll; + } + + omap->tll_base = ioremap(res->start, resource_size(res)); + if (!omap->tll_base) { + dev_err(dev, "TLL ioremap failed\n"); + ret = -ENOMEM; + goto err_tll; + } + + platform_set_drvdata(pdev, omap); + + ret = omap_usbhs_alloc_children(pdev); + if (ret) { + dev_err(dev, "omap_usbhs_alloc_children failed\n"); + goto err_alloc; + } + + goto end_probe; + +err_alloc: + iounmap(omap->tll_base); + +err_tll: + iounmap(omap->uhh_base); + +err_init_60m_fclk: + clk_put(omap->init_60m_fclk); + +err_usbtll_p2_fck: + clk_put(omap->usbtll_p2_fck); + +err_usbhost_p2_fck: + clk_put(omap->usbhost_p2_fck); + +err_usbtll_p1_fck: + clk_put(omap->usbtll_p1_fck); + +err_usbhost_p1_fck: + clk_put(omap->usbhost_p1_fck); + +err_xclk60mhsp2_ck: + clk_put(omap->xclk60mhsp2_ck); + +err_utmi_p2_fck: + clk_put(omap->utmi_p2_fck); + +err_xclk60mhsp1_ck: + clk_put(omap->xclk60mhsp1_ck); + +err_utmi_p1_fck: + clk_put(omap->utmi_p1_fck); + +err_usbtll_ick: + clk_put(omap->usbtll_ick); + +err_usbtll_fck: + clk_put(omap->usbtll_fck); + +err_usbhost_fs_fck: + clk_put(omap->usbhost_fs_fck); + +err_usbhost_hs_fck: + clk_put(omap->usbhost_hs_fck); + +err_usbhost_ick: + clk_put(omap->usbhost_ick); + +err_end: + kfree(omap); + +end_probe: + return ret; +} + +/** + * usbhs_omap_remove - shutdown processing for UHH & TLL HCDs + * @pdev: USB Host Controller being removed + * + * Reverses the effect of usbhs_omap_probe(). + */ +static int __devexit usbhs_omap_remove(struct platform_device *pdev) +{ + struct usbhs_hcd_omap *omap = platform_get_drvdata(pdev); + + if (omap->count != 0) { + dev_err(&pdev->dev, + "Either EHCI or OHCI is still using usbhs core\n"); + return -EBUSY; + } + + iounmap(omap->tll_base); + iounmap(omap->uhh_base); + clk_put(omap->init_60m_fclk); + clk_put(omap->usbtll_p2_fck); + clk_put(omap->usbhost_p2_fck); + clk_put(omap->usbtll_p1_fck); + clk_put(omap->usbhost_p1_fck); + clk_put(omap->xclk60mhsp2_ck); + clk_put(omap->utmi_p2_fck); + clk_put(omap->xclk60mhsp1_ck); + clk_put(omap->utmi_p1_fck); + clk_put(omap->usbtll_ick); + clk_put(omap->usbtll_fck); + clk_put(omap->usbhost_fs_fck); + clk_put(omap->usbhost_hs_fck); + clk_put(omap->usbhost_ick); + kfree(omap); + + return 0; +} + +static bool is_ohci_port(enum usbhs_omap_port_mode pmode) +{ + switch (pmode) { + case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0: + case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM: + case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0: + case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM: + case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0: + case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM: + case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0: + case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM: + case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0: + case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM: + return true; + + default: + return false; + } +} + +/* + * convert the port-mode enum to a value we can use in the FSLSMODE + * field of USBTLL_CHANNEL_CONF + */ +static unsigned ohci_omap3_fslsmode(enum usbhs_omap_port_mode mode) +{ + switch (mode) { + case OMAP_USBHS_PORT_MODE_UNUSED: + case OMAP_OHCI_PORT_MODE_PHY_6PIN_DATSE0: + return OMAP_TLL_FSLSMODE_6PIN_PHY_DAT_SE0; + + case OMAP_OHCI_PORT_MODE_PHY_6PIN_DPDM: + return OMAP_TLL_FSLSMODE_6PIN_PHY_DP_DM; + + case OMAP_OHCI_PORT_MODE_PHY_3PIN_DATSE0: + return OMAP_TLL_FSLSMODE_3PIN_PHY; + + case OMAP_OHCI_PORT_MODE_PHY_4PIN_DPDM: + return OMAP_TLL_FSLSMODE_4PIN_PHY; + + case OMAP_OHCI_PORT_MODE_TLL_6PIN_DATSE0: + return OMAP_TLL_FSLSMODE_6PIN_TLL_DAT_SE0; + + case OMAP_OHCI_PORT_MODE_TLL_6PIN_DPDM: + return OMAP_TLL_FSLSMODE_6PIN_TLL_DP_DM; + + case OMAP_OHCI_PORT_MODE_TLL_3PIN_DATSE0: + return OMAP_TLL_FSLSMODE_3PIN_TLL; + + case OMAP_OHCI_PORT_MODE_TLL_4PIN_DPDM: + return OMAP_TLL_FSLSMODE_4PIN_TLL; + + case OMAP_OHCI_PORT_MODE_TLL_2PIN_DATSE0: + return OMAP_TLL_FSLSMODE_2PIN_TLL_DAT_SE0; + + case OMAP_OHCI_PORT_MODE_TLL_2PIN_DPDM: + return OMAP_TLL_FSLSMODE_2PIN_DAT_DP_DM; + default: + pr_warning("Invalid port mode, using default\n"); + return OMAP_TLL_FSLSMODE_6PIN_PHY_DAT_SE0; + } +} + +static void usbhs_omap_tll_init(struct device *dev, u8 tll_channel_count) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = dev->platform_data; + unsigned reg; + int i; + + /* Program Common TLL register */ + reg = usbhs_read(omap->tll_base, OMAP_TLL_SHARED_CONF); + reg |= (OMAP_TLL_SHARED_CONF_FCLK_IS_ON + | OMAP_TLL_SHARED_CONF_USB_DIVRATION); + reg &= ~OMAP_TLL_SHARED_CONF_USB_90D_DDR_EN; + reg &= ~OMAP_TLL_SHARED_CONF_USB_180D_SDR_EN; + + usbhs_write(omap->tll_base, OMAP_TLL_SHARED_CONF, reg); + + /* Enable channels now */ + for (i = 0; i < tll_channel_count; i++) { + reg = usbhs_read(omap->tll_base, + OMAP_TLL_CHANNEL_CONF(i)); + + if (is_ohci_port(pdata->port_mode[i])) { + reg |= ohci_omap3_fslsmode(pdata->port_mode[i]) + << OMAP_TLL_CHANNEL_CONF_FSLSMODE_SHIFT; + reg |= OMAP_TLL_CHANNEL_CONF_CHANMODE_FSLS; + } else if (pdata->port_mode[i] == OMAP_EHCI_PORT_MODE_TLL) { + + /* Disable AutoIdle, BitStuffing and use SDR Mode */ + reg &= ~(OMAP_TLL_CHANNEL_CONF_UTMIAUTOIDLE + | OMAP_TLL_CHANNEL_CONF_ULPINOBITSTUFF + | OMAP_TLL_CHANNEL_CONF_ULPIDDRMODE); + + reg |= (1 << (i + 1)); + } else + continue; + + reg |= OMAP_TLL_CHANNEL_CONF_CHANEN; + usbhs_write(omap->tll_base, + OMAP_TLL_CHANNEL_CONF(i), reg); + + usbhs_writeb(omap->tll_base, + OMAP_TLL_ULPI_SCRATCH_REGISTER(i), 0xbe); + } +} + +static int usbhs_enable(struct device *dev) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = &omap->platdata; + unsigned long flags = 0; + int ret = 0; + unsigned long timeout; + unsigned reg; + + dev_dbg(dev, "starting TI HSUSB Controller\n"); + if (!pdata) { + dev_dbg(dev, "missing platform_data\n"); + return -ENODEV; + } + + spin_lock_irqsave(&omap->lock, flags); + if (omap->count > 0) + goto end_count; + + clk_enable(omap->usbhost_ick); + clk_enable(omap->usbhost_hs_fck); + clk_enable(omap->usbhost_fs_fck); + clk_enable(omap->usbtll_fck); + clk_enable(omap->usbtll_ick); + + if (pdata->ehci_data->phy_reset) { + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) { + gpio_request(pdata->ehci_data->reset_gpio_port[0], + "USB1 PHY reset"); + gpio_direction_output + (pdata->ehci_data->reset_gpio_port[0], 1); + } + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) { + gpio_request(pdata->ehci_data->reset_gpio_port[1], + "USB2 PHY reset"); + gpio_direction_output + (pdata->ehci_data->reset_gpio_port[1], 1); + } + + /* Hold the PHY in RESET for enough time till DIR is high */ + udelay(10); + } + + omap->usbhs_rev = usbhs_read(omap->uhh_base, OMAP_UHH_REVISION); + dev_dbg(dev, "OMAP UHH_REVISION 0x%x\n", omap->usbhs_rev); + + /* perform TLL soft reset, and wait until reset is complete */ + usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, + OMAP_USBTLL_SYSCONFIG_SOFTRESET); + + /* Wait for TLL reset to complete */ + timeout = jiffies + msecs_to_jiffies(1000); + while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) + & OMAP_USBTLL_SYSSTATUS_RESETDONE)) { + cpu_relax(); + + if (time_after(jiffies, timeout)) { + dev_dbg(dev, "operation timed out\n"); + ret = -EINVAL; + goto err_tll; + } + } + + dev_dbg(dev, "TLL RESET DONE\n"); + + /* (1<<3) = no idle mode only for initial debugging */ + usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, + OMAP_USBTLL_SYSCONFIG_ENAWAKEUP | + OMAP_USBTLL_SYSCONFIG_SIDLEMODE | + OMAP_USBTLL_SYSCONFIG_AUTOIDLE); + + /* Put UHH in NoIdle/NoStandby mode */ + reg = usbhs_read(omap->uhh_base, OMAP_UHH_SYSCONFIG); + if (is_omap_usbhs_rev1(omap)) { + reg |= (OMAP_UHH_SYSCONFIG_ENAWAKEUP + | OMAP_UHH_SYSCONFIG_SIDLEMODE + | OMAP_UHH_SYSCONFIG_CACTIVITY + | OMAP_UHH_SYSCONFIG_MIDLEMODE); + reg &= ~OMAP_UHH_SYSCONFIG_AUTOIDLE; + + + } else if (is_omap_usbhs_rev2(omap)) { + reg &= ~OMAP4_UHH_SYSCONFIG_IDLEMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOIDLE; + reg &= ~OMAP4_UHH_SYSCONFIG_STDBYMODE_CLEAR; + reg |= OMAP4_UHH_SYSCONFIG_NOSTDBY; + } + + usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, reg); + + reg = usbhs_read(omap->uhh_base, OMAP_UHH_HOSTCONFIG); + /* setup ULPI bypass and burst configurations */ + reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN + | OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN); + reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK; + reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN; + + if (is_omap_usbhs_rev1(omap)) { + if (pdata->port_mode[0] == OMAP_USBHS_PORT_MODE_UNUSED) + reg &= ~OMAP_UHH_HOSTCONFIG_P1_CONNECT_STATUS; + if (pdata->port_mode[1] == OMAP_USBHS_PORT_MODE_UNUSED) + reg &= ~OMAP_UHH_HOSTCONFIG_P2_CONNECT_STATUS; + if (pdata->port_mode[2] == OMAP_USBHS_PORT_MODE_UNUSED) + reg &= ~OMAP_UHH_HOSTCONFIG_P3_CONNECT_STATUS; + + /* Bypass the TLL module for PHY mode operation */ + if (cpu_is_omap3430() && (omap_rev() <= OMAP3430_REV_ES2_1)) { + dev_dbg(dev, "OMAP3 ES version <= ES2.1\n"); + if (is_ehci_phy_mode(pdata->port_mode[0]) || + is_ehci_phy_mode(pdata->port_mode[1]) || + is_ehci_phy_mode(pdata->port_mode[2])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_BYPASS; + } else { + dev_dbg(dev, "OMAP3 ES version > ES2.1\n"); + if (is_ehci_phy_mode(pdata->port_mode[0])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P1_BYPASS; + if (is_ehci_phy_mode(pdata->port_mode[1])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P2_BYPASS; + if (is_ehci_phy_mode(pdata->port_mode[2])) + reg &= ~OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + else + reg |= OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS; + } + } else if (is_omap_usbhs_rev2(omap)) { + /* Clear port mode fields for PHY mode*/ + reg &= ~OMAP4_P1_MODE_CLEAR; + reg &= ~OMAP4_P2_MODE_CLEAR; + + if (is_ehci_phy_mode(pdata->port_mode[0])) { + ret = clk_set_parent(omap->utmi_p1_fck, + omap->xclk60mhsp1_ck); + if (ret != 0) { + dev_err(dev, "xclk60mhsp1_ck set parent" + "failed error:%d\n", ret); + goto err_tll; + } + } else if (is_ehci_tll_mode(pdata->port_mode[0])) { + ret = clk_set_parent(omap->utmi_p1_fck, + omap->init_60m_fclk); + if (ret != 0) { + dev_err(dev, "init_60m_fclk set parent" + "failed error:%d\n", ret); + goto err_tll; + } + clk_enable(omap->usbhost_p1_fck); + clk_enable(omap->usbtll_p1_fck); + } + + if (is_ehci_phy_mode(pdata->port_mode[1])) { + ret = clk_set_parent(omap->utmi_p2_fck, + omap->xclk60mhsp2_ck); + if (ret != 0) { + dev_err(dev, "xclk60mhsp1_ck set parent" + "failed error:%d\n", ret); + goto err_tll; + } + } else if (is_ehci_tll_mode(pdata->port_mode[1])) { + ret = clk_set_parent(omap->utmi_p2_fck, + omap->init_60m_fclk); + if (ret != 0) { + dev_err(dev, "init_60m_fclk set parent" + "failed error:%d\n", ret); + goto err_tll; + } + clk_enable(omap->usbhost_p2_fck); + clk_enable(omap->usbtll_p2_fck); + } + + clk_enable(omap->utmi_p1_fck); + clk_enable(omap->utmi_p2_fck); + + if (is_ehci_tll_mode(pdata->port_mode[0]) || + (is_ohci_port(pdata->port_mode[0]))) + reg |= OMAP4_P1_MODE_TLL; + else if (is_ehci_hsic_mode(pdata->port_mode[0])) + reg |= OMAP4_P1_MODE_HSIC; + + if (is_ehci_tll_mode(pdata->port_mode[1]) || + (is_ohci_port(pdata->port_mode[1]))) + reg |= OMAP4_P2_MODE_TLL; + else if (is_ehci_hsic_mode(pdata->port_mode[1])) + reg |= OMAP4_P2_MODE_HSIC; + } + + usbhs_write(omap->uhh_base, OMAP_UHH_HOSTCONFIG, reg); + dev_dbg(dev, "UHH setup done, uhh_hostconfig=%x\n", reg); + + if (is_ehci_tll_mode(pdata->port_mode[0]) || + is_ehci_tll_mode(pdata->port_mode[1]) || + is_ehci_tll_mode(pdata->port_mode[2]) || + (is_ohci_port(pdata->port_mode[0])) || + (is_ohci_port(pdata->port_mode[1])) || + (is_ohci_port(pdata->port_mode[2]))) { + + /* Enable UTMI mode for required TLL channels */ + if (is_omap_usbhs_rev2(omap)) + usbhs_omap_tll_init(dev, OMAP_REV2_TLL_CHANNEL_COUNT); + else + usbhs_omap_tll_init(dev, OMAP_TLL_CHANNEL_COUNT); + } + + if (pdata->ehci_data->phy_reset) { + /* Hold the PHY in RESET for enough time till + * PHY is settled and ready + */ + udelay(10); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + gpio_set_value + (pdata->ehci_data->reset_gpio_port[0], 0); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + gpio_set_value + (pdata->ehci_data->reset_gpio_port[1], 0); + } + +end_count: + omap->count++; + spin_unlock_irqrestore(&omap->lock, flags); + return 0; + +err_tll: + if (pdata->ehci_data->phy_reset) { + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + gpio_free(pdata->ehci_data->reset_gpio_port[0]); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + gpio_free(pdata->ehci_data->reset_gpio_port[1]); + } + + clk_disable(omap->usbtll_ick); + clk_disable(omap->usbtll_fck); + clk_disable(omap->usbhost_fs_fck); + clk_disable(omap->usbhost_hs_fck); + clk_disable(omap->usbhost_ick); + spin_unlock_irqrestore(&omap->lock, flags); + return ret; +} + +static void usbhs_disable(struct device *dev) +{ + struct usbhs_hcd_omap *omap = dev_get_drvdata(dev); + struct usbhs_omap_platform_data *pdata = &omap->platdata; + unsigned long flags = 0; + unsigned long timeout; + + dev_dbg(dev, "stopping TI HSUSB Controller\n"); + + spin_lock_irqsave(&omap->lock, flags); + + if (omap->count == 0) + goto end_disble; + + omap->count--; + + if (omap->count != 0) + goto end_disble; + + /* Reset OMAP modules for insmod/rmmod to work */ + usbhs_write(omap->uhh_base, OMAP_UHH_SYSCONFIG, + is_omap_usbhs_rev2(omap) ? + OMAP4_UHH_SYSCONFIG_SOFTRESET : + OMAP_UHH_SYSCONFIG_SOFTRESET); + + timeout = jiffies + msecs_to_jiffies(100); + while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 0))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(dev, "operation timed out\n"); + } + + while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 1))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(dev, "operation timed out\n"); + } + + while (!(usbhs_read(omap->uhh_base, OMAP_UHH_SYSSTATUS) + & (1 << 2))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(dev, "operation timed out\n"); + } + + usbhs_write(omap->tll_base, OMAP_USBTLL_SYSCONFIG, (1 << 1)); + + while (!(usbhs_read(omap->tll_base, OMAP_USBTLL_SYSSTATUS) + & (1 << 0))) { + cpu_relax(); + + if (time_after(jiffies, timeout)) + dev_dbg(dev, "operation timed out\n"); + } + + if (pdata->ehci_data->phy_reset) { + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[0])) + gpio_free(pdata->ehci_data->reset_gpio_port[0]); + + if (gpio_is_valid(pdata->ehci_data->reset_gpio_port[1])) + gpio_free(pdata->ehci_data->reset_gpio_port[1]); + } + + clk_disable(omap->utmi_p2_fck); + clk_disable(omap->utmi_p1_fck); + clk_disable(omap->usbtll_ick); + clk_disable(omap->usbtll_fck); + clk_disable(omap->usbhost_fs_fck); + clk_disable(omap->usbhost_hs_fck); + clk_disable(omap->usbhost_ick); + +end_disble: + spin_unlock_irqrestore(&omap->lock, flags); +} + +int omap_usbhs_enable(struct device *dev) +{ + return usbhs_enable(dev->parent); +} +EXPORT_SYMBOL_GPL(omap_usbhs_enable); + +void omap_usbhs_disable(struct device *dev) +{ + usbhs_disable(dev->parent); +} +EXPORT_SYMBOL_GPL(omap_usbhs_disable); + +static struct platform_driver usbhs_omap_driver = { + .driver = { + .name = (char *)usbhs_driver_name, + .owner = THIS_MODULE, + }, + .remove = __exit_p(usbhs_omap_remove), +}; + +MODULE_AUTHOR("Keshava Munegowda <keshava_mgowda@ti.com>"); +MODULE_ALIAS("platform:" USBHS_DRIVER_NAME); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("usb host common core driver for omap EHCI and OHCI"); + +static int __init omap_usbhs_drvinit(void) +{ + return platform_driver_probe(&usbhs_omap_driver, usbhs_omap_probe); +} + +/* + * init before ehci and ohci drivers; + * The usbhs core driver should be initialized much before + * the omap ehci and ohci probe functions are called. + */ +fs_initcall(omap_usbhs_drvinit); + +static void __exit omap_usbhs_drvexit(void) +{ + platform_driver_unregister(&usbhs_omap_driver); +} +module_exit(omap_usbhs_drvexit); diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 501ce13..57868416 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -21,6 +21,7 @@ #include <linux/workqueue.h> #include <linux/platform_device.h> #include <linux/i2c.h> +#include <linux/pm.h> #include <linux/slab.h> #include <linux/mfd/pcf50633/core.h> @@ -50,7 +51,7 @@ static int __pcf50633_write(struct pcf50633 *pcf, u8 reg, int num, u8 *data) } -/* Read a block of upto 32 regs */ +/* Read a block of up to 32 regs */ int pcf50633_read_block(struct pcf50633 *pcf, u8 reg, int nr_regs, u8 *data) { @@ -64,7 +65,7 @@ int pcf50633_read_block(struct pcf50633 *pcf, u8 reg, } EXPORT_SYMBOL_GPL(pcf50633_read_block); -/* Write a block of upto 32 regs */ +/* Write a block of up to 32 regs */ int pcf50633_write_block(struct pcf50633 *pcf , u8 reg, int nr_regs, u8 *data) { @@ -230,27 +231,26 @@ pcf50633_client_dev_register(struct pcf50633 *pcf, const char *name, } } -#ifdef CONFIG_PM -static int pcf50633_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int pcf50633_suspend(struct device *dev) { - struct pcf50633 *pcf; - pcf = i2c_get_clientdata(client); + struct i2c_client *client = to_i2c_client(dev); + struct pcf50633 *pcf = i2c_get_clientdata(client); return pcf50633_irq_suspend(pcf); } -static int pcf50633_resume(struct i2c_client *client) +static int pcf50633_resume(struct device *dev) { - struct pcf50633 *pcf; - pcf = i2c_get_clientdata(client); + struct i2c_client *client = to_i2c_client(dev); + struct pcf50633 *pcf = i2c_get_clientdata(client); return pcf50633_irq_resume(pcf); } -#else -#define pcf50633_suspend NULL -#define pcf50633_resume NULL #endif +static SIMPLE_DEV_PM_OPS(pcf50633_pm, pcf50633_suspend, pcf50633_resume); + static int __devinit pcf50633_probe(struct i2c_client *client, const struct i2c_device_id *ids) { @@ -356,20 +356,20 @@ static int __devexit pcf50633_remove(struct i2c_client *client) return 0; } -static struct i2c_device_id pcf50633_id_table[] = { +static const struct i2c_device_id pcf50633_id_table[] = { {"pcf50633", 0x73}, {/* end of list */} }; +MODULE_DEVICE_TABLE(i2c, pcf50633_id_table); static struct i2c_driver pcf50633_driver = { .driver = { .name = "pcf50633", + .pm = &pcf50633_pm, }, .id_table = pcf50633_id_table, .probe = pcf50633_probe, .remove = __devexit_p(pcf50633_remove), - .suspend = pcf50633_suspend, - .resume = pcf50633_resume, }; static int __init pcf50633_init(void) diff --git a/drivers/mfd/rdc321x-southbridge.c b/drivers/mfd/rdc321x-southbridge.c index 5092297..10dbe63 100644 --- a/drivers/mfd/rdc321x-southbridge.c +++ b/drivers/mfd/rdc321x-southbridge.c @@ -61,12 +61,12 @@ static struct mfd_cell rdc321x_sb_cells[] = { .name = "rdc321x-wdt", .resources = rdc321x_wdt_resource, .num_resources = ARRAY_SIZE(rdc321x_wdt_resource), - .driver_data = &rdc321x_wdt_pdata, + .mfd_data = &rdc321x_wdt_pdata, }, { .name = "rdc321x-gpio", .resources = rdc321x_gpio_resources, .num_resources = ARRAY_SIZE(rdc321x_gpio_resources), - .driver_data = &rdc321x_gpio_pdata, + .mfd_data = &rdc321x_gpio_pdata, }, }; @@ -97,6 +97,7 @@ static DEFINE_PCI_DEVICE_TABLE(rdc321x_sb_table) = { { PCI_DEVICE(PCI_VENDOR_ID_RDC, PCI_DEVICE_ID_RDC_R6030) }, {} }; +MODULE_DEVICE_TABLE(pci, rdc321x_sb_table); static struct pci_driver rdc321x_sb_driver = { .name = "RDC321x Southbridge", diff --git a/drivers/mfd/sh_mobile_sdhi.c b/drivers/mfd/sh_mobile_sdhi.c deleted file mode 100644 index f1714f9..0000000 --- a/drivers/mfd/sh_mobile_sdhi.c +++ /dev/null @@ -1,196 +0,0 @@ -/* - * SuperH Mobile SDHI - * - * Copyright (C) 2009 Magnus Damm - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Based on "Compaq ASIC3 support": - * - * Copyright 2001 Compaq Computer Corporation. - * Copyright 2004-2005 Phil Blundell - * Copyright 2007-2008 OpenedHand Ltd. - * - * Authors: Phil Blundell <pb@handhelds.org>, - * Samuel Ortiz <sameo@openedhand.com> - * - */ - -#include <linux/kernel.h> -#include <linux/clk.h> -#include <linux/slab.h> -#include <linux/platform_device.h> -#include <linux/mmc/host.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tmio.h> -#include <linux/mfd/sh_mobile_sdhi.h> -#include <linux/sh_dma.h> - -struct sh_mobile_sdhi { - struct clk *clk; - struct tmio_mmc_data mmc_data; - struct mfd_cell cell_mmc; - struct sh_dmae_slave param_tx; - struct sh_dmae_slave param_rx; - struct tmio_mmc_dma dma_priv; -}; - -static struct resource sh_mobile_sdhi_resources[] = { - { - .start = 0x000, - .end = 0x1ff, - .flags = IORESOURCE_MEM, - }, - { - .start = 0, - .end = 0, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct mfd_cell sh_mobile_sdhi_cell = { - .name = "tmio-mmc", - .num_resources = ARRAY_SIZE(sh_mobile_sdhi_resources), - .resources = sh_mobile_sdhi_resources, -}; - -static void sh_mobile_sdhi_set_pwr(struct platform_device *tmio, int state) -{ - struct platform_device *pdev = to_platform_device(tmio->dev.parent); - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - - if (p && p->set_pwr) - p->set_pwr(pdev, state); -} - -static int sh_mobile_sdhi_get_cd(struct platform_device *tmio) -{ - struct platform_device *pdev = to_platform_device(tmio->dev.parent); - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - - if (p && p->get_cd) - return p->get_cd(pdev); - else - return -ENOSYS; -} - -static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev) -{ - struct sh_mobile_sdhi *priv; - struct tmio_mmc_data *mmc_data; - struct sh_mobile_sdhi_info *p = pdev->dev.platform_data; - struct resource *mem; - char clk_name[8]; - int ret, irq; - - mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!mem) - dev_err(&pdev->dev, "missing MEM resource\n"); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - dev_err(&pdev->dev, "missing IRQ resource\n"); - - if (!mem || (irq < 0)) - return -EINVAL; - - priv = kzalloc(sizeof(struct sh_mobile_sdhi), GFP_KERNEL); - if (priv == NULL) { - dev_err(&pdev->dev, "kzalloc failed\n"); - return -ENOMEM; - } - - mmc_data = &priv->mmc_data; - - snprintf(clk_name, sizeof(clk_name), "sdhi%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - ret = PTR_ERR(priv->clk); - kfree(priv); - return ret; - } - - clk_enable(priv->clk); - - mmc_data->hclk = clk_get_rate(priv->clk); - mmc_data->set_pwr = sh_mobile_sdhi_set_pwr; - mmc_data->get_cd = sh_mobile_sdhi_get_cd; - mmc_data->capabilities = MMC_CAP_MMC_HIGHSPEED; - if (p) { - mmc_data->flags = p->tmio_flags; - mmc_data->ocr_mask = p->tmio_ocr_mask; - mmc_data->capabilities |= p->tmio_caps; - } - - /* - * All SDHI blocks support 2-byte and larger block sizes in 4-bit - * bus width mode. - */ - mmc_data->flags |= TMIO_MMC_BLKSZ_2BYTES; - - if (p && p->dma_slave_tx >= 0 && p->dma_slave_rx >= 0) { - priv->param_tx.slave_id = p->dma_slave_tx; - priv->param_rx.slave_id = p->dma_slave_rx; - priv->dma_priv.chan_priv_tx = &priv->param_tx; - priv->dma_priv.chan_priv_rx = &priv->param_rx; - mmc_data->dma = &priv->dma_priv; - } - - memcpy(&priv->cell_mmc, &sh_mobile_sdhi_cell, sizeof(priv->cell_mmc)); - priv->cell_mmc.driver_data = mmc_data; - priv->cell_mmc.platform_data = &priv->cell_mmc; - priv->cell_mmc.data_size = sizeof(priv->cell_mmc); - - platform_set_drvdata(pdev, priv); - - ret = mfd_add_devices(&pdev->dev, pdev->id, - &priv->cell_mmc, 1, mem, irq); - if (ret) { - clk_disable(priv->clk); - clk_put(priv->clk); - kfree(priv); - } - - return ret; -} - -static int sh_mobile_sdhi_remove(struct platform_device *pdev) -{ - struct sh_mobile_sdhi *priv = platform_get_drvdata(pdev); - - mfd_remove_devices(&pdev->dev); - clk_disable(priv->clk); - clk_put(priv->clk); - kfree(priv); - - return 0; -} - -static struct platform_driver sh_mobile_sdhi_driver = { - .driver = { - .name = "sh_mobile_sdhi", - .owner = THIS_MODULE, - }, - .probe = sh_mobile_sdhi_probe, - .remove = __devexit_p(sh_mobile_sdhi_remove), -}; - -static int __init sh_mobile_sdhi_init(void) -{ - return platform_driver_register(&sh_mobile_sdhi_driver); -} - -static void __exit sh_mobile_sdhi_exit(void) -{ - platform_driver_unregister(&sh_mobile_sdhi_driver); -} - -module_init(sh_mobile_sdhi_init); -module_exit(sh_mobile_sdhi_exit); - -MODULE_DESCRIPTION("SuperH Mobile SDHI driver"); -MODULE_AUTHOR("Magnus Damm"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index bc9275c..df3702c 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -26,7 +26,7 @@ #include <linux/sm501-regs.h> #include <linux/serial_8250.h> -#include <asm/io.h> +#include <linux/io.h> struct sm501_device { struct list_head list; @@ -133,10 +133,10 @@ static unsigned long decode_div(unsigned long pll2, unsigned long val, static void sm501_dump_clk(struct sm501_devdata *sm) { - unsigned long misct = readl(sm->regs + SM501_MISC_TIMING); - unsigned long pm0 = readl(sm->regs + SM501_POWER_MODE_0_CLOCK); - unsigned long pm1 = readl(sm->regs + SM501_POWER_MODE_1_CLOCK); - unsigned long pmc = readl(sm->regs + SM501_POWER_MODE_CONTROL); + unsigned long misct = smc501_readl(sm->regs + SM501_MISC_TIMING); + unsigned long pm0 = smc501_readl(sm->regs + SM501_POWER_MODE_0_CLOCK); + unsigned long pm1 = smc501_readl(sm->regs + SM501_POWER_MODE_1_CLOCK); + unsigned long pmc = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); unsigned long sdclk0, sdclk1; unsigned long pll2 = 0; @@ -193,29 +193,29 @@ static void sm501_dump_regs(struct sm501_devdata *sm) void __iomem *regs = sm->regs; dev_info(sm->dev, "System Control %08x\n", - readl(regs + SM501_SYSTEM_CONTROL)); + smc501_readl(regs + SM501_SYSTEM_CONTROL)); dev_info(sm->dev, "Misc Control %08x\n", - readl(regs + SM501_MISC_CONTROL)); + smc501_readl(regs + SM501_MISC_CONTROL)); dev_info(sm->dev, "GPIO Control Low %08x\n", - readl(regs + SM501_GPIO31_0_CONTROL)); + smc501_readl(regs + SM501_GPIO31_0_CONTROL)); dev_info(sm->dev, "GPIO Control Hi %08x\n", - readl(regs + SM501_GPIO63_32_CONTROL)); + smc501_readl(regs + SM501_GPIO63_32_CONTROL)); dev_info(sm->dev, "DRAM Control %08x\n", - readl(regs + SM501_DRAM_CONTROL)); + smc501_readl(regs + SM501_DRAM_CONTROL)); dev_info(sm->dev, "Arbitration Ctrl %08x\n", - readl(regs + SM501_ARBTRTN_CONTROL)); + smc501_readl(regs + SM501_ARBTRTN_CONTROL)); dev_info(sm->dev, "Misc Timing %08x\n", - readl(regs + SM501_MISC_TIMING)); + smc501_readl(regs + SM501_MISC_TIMING)); } static void sm501_dump_gate(struct sm501_devdata *sm) { dev_info(sm->dev, "CurrentGate %08x\n", - readl(sm->regs + SM501_CURRENT_GATE)); + smc501_readl(sm->regs + SM501_CURRENT_GATE)); dev_info(sm->dev, "CurrentClock %08x\n", - readl(sm->regs + SM501_CURRENT_CLOCK)); + smc501_readl(sm->regs + SM501_CURRENT_CLOCK)); dev_info(sm->dev, "PowerModeControl %08x\n", - readl(sm->regs + SM501_POWER_MODE_CONTROL)); + smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL)); } #else @@ -231,7 +231,7 @@ static inline void sm501_dump_clk(struct sm501_devdata *sm) { } static void sm501_sync_regs(struct sm501_devdata *sm) { - readl(sm->regs); + smc501_readl(sm->regs); } static inline void sm501_mdelay(struct sm501_devdata *sm, unsigned int delay) @@ -261,11 +261,11 @@ int sm501_misc_control(struct device *dev, spin_lock_irqsave(&sm->reg_lock, save); - misc = readl(sm->regs + SM501_MISC_CONTROL); + misc = smc501_readl(sm->regs + SM501_MISC_CONTROL); to = (misc & ~clear) | set; if (to != misc) { - writel(to, sm->regs + SM501_MISC_CONTROL); + smc501_writel(to, sm->regs + SM501_MISC_CONTROL); sm501_sync_regs(sm); dev_dbg(sm->dev, "MISC_CONTROL %08lx\n", misc); @@ -294,11 +294,11 @@ unsigned long sm501_modify_reg(struct device *dev, spin_lock_irqsave(&sm->reg_lock, save); - data = readl(sm->regs + reg); + data = smc501_readl(sm->regs + reg); data |= set; data &= ~clear; - writel(data, sm->regs + reg); + smc501_writel(data, sm->regs + reg); sm501_sync_regs(sm); spin_unlock_irqrestore(&sm->reg_lock, save); @@ -322,9 +322,9 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) mutex_lock(&sm->clock_lock); - mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); - gate = readl(sm->regs + SM501_CURRENT_GATE); - clock = readl(sm->regs + SM501_CURRENT_CLOCK); + mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); + gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); + clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); mode &= 3; /* get current power mode */ @@ -356,14 +356,14 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) switch (mode) { case 1: - writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); - writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); + smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); + smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); mode = 0; break; case 2: case 0: - writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); - writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); + smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); + smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); mode = 1; break; @@ -372,7 +372,7 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to) goto already; } - writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); + smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); sm501_sync_regs(sm); dev_dbg(sm->dev, "gate %08lx, clock %08lx, mode %08lx\n", @@ -519,9 +519,9 @@ unsigned long sm501_set_clock(struct device *dev, unsigned long req_freq) { struct sm501_devdata *sm = dev_get_drvdata(dev); - unsigned long mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); - unsigned long gate = readl(sm->regs + SM501_CURRENT_GATE); - unsigned long clock = readl(sm->regs + SM501_CURRENT_CLOCK); + unsigned long mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); + unsigned long gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); + unsigned long clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); unsigned char reg; unsigned int pll_reg = 0; unsigned long sm501_freq; /* the actual frequency achieved */ @@ -592,9 +592,9 @@ unsigned long sm501_set_clock(struct device *dev, mutex_lock(&sm->clock_lock); - mode = readl(sm->regs + SM501_POWER_MODE_CONTROL); - gate = readl(sm->regs + SM501_CURRENT_GATE); - clock = readl(sm->regs + SM501_CURRENT_CLOCK); + mode = smc501_readl(sm->regs + SM501_POWER_MODE_CONTROL); + gate = smc501_readl(sm->regs + SM501_CURRENT_GATE); + clock = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); clock = clock & ~(0xFF << clksrc); clock |= reg<<clksrc; @@ -603,14 +603,14 @@ unsigned long sm501_set_clock(struct device *dev, switch (mode) { case 1: - writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); - writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); + smc501_writel(gate, sm->regs + SM501_POWER_MODE_0_GATE); + smc501_writel(clock, sm->regs + SM501_POWER_MODE_0_CLOCK); mode = 0; break; case 2: case 0: - writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); - writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); + smc501_writel(gate, sm->regs + SM501_POWER_MODE_1_GATE); + smc501_writel(clock, sm->regs + SM501_POWER_MODE_1_CLOCK); mode = 1; break; @@ -619,10 +619,11 @@ unsigned long sm501_set_clock(struct device *dev, return -1; } - writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); + smc501_writel(mode, sm->regs + SM501_POWER_MODE_CONTROL); if (pll_reg) - writel(pll_reg, sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL); + smc501_writel(pll_reg, + sm->regs + SM501_PROGRAMMABLE_PLL_CONTROL); sm501_sync_regs(sm); @@ -745,11 +746,8 @@ static int sm501_register_device(struct sm501_devdata *sm, int ret; for (ptr = 0; ptr < pdev->num_resources; ptr++) { - printk(KERN_DEBUG "%s[%d] flags %08lx: %08llx..%08llx\n", - pdev->name, ptr, - pdev->resource[ptr].flags, - (unsigned long long)pdev->resource[ptr].start, - (unsigned long long)pdev->resource[ptr].end); + printk(KERN_DEBUG "%s[%d] %pR\n", + pdev->name, ptr, &pdev->resource[ptr]); } ret = platform_device_register(pdev); @@ -905,7 +903,7 @@ static int sm501_gpio_get(struct gpio_chip *chip, unsigned offset) struct sm501_gpio_chip *smgpio = to_sm501_gpio(chip); unsigned long result; - result = readl(smgpio->regbase + SM501_GPIO_DATA_LOW); + result = smc501_readl(smgpio->regbase + SM501_GPIO_DATA_LOW); result >>= offset; return result & 1UL; @@ -918,13 +916,13 @@ static void sm501_gpio_ensure_gpio(struct sm501_gpio_chip *smchip, /* check and modify if this pin is not set as gpio. */ - if (readl(smchip->control) & bit) { + if (smc501_readl(smchip->control) & bit) { dev_info(sm501_gpio_to_dev(smchip->ourgpio)->dev, "changing mode of gpio, bit %08lx\n", bit); - ctrl = readl(smchip->control); + ctrl = smc501_readl(smchip->control); ctrl &= ~bit; - writel(ctrl, smchip->control); + smc501_writel(ctrl, smchip->control); sm501_sync_regs(sm501_gpio_to_dev(smchip->ourgpio)); } @@ -945,10 +943,10 @@ static void sm501_gpio_set(struct gpio_chip *chip, unsigned offset, int value) spin_lock_irqsave(&smgpio->lock, save); - val = readl(regs + SM501_GPIO_DATA_LOW) & ~bit; + val = smc501_readl(regs + SM501_GPIO_DATA_LOW) & ~bit; if (value) val |= bit; - writel(val, regs); + smc501_writel(val, regs); sm501_sync_regs(sm501_gpio_to_dev(smgpio)); sm501_gpio_ensure_gpio(smchip, bit); @@ -970,8 +968,8 @@ static int sm501_gpio_input(struct gpio_chip *chip, unsigned offset) spin_lock_irqsave(&smgpio->lock, save); - ddr = readl(regs + SM501_GPIO_DDR_LOW); - writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); + ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW); + smc501_writel(ddr & ~bit, regs + SM501_GPIO_DDR_LOW); sm501_sync_regs(sm501_gpio_to_dev(smgpio)); sm501_gpio_ensure_gpio(smchip, bit); @@ -997,18 +995,18 @@ static int sm501_gpio_output(struct gpio_chip *chip, spin_lock_irqsave(&smgpio->lock, save); - val = readl(regs + SM501_GPIO_DATA_LOW); + val = smc501_readl(regs + SM501_GPIO_DATA_LOW); if (value) val |= bit; else val &= ~bit; - writel(val, regs); + smc501_writel(val, regs); - ddr = readl(regs + SM501_GPIO_DDR_LOW); - writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); + ddr = smc501_readl(regs + SM501_GPIO_DDR_LOW); + smc501_writel(ddr | bit, regs + SM501_GPIO_DDR_LOW); sm501_sync_regs(sm501_gpio_to_dev(smgpio)); - writel(val, regs + SM501_GPIO_DATA_LOW); + smc501_writel(val, regs + SM501_GPIO_DATA_LOW); sm501_sync_regs(sm501_gpio_to_dev(smgpio)); spin_unlock_irqrestore(&smgpio->lock, save); @@ -1234,7 +1232,7 @@ static ssize_t sm501_dbg_regs(struct device *dev, for (reg = 0x00; reg < 0x70; reg += 4) { ret = sprintf(ptr, "%08x = %08x\n", - reg, readl(sm->regs + reg)); + reg, smc501_readl(sm->regs + reg)); ptr += ret; } @@ -1258,10 +1256,10 @@ static inline void sm501_init_reg(struct sm501_devdata *sm, { unsigned long tmp; - tmp = readl(sm->regs + reg); + tmp = smc501_readl(sm->regs + reg); tmp &= ~r->mask; tmp |= r->set; - writel(tmp, sm->regs + reg); + smc501_writel(tmp, sm->regs + reg); } /* sm501_init_regs @@ -1302,7 +1300,7 @@ static void sm501_init_regs(struct sm501_devdata *sm, static int sm501_check_clocks(struct sm501_devdata *sm) { - unsigned long pwrmode = readl(sm->regs + SM501_CURRENT_CLOCK); + unsigned long pwrmode = smc501_readl(sm->regs + SM501_CURRENT_CLOCK); unsigned long msrc = (pwrmode & SM501_POWERMODE_M_SRC); unsigned long m1src = (pwrmode & SM501_POWERMODE_M1_SRC); @@ -1337,7 +1335,7 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm) INIT_LIST_HEAD(&sm->devices); - devid = readl(sm->regs + SM501_DEVICEID); + devid = smc501_readl(sm->regs + SM501_DEVICEID); if ((devid & SM501_DEVICEID_IDMASK) != SM501_DEVICEID_SM501) { dev_err(sm->dev, "incorrect device id %08lx\n", devid); @@ -1345,9 +1343,9 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm) } /* disable irqs */ - writel(0, sm->regs + SM501_IRQ_MASK); + smc501_writel(0, sm->regs + SM501_IRQ_MASK); - dramctrl = readl(sm->regs + SM501_DRAM_CONTROL); + dramctrl = smc501_readl(sm->regs + SM501_DRAM_CONTROL); mem_avail = sm501_mem_local[(dramctrl >> 13) & 0x7]; dev_info(sm->dev, "SM501 At %p: Version %08lx, %ld Mb, IRQ %d\n", @@ -1379,7 +1377,7 @@ static int __devinit sm501_init_dev(struct sm501_devdata *sm) sm501_register_gpio(sm); } - if (pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) { + if (pdata && pdata->gpio_i2c != NULL && pdata->gpio_i2c_nr > 0) { if (!sm501_gpio_isregistered(sm)) dev_err(sm->dev, "no gpio available for i2c gpio.\n"); else @@ -1424,6 +1422,7 @@ static int __devinit sm501_plat_probe(struct platform_device *dev) sm->io_res = platform_get_resource(dev, IORESOURCE_MEM, 1); sm->mem_res = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (sm->io_res == NULL || sm->mem_res == NULL) { dev_err(&dev->dev, "failed to get IO resource\n"); ret = -ENOENT; @@ -1492,7 +1491,7 @@ static int sm501_plat_suspend(struct platform_device *pdev, pm_message_t state) struct sm501_devdata *sm = platform_get_drvdata(pdev); sm->in_suspend = 1; - sm->pm_misc = readl(sm->regs + SM501_MISC_CONTROL); + sm->pm_misc = smc501_readl(sm->regs + SM501_MISC_CONTROL); sm501_dump_regs(sm); @@ -1516,9 +1515,9 @@ static int sm501_plat_resume(struct platform_device *pdev) /* check to see if we are in the same state as when suspended */ - if (readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) { + if (smc501_readl(sm->regs + SM501_MISC_CONTROL) != sm->pm_misc) { dev_info(sm->dev, "SM501_MISC_CONTROL changed over sleep\n"); - writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL); + smc501_writel(sm->pm_misc, sm->regs + SM501_MISC_CONTROL); /* our suspend causes the controller state to change, * either by something attempting setup, power loss, @@ -1737,10 +1736,16 @@ static struct pci_driver sm501_pci_driver = { MODULE_ALIAS("platform:sm501"); +static struct of_device_id __devinitdata of_sm501_match_tbl[] = { + { .compatible = "smi,sm501", }, + { /* end */ } +}; + static struct platform_driver sm501_plat_driver = { .driver = { .name = "sm501", .owner = THIS_MODULE, + .of_match_table = of_sm501_match_tbl, }, .probe = sm501_plat_probe, .remove = sm501_plat_remove, diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index b11487f..7ab7746 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -699,16 +699,16 @@ static irqreturn_t stmpe_irq(int irq, void *data) return IRQ_HANDLED; } -static void stmpe_irq_lock(unsigned int irq) +static void stmpe_irq_lock(struct irq_data *data) { - struct stmpe *stmpe = get_irq_chip_data(irq); + struct stmpe *stmpe = irq_data_get_irq_chip_data(data); mutex_lock(&stmpe->irq_lock); } -static void stmpe_irq_sync_unlock(unsigned int irq) +static void stmpe_irq_sync_unlock(struct irq_data *data) { - struct stmpe *stmpe = get_irq_chip_data(irq); + struct stmpe *stmpe = irq_data_get_irq_chip_data(data); struct stmpe_variant_info *variant = stmpe->variant; int num = DIV_ROUND_UP(variant->num_irqs, 8); int i; @@ -727,20 +727,20 @@ static void stmpe_irq_sync_unlock(unsigned int irq) mutex_unlock(&stmpe->irq_lock); } -static void stmpe_irq_mask(unsigned int irq) +static void stmpe_irq_mask(struct irq_data *data) { - struct stmpe *stmpe = get_irq_chip_data(irq); - int offset = irq - stmpe->irq_base; + struct stmpe *stmpe = irq_data_get_irq_chip_data(data); + int offset = data->irq - stmpe->irq_base; int regoffset = offset / 8; int mask = 1 << (offset % 8); stmpe->ier[regoffset] &= ~mask; } -static void stmpe_irq_unmask(unsigned int irq) +static void stmpe_irq_unmask(struct irq_data *data) { - struct stmpe *stmpe = get_irq_chip_data(irq); - int offset = irq - stmpe->irq_base; + struct stmpe *stmpe = irq_data_get_irq_chip_data(data); + int offset = data->irq - stmpe->irq_base; int regoffset = offset / 8; int mask = 1 << (offset % 8); @@ -749,10 +749,10 @@ static void stmpe_irq_unmask(unsigned int irq) static struct irq_chip stmpe_irq_chip = { .name = "stmpe", - .bus_lock = stmpe_irq_lock, - .bus_sync_unlock = stmpe_irq_sync_unlock, - .mask = stmpe_irq_mask, - .unmask = stmpe_irq_unmask, + .irq_bus_lock = stmpe_irq_lock, + .irq_bus_sync_unlock = stmpe_irq_sync_unlock, + .irq_mask = stmpe_irq_mask, + .irq_unmask = stmpe_irq_unmask, }; static int __devinit stmpe_irq_init(struct stmpe *stmpe) @@ -762,14 +762,14 @@ static int __devinit stmpe_irq_init(struct stmpe *stmpe) int irq; for (irq = base; irq < base + num_irqs; irq++) { - set_irq_chip_data(irq, stmpe); - set_irq_chip_and_handler(irq, &stmpe_irq_chip, + irq_set_chip_data(irq, stmpe); + irq_set_chip_and_handler(irq, &stmpe_irq_chip, handle_edge_irq); - set_irq_nested_thread(irq, 1); + irq_set_nested_thread(irq, 1); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID); #else - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -786,8 +786,8 @@ static void stmpe_irq_remove(struct stmpe *stmpe) #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); } } diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c index 006c121..42830e6 100644 --- a/drivers/mfd/t7l66xb.c +++ b/drivers/mfd/t7l66xb.c @@ -170,7 +170,7 @@ static struct mfd_cell t7l66xb_cells[] = { .name = "tmio-mmc", .enable = t7l66xb_mmc_enable, .disable = t7l66xb_mmc_disable, - .driver_data = &t7166xb_mmc_data, + .mfd_data = &t7166xb_mmc_data, .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), .resources = t7l66xb_mmc_resources, }, @@ -186,7 +186,7 @@ static struct mfd_cell t7l66xb_cells[] = { /* Handle the T7L66XB interrupt mux */ static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc) { - struct t7l66xb *t7l66xb = get_irq_data(irq); + struct t7l66xb *t7l66xb = irq_get_handler_data(irq); unsigned int isr; unsigned int i, irq_base; @@ -199,37 +199,37 @@ static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc) generic_handle_irq(irq_base + i); } -static void t7l66xb_irq_mask(unsigned int irq) +static void t7l66xb_irq_mask(struct irq_data *data) { - struct t7l66xb *t7l66xb = get_irq_chip_data(irq); + struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data); unsigned long flags; u8 imr; spin_lock_irqsave(&t7l66xb->lock, flags); imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); - imr |= 1 << (irq - t7l66xb->irq_base); + imr |= 1 << (data->irq - t7l66xb->irq_base); tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); spin_unlock_irqrestore(&t7l66xb->lock, flags); } -static void t7l66xb_irq_unmask(unsigned int irq) +static void t7l66xb_irq_unmask(struct irq_data *data) { - struct t7l66xb *t7l66xb = get_irq_chip_data(irq); + struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data); unsigned long flags; u8 imr; spin_lock_irqsave(&t7l66xb->lock, flags); imr = tmio_ioread8(t7l66xb->scr + SCR_IMR); - imr &= ~(1 << (irq - t7l66xb->irq_base)); + imr &= ~(1 << (data->irq - t7l66xb->irq_base)); tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR); spin_unlock_irqrestore(&t7l66xb->lock, flags); } static struct irq_chip t7l66xb_chip = { - .name = "t7l66xb", - .ack = t7l66xb_irq_mask, - .mask = t7l66xb_irq_mask, - .unmask = t7l66xb_irq_unmask, + .name = "t7l66xb", + .irq_ack = t7l66xb_irq_mask, + .irq_mask = t7l66xb_irq_mask, + .irq_unmask = t7l66xb_irq_unmask, }; /*--------------------------------------------------------------------------*/ @@ -243,17 +243,16 @@ static void t7l66xb_attach_irq(struct platform_device *dev) irq_base = t7l66xb->irq_base; for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { - set_irq_chip(irq, &t7l66xb_chip); - set_irq_chip_data(irq, t7l66xb); - set_irq_handler(irq, handle_level_irq); + irq_set_chip_and_handler(irq, &t7l66xb_chip, handle_level_irq); + irq_set_chip_data(irq, t7l66xb); #ifdef CONFIG_ARM set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); #endif } - set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING); - set_irq_data(t7l66xb->irq, t7l66xb); - set_irq_chained_handler(t7l66xb->irq, t7l66xb_irq); + irq_set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING); + irq_set_handler_data(t7l66xb->irq, t7l66xb); + irq_set_chained_handler(t7l66xb->irq, t7l66xb_irq); } static void t7l66xb_detach_irq(struct platform_device *dev) @@ -263,15 +262,15 @@ static void t7l66xb_detach_irq(struct platform_device *dev) irq_base = t7l66xb->irq_base; - set_irq_chained_handler(t7l66xb->irq, NULL); - set_irq_data(t7l66xb->irq, NULL); + irq_set_chained_handler(t7l66xb->irq, NULL); + irq_set_handler_data(t7l66xb->irq, NULL); for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) { #ifdef CONFIG_ARM set_irq_flags(irq, 0); #endif - set_irq_chip(irq, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip(irq, NULL); + irq_set_chip_data(irq, NULL); } } @@ -383,16 +382,7 @@ static int t7l66xb_probe(struct platform_device *dev) t7l66xb_attach_irq(dev); - t7l66xb_cells[T7L66XB_CELL_NAND].driver_data = pdata->nand_data; - t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = - &t7l66xb_cells[T7L66XB_CELL_NAND]; - t7l66xb_cells[T7L66XB_CELL_NAND].data_size = - sizeof(t7l66xb_cells[T7L66XB_CELL_NAND]); - - t7l66xb_cells[T7L66XB_CELL_MMC].platform_data = - &t7l66xb_cells[T7L66XB_CELL_MMC]; - t7l66xb_cells[T7L66XB_CELL_MMC].data_size = - sizeof(t7l66xb_cells[T7L66XB_CELL_MMC]); + t7l66xb_cells[T7L66XB_CELL_NAND].mfd_data = pdata->nand_data; ret = mfd_add_devices(&dev->dev, dev->id, t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells), diff --git a/drivers/mfd/tc35892.c b/drivers/mfd/tc35892.c deleted file mode 100644 index e619e2a..0000000 --- a/drivers/mfd/tc35892.c +++ /dev/null @@ -1,345 +0,0 @@ -/* - * Copyright (C) ST-Ericsson SA 2010 - * - * License Terms: GNU General Public License, version 2 - * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson - * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson - */ - -#include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/mfd/core.h> -#include <linux/mfd/tc35892.h> - -/** - * tc35892_reg_read() - read a single TC35892 register - * @tc35892: Device to read from - * @reg: Register to read - */ -int tc35892_reg_read(struct tc35892 *tc35892, u8 reg) -{ - int ret; - - ret = i2c_smbus_read_byte_data(tc35892->i2c, reg); - if (ret < 0) - dev_err(tc35892->dev, "failed to read reg %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_reg_read); - -/** - * tc35892_reg_read() - write a single TC35892 register - * @tc35892: Device to write to - * @reg: Register to read - * @data: Value to write - */ -int tc35892_reg_write(struct tc35892 *tc35892, u8 reg, u8 data) -{ - int ret; - - ret = i2c_smbus_write_byte_data(tc35892->i2c, reg, data); - if (ret < 0) - dev_err(tc35892->dev, "failed to write reg %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_reg_write); - -/** - * tc35892_block_read() - read multiple TC35892 registers - * @tc35892: Device to read from - * @reg: First register - * @length: Number of registers - * @values: Buffer to write to - */ -int tc35892_block_read(struct tc35892 *tc35892, u8 reg, u8 length, u8 *values) -{ - int ret; - - ret = i2c_smbus_read_i2c_block_data(tc35892->i2c, reg, length, values); - if (ret < 0) - dev_err(tc35892->dev, "failed to read regs %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_block_read); - -/** - * tc35892_block_write() - write multiple TC35892 registers - * @tc35892: Device to write to - * @reg: First register - * @length: Number of registers - * @values: Values to write - */ -int tc35892_block_write(struct tc35892 *tc35892, u8 reg, u8 length, - const u8 *values) -{ - int ret; - - ret = i2c_smbus_write_i2c_block_data(tc35892->i2c, reg, length, - values); - if (ret < 0) - dev_err(tc35892->dev, "failed to write regs %#x: %d\n", - reg, ret); - - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_block_write); - -/** - * tc35892_set_bits() - set the value of a bitfield in a TC35892 register - * @tc35892: Device to write to - * @reg: Register to write - * @mask: Mask of bits to set - * @values: Value to set - */ -int tc35892_set_bits(struct tc35892 *tc35892, u8 reg, u8 mask, u8 val) -{ - int ret; - - mutex_lock(&tc35892->lock); - - ret = tc35892_reg_read(tc35892, reg); - if (ret < 0) - goto out; - - ret &= ~mask; - ret |= val; - - ret = tc35892_reg_write(tc35892, reg, ret); - -out: - mutex_unlock(&tc35892->lock); - return ret; -} -EXPORT_SYMBOL_GPL(tc35892_set_bits); - -static struct resource gpio_resources[] = { - { - .start = TC35892_INT_GPIIRQ, - .end = TC35892_INT_GPIIRQ, - .flags = IORESOURCE_IRQ, - }, -}; - -static struct mfd_cell tc35892_devs[] = { - { - .name = "tc35892-gpio", - .num_resources = ARRAY_SIZE(gpio_resources), - .resources = &gpio_resources[0], - }, -}; - -static irqreturn_t tc35892_irq(int irq, void *data) -{ - struct tc35892 *tc35892 = data; - int status; - - status = tc35892_reg_read(tc35892, TC35892_IRQST); - if (status < 0) - return IRQ_NONE; - - while (status) { - int bit = __ffs(status); - - handle_nested_irq(tc35892->irq_base + bit); - status &= ~(1 << bit); - } - - /* - * A dummy read or write (to any register) appears to be necessary to - * have the last interrupt clear (for example, GPIO IC write) take - * effect. - */ - tc35892_reg_read(tc35892, TC35892_IRQST); - - return IRQ_HANDLED; -} - -static void tc35892_irq_dummy(unsigned int irq) -{ - /* No mask/unmask at this level */ -} - -static struct irq_chip tc35892_irq_chip = { - .name = "tc35892", - .mask = tc35892_irq_dummy, - .unmask = tc35892_irq_dummy, -}; - -static int tc35892_irq_init(struct tc35892 *tc35892) -{ - int base = tc35892->irq_base; - int irq; - - for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { - set_irq_chip_data(irq, tc35892); - set_irq_chip_and_handler(irq, &tc35892_irq_chip, - handle_edge_irq); - set_irq_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - set_irq_noprobe(irq); -#endif - } - - return 0; -} - -static void tc35892_irq_remove(struct tc35892 *tc35892) -{ - int base = tc35892->irq_base; - int irq; - - for (irq = base; irq < base + TC35892_NR_INTERNAL_IRQS; irq++) { -#ifdef CONFIG_ARM - set_irq_flags(irq, 0); -#endif - set_irq_chip_and_handler(irq, NULL, NULL); - set_irq_chip_data(irq, NULL); - } -} - -static int tc35892_chip_init(struct tc35892 *tc35892) -{ - int manf, ver, ret; - - manf = tc35892_reg_read(tc35892, TC35892_MANFCODE); - if (manf < 0) - return manf; - - ver = tc35892_reg_read(tc35892, TC35892_VERSION); - if (ver < 0) - return ver; - - if (manf != TC35892_MANFCODE_MAGIC) { - dev_err(tc35892->dev, "unknown manufacturer: %#x\n", manf); - return -EINVAL; - } - - dev_info(tc35892->dev, "manufacturer: %#x, version: %#x\n", manf, ver); - - /* Put everything except the IRQ module into reset */ - ret = tc35892_reg_write(tc35892, TC35892_RSTCTRL, - TC35892_RSTCTRL_TIMRST - | TC35892_RSTCTRL_ROTRST - | TC35892_RSTCTRL_KBDRST - | TC35892_RSTCTRL_GPIRST); - if (ret < 0) - return ret; - - /* Clear the reset interrupt. */ - return tc35892_reg_write(tc35892, TC35892_RSTINTCLR, 0x1); -} - -static int __devinit tc35892_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) -{ - struct tc35892_platform_data *pdata = i2c->dev.platform_data; - struct tc35892 *tc35892; - int ret; - - if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA - | I2C_FUNC_SMBUS_I2C_BLOCK)) - return -EIO; - - tc35892 = kzalloc(sizeof(struct tc35892), GFP_KERNEL); - if (!tc35892) - return -ENOMEM; - - mutex_init(&tc35892->lock); - - tc35892->dev = &i2c->dev; - tc35892->i2c = i2c; - tc35892->pdata = pdata; - tc35892->irq_base = pdata->irq_base; - tc35892->num_gpio = id->driver_data; - - i2c_set_clientdata(i2c, tc35892); - - ret = tc35892_chip_init(tc35892); - if (ret) - goto out_free; - - ret = tc35892_irq_init(tc35892); - if (ret) - goto out_free; - - ret = request_threaded_irq(tc35892->i2c->irq, NULL, tc35892_irq, - IRQF_TRIGGER_FALLING | IRQF_ONESHOT, - "tc35892", tc35892); - if (ret) { - dev_err(tc35892->dev, "failed to request IRQ: %d\n", ret); - goto out_removeirq; - } - - ret = mfd_add_devices(tc35892->dev, -1, tc35892_devs, - ARRAY_SIZE(tc35892_devs), NULL, - tc35892->irq_base); - if (ret) { - dev_err(tc35892->dev, "failed to add children\n"); - goto out_freeirq; - } - - return 0; - -out_freeirq: - free_irq(tc35892->i2c->irq, tc35892); -out_removeirq: - tc35892_irq_remove(tc35892); -out_free: - kfree(tc35892); - return ret; -} - -static int __devexit tc35892_remove(struct i2c_client *client) -{ - struct tc35892 *tc35892 = i2c_get_clientdata(client); - - mfd_remove_devices(tc35892->dev); - - free_irq(tc35892->i2c->irq, tc35892); - tc35892_irq_remove(tc35892); - - kfree(tc35892); - - return 0; -} - -static const struct i2c_device_id tc35892_id[] = { - { "tc35892", 24 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, tc35892_id); - -static struct i2c_driver tc35892_driver = { - .driver.name = "tc35892", - .driver.owner = THIS_MODULE, - .probe = tc35892_probe, - .remove = __devexit_p(tc35892_remove), - .id_table = tc35892_id, -}; - -static int __init tc35892_init(void) -{ - return i2c_add_driver(&tc35892_driver); -} -subsys_initcall(tc35892_init); - -static void __exit tc35892_exit(void) -{ - i2c_del_driver(&tc35892_driver); -} -module_exit(tc35892_exit); - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TC35892 MFD core driver"); -MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/tc3589x.c b/drivers/mfd/tc3589x.c new file mode 100644 index 0000000..c27e515 --- /dev/null +++ b/drivers/mfd/tc3589x.c @@ -0,0 +1,422 @@ +/* + * Copyright (C) ST-Ericsson SA 2010 + * + * License Terms: GNU General Public License, version 2 + * Author: Hanumath Prasad <hanumath.prasad@stericsson.com> for ST-Ericsson + * Author: Rabin Vincent <rabin.vincent@stericsson.com> for ST-Ericsson + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tc3589x.h> + +#define TC3589x_CLKMODE_MODCTL_SLEEP 0x0 +#define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0) + +/** + * tc3589x_reg_read() - read a single TC3589x register + * @tc3589x: Device to read from + * @reg: Register to read + */ +int tc3589x_reg_read(struct tc3589x *tc3589x, u8 reg) +{ + int ret; + + ret = i2c_smbus_read_byte_data(tc3589x->i2c, reg); + if (ret < 0) + dev_err(tc3589x->dev, "failed to read reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc3589x_reg_read); + +/** + * tc3589x_reg_read() - write a single TC3589x register + * @tc3589x: Device to write to + * @reg: Register to read + * @data: Value to write + */ +int tc3589x_reg_write(struct tc3589x *tc3589x, u8 reg, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(tc3589x->i2c, reg, data); + if (ret < 0) + dev_err(tc3589x->dev, "failed to write reg %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc3589x_reg_write); + +/** + * tc3589x_block_read() - read multiple TC3589x registers + * @tc3589x: Device to read from + * @reg: First register + * @length: Number of registers + * @values: Buffer to write to + */ +int tc3589x_block_read(struct tc3589x *tc3589x, u8 reg, u8 length, u8 *values) +{ + int ret; + + ret = i2c_smbus_read_i2c_block_data(tc3589x->i2c, reg, length, values); + if (ret < 0) + dev_err(tc3589x->dev, "failed to read regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc3589x_block_read); + +/** + * tc3589x_block_write() - write multiple TC3589x registers + * @tc3589x: Device to write to + * @reg: First register + * @length: Number of registers + * @values: Values to write + */ +int tc3589x_block_write(struct tc3589x *tc3589x, u8 reg, u8 length, + const u8 *values) +{ + int ret; + + ret = i2c_smbus_write_i2c_block_data(tc3589x->i2c, reg, length, + values); + if (ret < 0) + dev_err(tc3589x->dev, "failed to write regs %#x: %d\n", + reg, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(tc3589x_block_write); + +/** + * tc3589x_set_bits() - set the value of a bitfield in a TC3589x register + * @tc3589x: Device to write to + * @reg: Register to write + * @mask: Mask of bits to set + * @values: Value to set + */ +int tc3589x_set_bits(struct tc3589x *tc3589x, u8 reg, u8 mask, u8 val) +{ + int ret; + + mutex_lock(&tc3589x->lock); + + ret = tc3589x_reg_read(tc3589x, reg); + if (ret < 0) + goto out; + + ret &= ~mask; + ret |= val; + + ret = tc3589x_reg_write(tc3589x, reg, ret); + +out: + mutex_unlock(&tc3589x->lock); + return ret; +} +EXPORT_SYMBOL_GPL(tc3589x_set_bits); + +static struct resource gpio_resources[] = { + { + .start = TC3589x_INT_GPIIRQ, + .end = TC3589x_INT_GPIIRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource keypad_resources[] = { + { + .start = TC3589x_INT_KBDIRQ, + .end = TC3589x_INT_KBDIRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct mfd_cell tc3589x_dev_gpio[] = { + { + .name = "tc3589x-gpio", + .num_resources = ARRAY_SIZE(gpio_resources), + .resources = &gpio_resources[0], + }, +}; + +static struct mfd_cell tc3589x_dev_keypad[] = { + { + .name = "tc3589x-keypad", + .num_resources = ARRAY_SIZE(keypad_resources), + .resources = &keypad_resources[0], + }, +}; + +static irqreturn_t tc3589x_irq(int irq, void *data) +{ + struct tc3589x *tc3589x = data; + int status; + +again: + status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); + if (status < 0) + return IRQ_NONE; + + while (status) { + int bit = __ffs(status); + + handle_nested_irq(tc3589x->irq_base + bit); + status &= ~(1 << bit); + } + + /* + * A dummy read or write (to any register) appears to be necessary to + * have the last interrupt clear (for example, GPIO IC write) take + * effect. In such a case, recheck for any interrupt which is still + * pending. + */ + status = tc3589x_reg_read(tc3589x, TC3589x_IRQST); + if (status) + goto again; + + return IRQ_HANDLED; +} + +static int tc3589x_irq_init(struct tc3589x *tc3589x) +{ + int base = tc3589x->irq_base; + int irq; + + for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { + irq_set_chip_data(irq, tc3589x); + irq_set_chip_and_handler(irq, &dummy_irq_chip, + handle_edge_irq); + irq_set_nested_thread(irq, 1); +#ifdef CONFIG_ARM + set_irq_flags(irq, IRQF_VALID); +#else + irq_set_noprobe(irq); +#endif + } + + return 0; +} + +static void tc3589x_irq_remove(struct tc3589x *tc3589x) +{ + int base = tc3589x->irq_base; + int irq; + + for (irq = base; irq < base + TC3589x_NR_INTERNAL_IRQS; irq++) { +#ifdef CONFIG_ARM + set_irq_flags(irq, 0); +#endif + irq_set_chip_and_handler(irq, NULL, NULL); + irq_set_chip_data(irq, NULL); + } +} + +static int tc3589x_chip_init(struct tc3589x *tc3589x) +{ + int manf, ver, ret; + + manf = tc3589x_reg_read(tc3589x, TC3589x_MANFCODE); + if (manf < 0) + return manf; + + ver = tc3589x_reg_read(tc3589x, TC3589x_VERSION); + if (ver < 0) + return ver; + + if (manf != TC3589x_MANFCODE_MAGIC) { + dev_err(tc3589x->dev, "unknown manufacturer: %#x\n", manf); + return -EINVAL; + } + + dev_info(tc3589x->dev, "manufacturer: %#x, version: %#x\n", manf, ver); + + /* + * Put everything except the IRQ module into reset; + * also spare the GPIO module for any pin initialization + * done during pre-kernel boot + */ + ret = tc3589x_reg_write(tc3589x, TC3589x_RSTCTRL, + TC3589x_RSTCTRL_TIMRST + | TC3589x_RSTCTRL_ROTRST + | TC3589x_RSTCTRL_KBDRST); + if (ret < 0) + return ret; + + /* Clear the reset interrupt. */ + return tc3589x_reg_write(tc3589x, TC3589x_RSTINTCLR, 0x1); +} + +static int __devinit tc3589x_device_init(struct tc3589x *tc3589x) +{ + int ret = 0; + unsigned int blocks = tc3589x->pdata->block; + + if (blocks & TC3589x_BLOCK_GPIO) { + ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_gpio, + ARRAY_SIZE(tc3589x_dev_gpio), NULL, + tc3589x->irq_base); + if (ret) { + dev_err(tc3589x->dev, "failed to add gpio child\n"); + return ret; + } + dev_info(tc3589x->dev, "added gpio block\n"); + } + + if (blocks & TC3589x_BLOCK_KEYPAD) { + ret = mfd_add_devices(tc3589x->dev, -1, tc3589x_dev_keypad, + ARRAY_SIZE(tc3589x_dev_keypad), NULL, + tc3589x->irq_base); + if (ret) { + dev_err(tc3589x->dev, "failed to keypad child\n"); + return ret; + } + dev_info(tc3589x->dev, "added keypad block\n"); + } + + return ret; +} + +static int __devinit tc3589x_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct tc3589x_platform_data *pdata = i2c->dev.platform_data; + struct tc3589x *tc3589x; + int ret; + + if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_I2C_BLOCK)) + return -EIO; + + tc3589x = kzalloc(sizeof(struct tc3589x), GFP_KERNEL); + if (!tc3589x) + return -ENOMEM; + + mutex_init(&tc3589x->lock); + + tc3589x->dev = &i2c->dev; + tc3589x->i2c = i2c; + tc3589x->pdata = pdata; + tc3589x->irq_base = pdata->irq_base; + tc3589x->num_gpio = id->driver_data; + + i2c_set_clientdata(i2c, tc3589x); + + ret = tc3589x_chip_init(tc3589x); + if (ret) + goto out_free; + + ret = tc3589x_irq_init(tc3589x); + if (ret) + goto out_free; + + ret = request_threaded_irq(tc3589x->i2c->irq, NULL, tc3589x_irq, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tc3589x", tc3589x); + if (ret) { + dev_err(tc3589x->dev, "failed to request IRQ: %d\n", ret); + goto out_removeirq; + } + + ret = tc3589x_device_init(tc3589x); + if (ret) { + dev_err(tc3589x->dev, "failed to add child devices\n"); + goto out_freeirq; + } + + return 0; + +out_freeirq: + free_irq(tc3589x->i2c->irq, tc3589x); +out_removeirq: + tc3589x_irq_remove(tc3589x); +out_free: + kfree(tc3589x); + return ret; +} + +static int __devexit tc3589x_remove(struct i2c_client *client) +{ + struct tc3589x *tc3589x = i2c_get_clientdata(client); + + mfd_remove_devices(tc3589x->dev); + + free_irq(tc3589x->i2c->irq, tc3589x); + tc3589x_irq_remove(tc3589x); + + kfree(tc3589x); + + return 0; +} + +static int tc3589x_suspend(struct device *dev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(dev); + struct i2c_client *client = tc3589x->i2c; + int ret = 0; + + /* put the system to sleep mode */ + if (!device_may_wakeup(&client->dev)) + ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, + TC3589x_CLKMODE_MODCTL_SLEEP); + + return ret; +} + +static int tc3589x_resume(struct device *dev) +{ + struct tc3589x *tc3589x = dev_get_drvdata(dev); + struct i2c_client *client = tc3589x->i2c; + int ret = 0; + + /* enable the system into operation */ + if (!device_may_wakeup(&client->dev)) + ret = tc3589x_reg_write(tc3589x, TC3589x_CLKMODE, + TC3589x_CLKMODE_MODCTL_OPERATION); + + return ret; +} + +static const SIMPLE_DEV_PM_OPS(tc3589x_dev_pm_ops, tc3589x_suspend, + tc3589x_resume); + +static const struct i2c_device_id tc3589x_id[] = { + { "tc3589x", 24 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tc3589x_id); + +static struct i2c_driver tc3589x_driver = { + .driver.name = "tc3589x", + .driver.owner = THIS_MODULE, +#ifdef CONFIG_PM + .driver.pm = &tc3589x_dev_pm_ops, +#endif + .probe = tc3589x_probe, + .remove = __devexit_p(tc3589x_remove), + .id_table = tc3589x_id, +}; + +static int __init tc3589x_init(void) +{ + return i2c_add_driver(&tc3589x_driver); +} +subsys_initcall(tc3589x_init); + +static void __exit tc3589x_exit(void) +{ + i2c_del_driver(&tc3589x_driver); +} +module_exit(tc3589x_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TC3589x MFD core driver"); +MODULE_AUTHOR("Hanumath Prasad, Rabin Vincent"); diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c index 6315f63..b006f7c 100644 --- a/drivers/mfd/tc6387xb.c +++ b/drivers/mfd/tc6387xb.c @@ -131,7 +131,7 @@ static struct mfd_cell tc6387xb_cells[] = { .name = "tmio-mmc", .enable = tc6387xb_mmc_enable, .disable = tc6387xb_mmc_disable, - .driver_data = &tc6387xb_mmc_data, + .mfd_data = &tc6387xb_mmc_data, .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources), .resources = tc6387xb_mmc_resources, }, @@ -190,11 +190,6 @@ static int __devinit tc6387xb_probe(struct platform_device *dev) printk(KERN_INFO "Toshiba tc6387xb initialised\n"); - tc6387xb_cells[TC6387XB_CELL_MMC].platform_data = - &tc6387xb_cells[TC6387XB_CELL_MMC]; - tc6387xb_cells[TC6387XB_CELL_MMC].data_size = - sizeof(tc6387xb_cells[TC6387XB_CELL_MMC]); - ret = mfd_add_devices(&dev->dev, dev->id, tc6387xb_cells, ARRAY_SIZE(tc6387xb_cells), iomem, irq); diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c index 1ea80d8a..fc53ce2 100644 --- a/drivers/mfd/tc6393xb.c +++ b/drivers/mfd/tc6393xb.c @@ -393,7 +393,7 @@ static struct mfd_cell __devinitdata tc6393xb_cells[] = { .name = "tmio-mmc", .enable = tc6393xb_mmc_enable, .resume = tc6393xb_mmc_resume, - .driver_data = &tc6393xb_mmc_data, + .mfd_data = &tc6393xb_mmc_data, .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), .resources = tc6393xb_mmc_resources, }, @@ -513,7 +513,7 @@ static int tc6393xb_register_gpio(struct tc6393xb *tc6393xb, int gpio_base) static void tc6393xb_irq(unsigned int irq, struct irq_desc *desc) { - struct tc6393xb *tc6393xb = get_irq_data(irq); + struct tc6393xb *tc6393xb = irq_get_handler_data(irq); unsigned int isr; unsigned int i, irq_base; @@ -527,41 +527,41 @@ tc6393xb_irq(unsigned int irq, struct irq_desc *desc) } } -static void tc6393xb_irq_ack(unsigned int irq) +static void tc6393xb_irq_ack(struct irq_data *data) { } -static void tc6393xb_irq_mask(unsigned int irq) +static void tc6393xb_irq_mask(struct irq_data *data) { - struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + struct tc6393xb *tc6393xb = irq_data_get_irq_chip_data(data); unsigned long flags; u8 imr; spin_lock_irqsave(&tc6393xb->lock, flags); imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); - imr |= 1 << (irq - tc6393xb->irq_base); + imr |= 1 << (data->irq - tc6393xb->irq_base); tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); spin_unlock_irqrestore(&tc6393xb->lock, flags); } -static void tc6393xb_irq_unmask(unsigned int irq) +static void tc6393xb_irq_unmask(struct irq_data *data) { - struct tc6393xb *tc6393xb = get_irq_chip_data(irq); + struct tc6393xb *tc6393xb = irq_data_get_irq_chip_data(data); unsigned long flags; u8 imr; spin_lock_irqsave(&tc6393xb->lock, flags); imr = tmio_ioread8(tc6393xb->scr + SCR_IMR); - imr &= ~(1 << (irq - tc6393xb->irq_base)); + imr &= ~(1 << (data->irq - tc6393xb->irq_base)); tmio_iowrite8(imr, tc6393xb->scr + SCR_IMR); spin_unlock_irqrestore(&tc6393xb->lock, flags); } static struct irq_chip tc6393xb_chip = { - .name = "tc6393xb", - .ack = tc6393xb_irq_ack, - .mask = tc6393xb_irq_mask, - .unmask = tc6393xb_irq_unmask, + .name = "tc6393xb", + .irq_ack = tc6393xb_irq_ack, + .irq_mask = tc6393xb_irq_mask, + .irq_unmask = tc6393xb_irq_unmask, }; static void tc6393xb_attach_irq(struct platform_device *dev) @@ -572,15 +572,14 @@ static void tc6393xb_attach_irq(struct platform_device *dev) irq_base = tc6393xb->irq_base; for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { - set_irq_chip(irq, &tc6393xb_chip); - set_irq_chip_data(irq, tc6393xb); - set_irq_handler(irq, handle_edge_irq); + irq_set_chip_and_handler(irq, &tc6393xb_chip, handle_edge_irq); + irq_set_chip_data(irq, tc6393xb); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); } - set_irq_type(tc6393xb->irq, IRQ_TYPE_EDGE_FALLING); - set_irq_data(tc6393xb->irq, tc6393xb); - set_irq_chained_handler(tc6393xb->irq, tc6393xb_irq); + irq_set_irq_type(tc6393xb->irq, IRQ_TYPE_EDGE_FALLING); + irq_set_handler_data(tc6393xb->irq, tc6393xb); + irq_set_chained_handler(tc6393xb->irq, tc6393xb_irq); } static void tc6393xb_detach_irq(struct platform_device *dev) @@ -588,15 +587,15 @@ static void tc6393xb_detach_irq(struct platform_device *dev) struct tc6393xb *tc6393xb = platform_get_drvdata(dev); unsigned int irq, irq_base; - set_irq_chained_handler(tc6393xb->irq, NULL); - set_irq_data(tc6393xb->irq, NULL); + irq_set_chained_handler(tc6393xb->irq, NULL); + irq_set_handler_data(tc6393xb->irq, NULL); irq_base = tc6393xb->irq_base; for (irq = irq_base; irq < irq_base + TC6393XB_NR_IRQS; irq++) { set_irq_flags(irq, 0); - set_irq_chip(irq, NULL); - set_irq_chip_data(irq, NULL); + irq_set_chip(irq, NULL); + irq_set_chip_data(irq, NULL); } } @@ -693,27 +692,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) goto err_setup; } - tc6393xb_cells[TC6393XB_CELL_NAND].driver_data = tcpd->nand_data; - tc6393xb_cells[TC6393XB_CELL_NAND].platform_data = - &tc6393xb_cells[TC6393XB_CELL_NAND]; - tc6393xb_cells[TC6393XB_CELL_NAND].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_NAND]); - - tc6393xb_cells[TC6393XB_CELL_MMC].platform_data = - &tc6393xb_cells[TC6393XB_CELL_MMC]; - tc6393xb_cells[TC6393XB_CELL_MMC].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_MMC]); - - tc6393xb_cells[TC6393XB_CELL_OHCI].platform_data = - &tc6393xb_cells[TC6393XB_CELL_OHCI]; - tc6393xb_cells[TC6393XB_CELL_OHCI].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_OHCI]); - - tc6393xb_cells[TC6393XB_CELL_FB].driver_data = tcpd->fb_data; - tc6393xb_cells[TC6393XB_CELL_FB].platform_data = - &tc6393xb_cells[TC6393XB_CELL_FB]; - tc6393xb_cells[TC6393XB_CELL_FB].data_size = - sizeof(tc6393xb_cells[TC6393XB_CELL_FB]); + tc6393xb_cells[TC6393XB_CELL_NAND].mfd_data = tcpd->nand_data; + tc6393xb_cells[TC6393XB_CELL_FB].mfd_data = tcpd->fb_data; ret = mfd_add_devices(&dev->dev, dev->id, tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c new file mode 100644 index 0000000..af9ab0e --- /dev/null +++ b/drivers/mfd/ti-ssp.c @@ -0,0 +1,476 @@ +/* + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs + * + * Copyright (C) 2010 Texas Instruments Inc + * + * 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 + */ + +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/spinlock.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/mfd/core.h> +#include <linux/mfd/ti_ssp.h> + +/* Register Offsets */ +#define REG_REV 0x00 +#define REG_IOSEL_1 0x04 +#define REG_IOSEL_2 0x08 +#define REG_PREDIV 0x0c +#define REG_INTR_ST 0x10 +#define REG_INTR_EN 0x14 +#define REG_TEST_CTRL 0x18 + +/* Per port registers */ +#define PORT_CFG_2 0x00 +#define PORT_ADDR 0x04 +#define PORT_DATA 0x08 +#define PORT_CFG_1 0x0c +#define PORT_STATE 0x10 + +#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT) +#define SSP_PORT_CLKRATE_MASK 0x0f + +#define SSP_SEQRAM_WR_EN BIT(4) +#define SSP_SEQRAM_RD_EN BIT(5) +#define SSP_START BIT(15) +#define SSP_BUSY BIT(10) +#define SSP_PORT_ASL BIT(7) +#define SSP_PORT_CFO1 BIT(6) + +#define SSP_PORT_SEQRAM_SIZE 32 + +static const int ssp_port_base[] = {0x040, 0x080}; +static const int ssp_port_seqram[] = {0x100, 0x180}; + +struct ti_ssp { + struct resource *res; + struct device *dev; + void __iomem *regs; + spinlock_t lock; + struct clk *clk; + int irq; + wait_queue_head_t wqh; + + /* + * Some of the iosel2 register bits always read-back as 0, we need to + * remember these values so that we don't clobber previously set + * values. + */ + u32 iosel2; +}; + +static inline struct ti_ssp *dev_to_ssp(struct device *dev) +{ + return dev_get_drvdata(dev->parent); +} + +static inline int dev_to_port(struct device *dev) +{ + return to_platform_device(dev)->id; +} + +/* Register Access Helpers, rmw() functions need to run locked */ +static inline u32 ssp_read(struct ti_ssp *ssp, int reg) +{ + return __raw_readl(ssp->regs + reg); +} + +static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) +{ + __raw_writel(val, ssp->regs + reg); +} + +static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) +{ + ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits); +} + +static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg) +{ + return ssp_read(ssp, ssp_port_base[port] + reg); +} + +static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg, + u32 val) +{ + ssp_write(ssp, ssp_port_base[port] + reg, val); +} + +static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg, + u32 mask, u32 bits) +{ + ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits); +} + +static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg, + u32 bits) +{ + ssp_port_rmw(ssp, port, reg, bits, 0); +} + +static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg, + u32 bits) +{ + ssp_port_rmw(ssp, port, reg, 0, bits); +} + +/* Called to setup port clock mode, caller must hold ssp->lock */ +static int __set_mode(struct ti_ssp *ssp, int port, int mode) +{ + mode &= SSP_PORT_CONFIG_MASK; + ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode); + + return 0; +} + +int ti_ssp_set_mode(struct device *dev, int mode) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + spin_lock(&ssp->lock); + ret = __set_mode(ssp, port, mode); + spin_unlock(&ssp->lock); + + return ret; +} +EXPORT_SYMBOL(ti_ssp_set_mode); + +/* Called to setup iosel2, caller must hold ssp->lock */ +static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val) +{ + ssp->iosel2 = (ssp->iosel2 & ~mask) | val; + ssp_write(ssp, REG_IOSEL_2, ssp->iosel2); +} + +/* Called to setup port iosel, caller must hold ssp->lock */ +static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) +{ + unsigned val, shift = port ? 16 : 0; + + /* IOSEL1 gets the least significant 16 bits */ + val = ssp_read(ssp, REG_IOSEL_1); + val &= 0xffff << (port ? 0 : 16); + val |= (iosel & 0xffff) << (port ? 16 : 0); + ssp_write(ssp, REG_IOSEL_1, val); + + /* IOSEL2 gets the most significant 16 bits */ + val = (iosel >> 16) & 0x7; + __set_iosel2(ssp, 0x7 << shift, val << shift); +} + +int ti_ssp_set_iosel(struct device *dev, u32 iosel) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + + spin_lock(&ssp->lock); + __set_iosel(ssp, port, iosel); + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_set_iosel); + +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int i; + + if (len > SSP_PORT_SEQRAM_SIZE) + return -ENOSPC; + + spin_lock(&ssp->lock); + + /* Enable SeqRAM access */ + ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); + + /* Copy code */ + for (i = 0; i < len; i++) { + __raw_writel(prog[i], ssp->regs + offs + 4*i + + ssp_port_seqram[port]); + } + + /* Disable SeqRAM access */ + ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); + + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_load); + +int ti_ssp_raw_read(struct device *dev) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int shift = port ? 27 : 11; + + return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf; +} +EXPORT_SYMBOL(ti_ssp_raw_read); + +int ti_ssp_raw_write(struct device *dev, u32 val) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev), shift; + + spin_lock(&ssp->lock); + + shift = port ? 22 : 6; + val &= 0xf; + __set_iosel2(ssp, 0xf << shift, val << shift); + + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_raw_write); + +static inline int __xfer_done(struct ti_ssp *ssp, int port) +{ + return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY); +} + +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + if (pc & ~(0x3f)) + return -EINVAL; + + /* Grab ssp->lock to serialize rmw on ssp registers */ + spin_lock(&ssp->lock); + + ssp_port_write(ssp, port, PORT_ADDR, input >> 16); + ssp_port_write(ssp, port, PORT_DATA, input & 0xffff); + ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc); + + /* grab wait queue head lock to avoid race with the isr */ + spin_lock_irq(&ssp->wqh.lock); + + /* kick off sequence execution in hardware */ + ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START); + + /* drop ssp lock; no register writes beyond this */ + spin_unlock(&ssp->lock); + + ret = wait_event_interruptible_locked_irq(ssp->wqh, + __xfer_done(ssp, port)); + spin_unlock_irq(&ssp->wqh.lock); + + if (ret < 0) + return ret; + + if (output) { + *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) | + (ssp_port_read(ssp, port, PORT_DATA) & 0xffff); + } + + ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */ + + return ret; +} +EXPORT_SYMBOL(ti_ssp_run); + +static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data) +{ + struct ti_ssp *ssp = dev_data; + + spin_lock(&ssp->wqh.lock); + + ssp_write(ssp, REG_INTR_ST, 0x3); + wake_up_locked(&ssp->wqh); + + spin_unlock(&ssp->wqh.lock); + + return IRQ_HANDLED; +} + +static int __devinit ti_ssp_probe(struct platform_device *pdev) +{ + static struct ti_ssp *ssp; + const struct ti_ssp_data *pdata = pdev->dev.platform_data; + int error = 0, prediv = 0xff, id; + unsigned long sysclk; + struct device *dev = &pdev->dev; + struct mfd_cell cells[2]; + + ssp = kzalloc(sizeof(*ssp), GFP_KERNEL); + if (!ssp) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + ssp->dev = dev; + dev_set_drvdata(dev, ssp); + + ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ssp->res) { + error = -ENODEV; + dev_err(dev, "cannot determine register area\n"); + goto error_res; + } + + if (!request_mem_region(ssp->res->start, resource_size(ssp->res), + pdev->name)) { + error = -ENOMEM; + dev_err(dev, "cannot claim register memory\n"); + goto error_res; + } + + ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res)); + if (!ssp->regs) { + error = -ENOMEM; + dev_err(dev, "cannot map register memory\n"); + goto error_map; + } + + ssp->clk = clk_get(dev, NULL); + if (IS_ERR(ssp->clk)) { + error = PTR_ERR(ssp->clk); + dev_err(dev, "cannot claim device clock\n"); + goto error_clk; + } + + ssp->irq = platform_get_irq(pdev, 0); + if (ssp->irq < 0) { + error = -ENODEV; + dev_err(dev, "unknown irq\n"); + goto error_irq; + } + + error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0, + dev_name(dev), ssp); + if (error < 0) { + dev_err(dev, "cannot acquire irq\n"); + goto error_irq; + } + + spin_lock_init(&ssp->lock); + init_waitqueue_head(&ssp->wqh); + + /* Power on and initialize SSP */ + error = clk_enable(ssp->clk); + if (error) { + dev_err(dev, "cannot enable device clock\n"); + goto error_enable; + } + + /* Reset registers to a sensible known state */ + ssp_write(ssp, REG_IOSEL_1, 0); + ssp_write(ssp, REG_IOSEL_2, 0); + ssp_write(ssp, REG_INTR_EN, 0x3); + ssp_write(ssp, REG_INTR_ST, 0x3); + ssp_write(ssp, REG_TEST_CTRL, 0); + ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL); + ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL); + ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1); + ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1); + + sysclk = clk_get_rate(ssp->clk); + if (pdata && pdata->out_clock) + prediv = (sysclk / pdata->out_clock) - 1; + prediv = clamp(prediv, 0, 0xff); + ssp_rmw(ssp, REG_PREDIV, 0xff, prediv); + + memset(cells, 0, sizeof(cells)); + for (id = 0; id < 2; id++) { + const struct ti_ssp_dev_data *data = &pdata->dev_data[id]; + + cells[id].id = id; + cells[id].name = data->dev_name; + cells[id].platform_data = data->pdata; + cells[id].data_size = data->pdata_size; + } + + error = mfd_add_devices(dev, 0, cells, 2, NULL, 0); + if (error < 0) { + dev_err(dev, "cannot add mfd cells\n"); + goto error_enable; + } + + return 0; + +error_enable: + free_irq(ssp->irq, ssp); +error_irq: + clk_put(ssp->clk); +error_clk: + iounmap(ssp->regs); +error_map: + release_mem_region(ssp->res->start, resource_size(ssp->res)); +error_res: + kfree(ssp); + return error; +} + +static int __devexit ti_ssp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_ssp *ssp = dev_get_drvdata(dev); + + mfd_remove_devices(dev); + clk_disable(ssp->clk); + free_irq(ssp->irq, ssp); + clk_put(ssp->clk); + iounmap(ssp->regs); + release_mem_region(ssp->res->start, resource_size(ssp->res)); + kfree(ssp); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct platform_driver ti_ssp_driver = { + .probe = ti_ssp_probe, + .remove = __devexit_p(ti_ssp_remove), + .driver = { + .name = "ti-ssp", + .owner = THIS_MODULE, + } +}; + +static int __init ti_ssp_init(void) +{ + return platform_driver_register(&ti_ssp_driver); +} +module_init(ti_ssp_init); + +static void __exit ti_ssp_exit(void) +{ + platform_driver_unregister(&ti_ssp_driver); +} +module_exit(ti_ssp_exit); + +MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp"); diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 727f62c..94c6c8a 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -40,6 +40,7 @@ #include <linux/spi/mc33880.h> #include <media/timb_radio.h> +#include <media/timb_video.h> #include <linux/timb_dma.h> @@ -246,7 +247,23 @@ static const __devinitconst struct resource timberdale_uartlite_resources[] = { }, }; -static const __devinitconst struct resource timberdale_radio_resources[] = { +static __devinitdata struct i2c_board_info timberdale_adv7180_i2c_board_info = { + /* Requires jumper JP9 to be off */ + I2C_BOARD_INFO("adv7180", 0x42 >> 1), + .irq = IRQ_TIMBERDALE_ADV7180 +}; + +static __devinitdata struct timb_video_platform_data + timberdale_video_platform_data = { + .dma_channel = DMA_VIDEO_RX, + .i2c_adapter = 0, + .encoder = { + .info = &timberdale_adv7180_i2c_board_info + } +}; + +static const __devinitconst struct resource +timberdale_radio_resources[] = { { .start = RDSOFFSET, .end = RDSEND, @@ -271,15 +288,25 @@ static __devinitdata struct timb_radio_platform_data timberdale_radio_platform_data = { .i2c_adapter = 0, .tuner = { - .module_name = "tef6862", .info = &timberdale_tef6868_i2c_board_info }, .dsp = { - .module_name = "saa7706h", .info = &timberdale_saa7706_i2c_board_info } }; +static const __devinitconst struct resource timberdale_video_resources[] = { + { + .start = LOGIWOFFSET, + .end = LOGIWEND, + .flags = IORESOURCE_MEM, + }, + /* + note that the "frame buffer" is located in DMA area + starting at 0x1200000 + */ +}; + static __devinitdata struct timb_dma_platform_data timb_dma_platform_data = { .nr_channels = 10, .channels = { @@ -357,8 +384,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -369,36 +395,37 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, - .data_size = sizeof(timberdale_xiic_platform_data), + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, + }, + { + .name = "timb-video", + .num_resources = ARRAY_SIZE(timberdale_video_resources), + .resources = timberdale_video_resources, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, - .data_size = sizeof(timberdale_ks8842_platform_data) + .mfd_data = &timberdale_ks8842_platform_data, }, }; @@ -407,8 +434,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -424,15 +450,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, - .data_size = sizeof(timberdale_xiic_platform_data), + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-mlogicore", @@ -440,25 +464,28 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .resources = timberdale_mlogicore_resources, }, { + .name = "timb-video", + .num_resources = ARRAY_SIZE(timberdale_video_resources), + .resources = timberdale_video_resources, + .mfd_data = &timberdale_video_platform_data, + }, + { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, - .data_size = sizeof(timberdale_ks8842_platform_data) + .mfd_data = &timberdale_ks8842_platform_data, }, }; @@ -467,8 +494,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -479,29 +505,31 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, - .data_size = sizeof(timberdale_xiic_platform_data), + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, + }, + { + .name = "timb-video", + .num_resources = ARRAY_SIZE(timberdale_video_resources), + .resources = timberdale_video_resources, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, }; @@ -510,8 +538,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, - .data_size = sizeof(timb_dma_platform_data), + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -522,36 +549,37 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "ocores-i2c", .num_resources = ARRAY_SIZE(timberdale_ocores_resources), .resources = timberdale_ocores_resources, - .platform_data = &timberdale_ocores_platform_data, - .data_size = sizeof(timberdale_ocores_platform_data), + .mfd_data = &timberdale_ocores_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, - .data_size = sizeof(timberdale_gpio_platform_data), + .mfd_data = &timberdale_gpio_platform_data, + }, + { + .name = "timb-video", + .num_resources = ARRAY_SIZE(timberdale_video_resources), + .resources = timberdale_video_resources, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, - .data_size = sizeof(timberdale_radio_platform_data), + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, - .data_size = sizeof(timberdale_xspi_platform_data), + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, - .data_size = sizeof(timberdale_ks8842_platform_data) + .mfd_data = &timberdale_ks8842_platform_data, }, }; diff --git a/drivers/mfd/timberdale.h b/drivers/mfd/timberdale.h index c11bf6e..4412acd 100644 --- a/drivers/mfd/timberdale.h +++ b/drivers/mfd/timberdale.h @@ -23,7 +23,7 @@ #ifndef MFD_TIMBERDALE_H #define MFD_TIMBERDALE_H -#define DRV_VERSION "0.2" +#define DRV_VERSION "0.3" /* This driver only support versions >= 3.8 and < 4.0 */ #define TIMB_SUPPORTED_MAJOR 3 diff --git a/drivers/mfd/tps6105x.c b/drivers/mfd/tps6105x.c new file mode 100644 index 0000000..46d8205 --- /dev/null +++ b/drivers/mfd/tps6105x.c @@ -0,0 +1,246 @@ +/* + * Core driver for TPS61050/61052 boost converters, used for while LED + * driving, audio power amplification, white LED flash, and generic + * boost conversion. Additionally it provides a 1-bit GPIO pin (out or in) + * and a flash synchronization pin to synchronize flash events when used as + * flashgun. + * + * Copyright (C) 2011 ST-Ericsson SA + * Written on behalf of Linaro for ST-Ericsson + * + * Author: Linus Walleij <linus.walleij@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/gpio.h> +#include <linux/spinlock.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/regulator/driver.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps6105x.h> + +int tps6105x_set(struct tps6105x *tps6105x, u8 reg, u8 value) +{ + int ret; + + ret = mutex_lock_interruptible(&tps6105x->lock); + if (ret) + return ret; + ret = i2c_smbus_write_byte_data(tps6105x->client, reg, value); + mutex_unlock(&tps6105x->lock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(tps6105x_set); + +int tps6105x_get(struct tps6105x *tps6105x, u8 reg, u8 *buf) +{ + int ret; + + ret = mutex_lock_interruptible(&tps6105x->lock); + if (ret) + return ret; + ret = i2c_smbus_read_byte_data(tps6105x->client, reg); + mutex_unlock(&tps6105x->lock); + if (ret < 0) + return ret; + + *buf = ret; + return 0; +} +EXPORT_SYMBOL(tps6105x_get); + +/* + * Masks off the bits in the mask and sets the bits in the bitvalues + * parameter in one atomic operation + */ +int tps6105x_mask_and_set(struct tps6105x *tps6105x, u8 reg, + u8 bitmask, u8 bitvalues) +{ + int ret; + u8 regval; + + ret = mutex_lock_interruptible(&tps6105x->lock); + if (ret) + return ret; + ret = i2c_smbus_read_byte_data(tps6105x->client, reg); + if (ret < 0) + goto fail; + regval = ret; + regval = (~bitmask & regval) | (bitmask & bitvalues); + ret = i2c_smbus_write_byte_data(tps6105x->client, reg, regval); +fail: + mutex_unlock(&tps6105x->lock); + if (ret < 0) + return ret; + + return 0; +} +EXPORT_SYMBOL(tps6105x_mask_and_set); + +static int __devinit tps6105x_startup(struct tps6105x *tps6105x) +{ + int ret; + u8 regval; + + ret = tps6105x_get(tps6105x, TPS6105X_REG_0, ®val); + if (ret) + return ret; + switch (regval >> TPS6105X_REG0_MODE_SHIFT) { + case TPS6105X_REG0_MODE_SHUTDOWN: + dev_info(&tps6105x->client->dev, + "TPS6105x found in SHUTDOWN mode\n"); + break; + case TPS6105X_REG0_MODE_TORCH: + dev_info(&tps6105x->client->dev, + "TPS6105x found in TORCH mode\n"); + break; + case TPS6105X_REG0_MODE_TORCH_FLASH: + dev_info(&tps6105x->client->dev, + "TPS6105x found in FLASH mode\n"); + break; + case TPS6105X_REG0_MODE_VOLTAGE: + dev_info(&tps6105x->client->dev, + "TPS6105x found in VOLTAGE mode\n"); + break; + default: + break; + } + + return ret; +} + +/* + * MFD cells - we have one cell which is selected operation + * mode, and we always have a GPIO cell. + */ +static struct mfd_cell tps6105x_cells[] = { + { + /* name will be runtime assigned */ + .id = -1, + }, + { + .name = "tps6105x-gpio", + .id = -1, + }, +}; + +static int __devinit tps6105x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps6105x *tps6105x; + struct tps6105x_platform_data *pdata; + int ret; + int i; + + tps6105x = kmalloc(sizeof(*tps6105x), GFP_KERNEL); + if (!tps6105x) + return -ENOMEM; + + i2c_set_clientdata(client, tps6105x); + tps6105x->client = client; + pdata = client->dev.platform_data; + tps6105x->pdata = pdata; + mutex_init(&tps6105x->lock); + + ret = tps6105x_startup(tps6105x); + if (ret) { + dev_err(&client->dev, "chip initialization failed\n"); + goto fail; + } + + /* Remove warning texts when you implement new cell drivers */ + switch (pdata->mode) { + case TPS6105X_MODE_SHUTDOWN: + dev_info(&client->dev, + "present, not used for anything, only GPIO\n"); + break; + case TPS6105X_MODE_TORCH: + tps6105x_cells[0].name = "tps6105x-leds"; + dev_warn(&client->dev, + "torch mode is unsupported\n"); + break; + case TPS6105X_MODE_TORCH_FLASH: + tps6105x_cells[0].name = "tps6105x-flash"; + dev_warn(&client->dev, + "flash mode is unsupported\n"); + break; + case TPS6105X_MODE_VOLTAGE: + tps6105x_cells[0].name ="tps6105x-regulator"; + break; + default: + break; + } + + /* Set up and register the platform devices. */ + for (i = 0; i < ARRAY_SIZE(tps6105x_cells); i++) { + /* One state holder for all drivers, this is simple */ + tps6105x_cells[i].mfd_data = tps6105x; + } + + ret = mfd_add_devices(&client->dev, 0, tps6105x_cells, + ARRAY_SIZE(tps6105x_cells), NULL, 0); + if (ret) + goto fail; + + return 0; + +fail: + kfree(tps6105x); + return ret; +} + +static int __devexit tps6105x_remove(struct i2c_client *client) +{ + struct tps6105x *tps6105x = i2c_get_clientdata(client); + + mfd_remove_devices(&client->dev); + + /* Put chip in shutdown mode */ + tps6105x_mask_and_set(tps6105x, TPS6105X_REG_0, + TPS6105X_REG0_MODE_MASK, + TPS6105X_MODE_SHUTDOWN << TPS6105X_REG0_MODE_SHIFT); + + kfree(tps6105x); + return 0; +} + +static const struct i2c_device_id tps6105x_id[] = { + { "tps61050", 0 }, + { "tps61052", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tps6105x_id); + +static struct i2c_driver tps6105x_driver = { + .driver = { + .name = "tps6105x", + }, + .probe = tps6105x_probe, + .remove = __devexit_p(tps6105x_remove), + .id_table = tps6105x_id, +}; + +static int __init tps6105x_init(void) +{ + return i2c_add_driver(&tps6105x_driver); +} +subsys_initcall(tps6105x_init); + +static void __exit tps6105x_exit(void) +{ + i2c_del_driver(&tps6105x_driver); +} +module_exit(tps6105x_exit); + +MODULE_AUTHOR("Linus Walleij"); +MODULE_DESCRIPTION("TPS6105x White LED Boost Converter Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65010.c b/drivers/mfd/tps65010.c index d0016b6..93d5fdf 100644 --- a/drivers/mfd/tps65010.c +++ b/drivers/mfd/tps65010.c @@ -34,7 +34,7 @@ #include <linux/i2c/tps65010.h> -#include <asm/gpio.h> +#include <linux/gpio.h> /*-------------------------------------------------------------------------*/ @@ -242,7 +242,7 @@ static int dbg_show(struct seq_file *s, void *_) seq_printf(s, "mask2 %s\n", buf); /* ignore ackint2 */ - (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY); + schedule_delayed_work(&tps->work, POWER_POLL_DELAY); /* VMAIN voltage, enable lowpower, etc */ @@ -400,7 +400,7 @@ static void tps65010_interrupt(struct tps65010 *tps) && (tps->chgstatus & (TPS_CHG_USB|TPS_CHG_AC))) poll = 1; if (poll) - (void) schedule_delayed_work(&tps->work, POWER_POLL_DELAY); + schedule_delayed_work(&tps->work, POWER_POLL_DELAY); /* also potentially gpio-in rise or fall */ } @@ -410,7 +410,7 @@ static void tps65010_work(struct work_struct *work) { struct tps65010 *tps; - tps = container_of(work, struct tps65010, work.work); + tps = container_of(to_delayed_work(work), struct tps65010, work); mutex_lock(&tps->lock); tps65010_interrupt(tps); @@ -448,7 +448,7 @@ static irqreturn_t tps65010_irq(int irq, void *_tps) disable_irq_nosync(irq); set_bit(FLAG_IRQ_ENABLE, &tps->flags); - (void) schedule_work(&tps->work.work); + schedule_delayed_work(&tps->work, 0); return IRQ_HANDLED; } @@ -527,8 +527,7 @@ static int __exit tps65010_remove(struct i2c_client *client) } if (client->irq > 0) free_irq(client->irq, tps); - cancel_delayed_work(&tps->work); - flush_scheduled_work(); + cancel_delayed_work_sync(&tps->work); debugfs_remove(tps->file); kfree(tps); the_tps = NULL; @@ -720,7 +719,7 @@ int tps65010_set_vbus_draw(unsigned mA) && test_and_set_bit( FLAG_VBUS_CHANGED, &the_tps->flags)) { /* gadget drivers call this in_irq() */ - (void) schedule_work(&the_tps->work.work); + schedule_delayed_work(&the_tps->work, 0); } local_irq_restore(flags); diff --git a/drivers/mfd/tps6586x.c b/drivers/mfd/tps6586x.c index b4931ab..b600808 100644 --- a/drivers/mfd/tps6586x.c +++ b/drivers/mfd/tps6586x.c @@ -46,8 +46,6 @@ /* device id */ #define TPS6586X_VERSIONCRC 0xcd -#define TPS658621A_VERSIONCRC 0x15 -#define TPS658621C_VERSIONCRC 0x2c struct tps6586x_irq_data { u8 mask_reg; @@ -152,12 +150,12 @@ static inline int __tps6586x_write(struct i2c_client *client, static inline int __tps6586x_writes(struct i2c_client *client, int reg, int len, uint8_t *val) { - int ret; + int ret, i; - ret = i2c_smbus_write_i2c_block_data(client, reg, len, val); - if (ret < 0) { - dev_err(&client->dev, "failed writings to 0x%02x\n", reg); - return ret; + for (i = 0; i < len; i++) { + ret = __tps6586x_write(client, reg + i, *(val + i)); + if (ret < 0) + return ret; } return 0; @@ -290,12 +288,10 @@ static int tps6586x_gpio_output(struct gpio_chip *gc, unsigned offset, return tps6586x_update(tps6586x->dev, TPS6586X_GPIOSET1, val, mask); } -static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) +static int tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) { - int ret; - if (!gpio_base) - return; + return 0; tps6586x->gpio.owner = THIS_MODULE; tps6586x->gpio.label = tps6586x->client->name; @@ -309,9 +305,7 @@ static void tps6586x_gpio_init(struct tps6586x *tps6586x, int gpio_base) tps6586x->gpio.set = tps6586x_gpio_set; tps6586x->gpio.get = tps6586x_gpio_get; - ret = gpiochip_add(&tps6586x->gpio); - if (ret) - dev_warn(tps6586x->dev, "GPIO registration failed: %d\n", ret); + return gpiochip_add(&tps6586x->gpio); } static int __remove_subdev(struct device *dev, void *unused) @@ -325,37 +319,37 @@ static int tps6586x_remove_subdevs(struct tps6586x *tps6586x) return device_for_each_child(tps6586x->dev, NULL, __remove_subdev); } -static void tps6586x_irq_lock(unsigned int irq) +static void tps6586x_irq_lock(struct irq_data *data) { - struct tps6586x *tps6586x = get_irq_chip_data(irq); + struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data); mutex_lock(&tps6586x->irq_lock); } -static void tps6586x_irq_enable(unsigned int irq) +static void tps6586x_irq_enable(struct irq_data *irq_data) { - struct tps6586x *tps6586x = get_irq_chip_data(irq); - unsigned int __irq = irq - tps6586x->irq_base; + struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); + unsigned int __irq = irq_data->irq - tps6586x->irq_base; const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; tps6586x->mask_reg[data->mask_reg] &= ~data->mask_mask; tps6586x->irq_en |= (1 << __irq); } -static void tps6586x_irq_disable(unsigned int irq) +static void tps6586x_irq_disable(struct irq_data *irq_data) { - struct tps6586x *tps6586x = get_irq_chip_data(irq); + struct tps6586x *tps6586x = irq_data_get_irq_chip_data(irq_data); - unsigned int __irq = irq - tps6586x->irq_base; + unsigned int __irq = irq_data->irq - tps6586x->irq_base; const struct tps6586x_irq_data *data = &tps6586x_irqs[__irq]; tps6586x->mask_reg[data->mask_reg] |= data->mask_mask; tps6586x->irq_en &= ~(1 << __irq); } -static void tps6586x_irq_sync_unlock(unsigned int irq) +static void tps6586x_irq_sync_unlock(struct irq_data *data) { - struct tps6586x *tps6586x = get_irq_chip_data(irq); + struct tps6586x *tps6586x = irq_data_get_irq_chip_data(data); int i; for (i = 0; i < ARRAY_SIZE(tps6586x->mask_reg); i++) { @@ -421,17 +415,17 @@ static int __devinit tps6586x_irq_init(struct tps6586x *tps6586x, int irq, tps6586x->irq_base = irq_base; tps6586x->irq_chip.name = "tps6586x"; - tps6586x->irq_chip.enable = tps6586x_irq_enable; - tps6586x->irq_chip.disable = tps6586x_irq_disable; - tps6586x->irq_chip.bus_lock = tps6586x_irq_lock; - tps6586x->irq_chip.bus_sync_unlock = tps6586x_irq_sync_unlock; + tps6586x->irq_chip.irq_enable = tps6586x_irq_enable; + tps6586x->irq_chip.irq_disable = tps6586x_irq_disable; + tps6586x->irq_chip.irq_bus_lock = tps6586x_irq_lock; + tps6586x->irq_chip.irq_bus_sync_unlock = tps6586x_irq_sync_unlock; for (i = 0; i < ARRAY_SIZE(tps6586x_irqs); i++) { int __irq = i + tps6586x->irq_base; - set_irq_chip_data(__irq, tps6586x); - set_irq_chip_and_handler(__irq, &tps6586x->irq_chip, + irq_set_chip_data(__irq, tps6586x); + irq_set_chip_and_handler(__irq, &tps6586x->irq_chip, handle_simple_irq); - set_irq_nested_thread(__irq, 1); + irq_set_nested_thread(__irq, 1); #ifdef CONFIG_ARM set_irq_flags(__irq, IRQF_VALID); #endif @@ -498,11 +492,7 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, return -EIO; } - if ((ret != TPS658621A_VERSIONCRC) && - (ret != TPS658621C_VERSIONCRC)) { - dev_err(&client->dev, "Unsupported chip ID: %x\n", ret); - return -ENODEV; - } + dev_info(&client->dev, "VERSIONCRC is %02x\n", ret); tps6586x = kzalloc(sizeof(struct tps6586x), GFP_KERNEL); if (tps6586x == NULL) @@ -523,17 +513,28 @@ static int __devinit tps6586x_i2c_probe(struct i2c_client *client, } } + ret = tps6586x_gpio_init(tps6586x, pdata->gpio_base); + if (ret) { + dev_err(&client->dev, "GPIO registration failed: %d\n", ret); + goto err_gpio_init; + } + ret = tps6586x_add_subdevs(tps6586x, pdata); if (ret) { dev_err(&client->dev, "add devices failed: %d\n", ret); goto err_add_devs; } - tps6586x_gpio_init(tps6586x, pdata->gpio_base); - return 0; err_add_devs: + if (pdata->gpio_base) { + ret = gpiochip_remove(&tps6586x->gpio); + if (ret) + dev_err(&client->dev, "Can't remove gpio chip: %d\n", + ret); + } +err_gpio_init: if (client->irq) free_irq(client->irq, tps6586x); err_irq_init: @@ -593,4 +594,3 @@ module_exit(tps6586x_exit); MODULE_DESCRIPTION("TPS6586X core driver"); MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); MODULE_LICENSE("GPL"); - diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 35275ba..960b5be 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -95,7 +95,8 @@ #define twl_has_rtc() false #endif -#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE) +#if defined(CONFIG_TWL4030_USB) || defined(CONFIG_TWL4030_USB_MODULE) ||\ + defined(CONFIG_TWL6030_USB) || defined(CONFIG_TWL6030_USB_MODULE) #define twl_has_usb() true #else #define twl_has_usb() false @@ -682,14 +683,51 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) usb3v1.dev = child; } } + if (twl_has_usb() && pdata->usb && twl_class_is_6030()) { - if (twl_has_watchdog()) { + static struct regulator_consumer_supply usb3v3 = { + .supply = "vusb", + }; + + if (twl_has_regulator()) { + /* this is a template that gets copied */ + struct regulator_init_data usb_fixed = { + .constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL + | REGULATOR_MODE_STANDBY, + .constraints.valid_ops_mask = + REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS, + }; + + child = add_regulator_linked(TWL6030_REG_VUSB, + &usb_fixed, &usb3v3, 1); + if (IS_ERR(child)) + return PTR_ERR(child); + } + + child = add_child(0, "twl6030_usb", + pdata->usb, sizeof(*pdata->usb), + true, + /* irq1 = VBUS_PRES, irq0 = USB ID */ + pdata->irq_base + USBOTG_INTR_OFFSET, + pdata->irq_base + USB_PRES_INTR_OFFSET); + + if (IS_ERR(child)) + return PTR_ERR(child); + /* we need to connect regulators to this transceiver */ + if (twl_has_regulator() && child) + usb3v3.dev = child; + + } + + if (twl_has_watchdog() && twl_class_is_4030()) { child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); if (IS_ERR(child)) return PTR_ERR(child); } - if (twl_has_pwrbutton()) { + if (twl_has_pwrbutton() && twl_class_is_4030()) { child = add_child(1, "twl4030_pwrbutton", NULL, 0, true, pdata->irq_base + 8 + 0, 0); if (IS_ERR(child)) @@ -815,10 +853,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) if (IS_ERR(child)) return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VUSB, pdata->vusb); - if (IS_ERR(child)) - return PTR_ERR(child); - child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1); if (IS_ERR(child)) return PTR_ERR(child); @@ -830,6 +864,10 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3); if (IS_ERR(child)) return PTR_ERR(child); + + child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg); + if (IS_ERR(child)) + return PTR_ERR(child); } if (twl_has_bci() && pdata->bci && @@ -969,7 +1007,7 @@ static int twl_remove(struct i2c_client *client) } /* NOTE: this driver only handles a single twl4030/tps659x0 chip */ -static int __init +static int __devinit twl_probe(struct i2c_client *client, const struct i2c_device_id *id) { int status; diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index 9a4b196..c02fded 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -208,15 +208,13 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) if (pdata->audio) { cell = &codec->cells[childs]; cell->name = "twl4030-codec"; - cell->platform_data = pdata->audio; - cell->data_size = sizeof(*pdata->audio); + cell->mfd_data = pdata->audio; childs++; } if (pdata->vibra) { cell = &codec->cells[childs]; cell->name = "twl4030-vibra"; - cell->platform_data = pdata->vibra; - cell->data_size = sizeof(*pdata->vibra); + cell->mfd_data = pdata->vibra; childs++; } diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index 5d3a147..8a7ee31 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -320,24 +320,8 @@ static int twl4030_irq_thread(void *data) for (module_irq = twl4030_irq_base; pih_isr; pih_isr >>= 1, module_irq++) { - if (pih_isr & 0x1) { - struct irq_desc *d = irq_to_desc(module_irq); - - if (!d) { - pr_err("twl4030: Invalid SIH IRQ: %d\n", - module_irq); - return -EINVAL; - } - - /* These can't be masked ... always warn - * if we get any surprises. - */ - if (d->status & IRQ_DISABLED) - note_interrupt(module_irq, d, - IRQ_NONE); - else - d->handle_irq(module_irq, d); - } + if (pih_isr & 0x1) + generic_handle_irq(module_irq); } local_irq_enable(); @@ -470,7 +454,7 @@ static inline void activate_irq(int irq) set_irq_flags(irq, IRQF_VALID); #else /* same effect on other architectures */ - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -560,24 +544,18 @@ static void twl4030_sih_do_edge(struct work_struct *work) /* Modify only the bits we know must change */ while (edge_change) { int i = fls(edge_change) - 1; - struct irq_desc *d = irq_to_desc(i + agent->irq_base); + struct irq_data *idata = irq_get_irq_data(i + agent->irq_base); int byte = 1 + (i >> 2); int off = (i & 0x3) * 2; - - if (!d) { - pr_err("twl4030: Invalid IRQ: %d\n", - i + agent->irq_base); - return; - } + unsigned int type; bytes[byte] &= ~(0x03 << off); - raw_spin_lock_irq(&d->lock); - if (d->status & IRQ_TYPE_EDGE_RISING) + type = irqd_get_trigger_type(idata); + if (type & IRQ_TYPE_EDGE_RISING) bytes[byte] |= BIT(off + 1); - if (d->status & IRQ_TYPE_EDGE_FALLING) + if (type & IRQ_TYPE_EDGE_FALLING) bytes[byte] |= BIT(off + 0); - raw_spin_unlock_irq(&d->lock); edge_change &= ~BIT(i); } @@ -599,49 +577,41 @@ static void twl4030_sih_do_edge(struct work_struct *work) * completion, potentially including some re-ordering, of these requests. */ -static void twl4030_sih_mask(unsigned irq) +static void twl4030_sih_mask(struct irq_data *data) { - struct sih_agent *sih = get_irq_chip_data(irq); + struct sih_agent *sih = irq_data_get_irq_chip_data(data); unsigned long flags; spin_lock_irqsave(&sih_agent_lock, flags); - sih->imr |= BIT(irq - sih->irq_base); + sih->imr |= BIT(data->irq - sih->irq_base); sih->imr_change_pending = true; queue_work(wq, &sih->mask_work); spin_unlock_irqrestore(&sih_agent_lock, flags); } -static void twl4030_sih_unmask(unsigned irq) +static void twl4030_sih_unmask(struct irq_data *data) { - struct sih_agent *sih = get_irq_chip_data(irq); + struct sih_agent *sih = irq_data_get_irq_chip_data(data); unsigned long flags; spin_lock_irqsave(&sih_agent_lock, flags); - sih->imr &= ~BIT(irq - sih->irq_base); + sih->imr &= ~BIT(data->irq - sih->irq_base); sih->imr_change_pending = true; queue_work(wq, &sih->mask_work); spin_unlock_irqrestore(&sih_agent_lock, flags); } -static int twl4030_sih_set_type(unsigned irq, unsigned trigger) +static int twl4030_sih_set_type(struct irq_data *data, unsigned trigger) { - struct sih_agent *sih = get_irq_chip_data(irq); - struct irq_desc *desc = irq_to_desc(irq); + struct sih_agent *sih = irq_data_get_irq_chip_data(data); unsigned long flags; - if (!desc) { - pr_err("twl4030: Invalid IRQ: %d\n", irq); - return -EINVAL; - } - if (trigger & ~(IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING)) return -EINVAL; spin_lock_irqsave(&sih_agent_lock, flags); - if ((desc->status & IRQ_TYPE_SENSE_MASK) != trigger) { - desc->status &= ~IRQ_TYPE_SENSE_MASK; - desc->status |= trigger; - sih->edge_change |= BIT(irq - sih->irq_base); + if (irqd_get_trigger_type(data) != trigger) { + sih->edge_change |= BIT(data->irq - sih->irq_base); queue_work(wq, &sih->edge_work); } spin_unlock_irqrestore(&sih_agent_lock, flags); @@ -650,9 +620,9 @@ static int twl4030_sih_set_type(unsigned irq, unsigned trigger) static struct irq_chip twl4030_sih_irq_chip = { .name = "twl4030", - .mask = twl4030_sih_mask, - .unmask = twl4030_sih_unmask, - .set_type = twl4030_sih_set_type, + .irq_mask = twl4030_sih_mask, + .irq_unmask = twl4030_sih_unmask, + .irq_set_type = twl4030_sih_set_type, }; /*----------------------------------------------------------------------*/ @@ -680,7 +650,7 @@ static inline int sih_read_isr(const struct sih *sih) */ static void handle_twl4030_sih(unsigned irq, struct irq_desc *desc) { - struct sih_agent *agent = get_irq_data(irq); + struct sih_agent *agent = irq_get_handler_data(irq); const struct sih *sih = agent->sih; int isr; @@ -754,9 +724,9 @@ int twl4030_sih_setup(int module) for (i = 0; i < sih->bits; i++) { irq = irq_base + i; - set_irq_chip_and_handler(irq, &twl4030_sih_irq_chip, - handle_edge_irq); - set_irq_chip_data(irq, agent); + irq_set_chip_and_handler(irq, &twl4030_sih_irq_chip, + handle_edge_irq); + irq_set_chip_data(irq, agent); activate_irq(irq); } @@ -765,8 +735,8 @@ int twl4030_sih_setup(int module) /* replace generic PIH handler (handle_simple_irq) */ irq = sih_mod + twl4030_irq_base; - set_irq_data(irq, agent); - set_irq_chained_handler(irq, handle_twl4030_sih); + irq_set_handler_data(irq, agent); + irq_set_chained_handler(irq, handle_twl4030_sih); pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", sih->name, irq, irq_base, twl4030_irq_next - 1); @@ -815,8 +785,8 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) twl4030_sih_irq_chip.irq_ack = dummy_irq_chip.irq_ack; for (i = irq_base; i < irq_end; i++) { - set_irq_chip_and_handler(i, &twl4030_irq_chip, - handle_simple_irq); + irq_set_chip_and_handler(i, &twl4030_irq_chip, + handle_simple_irq); activate_irq(i); } twl4030_irq_next = i; @@ -856,7 +826,7 @@ fail_rqirq: /* clean up twl4030_sih_setup */ fail: for (i = irq_base; i < irq_end; i++) - set_irq_chip_and_handler(i, NULL, NULL); + irq_set_chip_and_handler(i, NULL, NULL); destroy_workqueue(wq); wq = NULL; return status; diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c new file mode 100644 index 0000000..3941ddc --- /dev/null +++ b/drivers/mfd/twl4030-madc.c @@ -0,0 +1,802 @@ +/* + * + * TWL4030 MADC module driver-This driver monitors the real time + * conversion of analog signals like battery temperature, + * battery type, battery level etc. + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy <j-keerthy@ti.com> + * + * Based on twl4030-madc.c + * Copyright (C) 2008 Nokia Corporation + * Mikko Ylinen <mikko.k.ylinen@nokia.com> + * + * Amit Kucheria <amit.kucheria@canonical.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/i2c/twl.h> +#include <linux/i2c/twl4030-madc.h> +#include <linux/module.h> +#include <linux/stddef.h> +#include <linux/mutex.h> +#include <linux/bitops.h> +#include <linux/jiffies.h> +#include <linux/types.h> +#include <linux/gfp.h> +#include <linux/err.h> + +/* + * struct twl4030_madc_data - a container for madc info + * @dev - pointer to device structure for madc + * @lock - mutex protecting this data structure + * @requests - Array of request struct corresponding to SW1, SW2 and RT + * @imr - Interrupt mask register of MADC + * @isr - Interrupt status register of MADC + */ +struct twl4030_madc_data { + struct device *dev; + struct mutex lock; /* mutex protecting this data structure */ + struct twl4030_madc_request requests[TWL4030_MADC_NUM_METHODS]; + int imr; + int isr; +}; + +static struct twl4030_madc_data *twl4030_madc; + +struct twl4030_prescale_divider_ratios { + s16 numerator; + s16 denominator; +}; + +static const struct twl4030_prescale_divider_ratios +twl4030_divider_ratios[16] = { + {1, 1}, /* CHANNEL 0 No Prescaler */ + {1, 1}, /* CHANNEL 1 No Prescaler */ + {6, 10}, /* CHANNEL 2 */ + {6, 10}, /* CHANNEL 3 */ + {6, 10}, /* CHANNEL 4 */ + {6, 10}, /* CHANNEL 5 */ + {6, 10}, /* CHANNEL 6 */ + {6, 10}, /* CHANNEL 7 */ + {3, 14}, /* CHANNEL 8 */ + {1, 3}, /* CHANNEL 9 */ + {1, 1}, /* CHANNEL 10 No Prescaler */ + {15, 100}, /* CHANNEL 11 */ + {1, 4}, /* CHANNEL 12 */ + {1, 1}, /* CHANNEL 13 Reserved channels */ + {1, 1}, /* CHANNEL 14 Reseved channels */ + {5, 11}, /* CHANNEL 15 */ +}; + + +/* + * Conversion table from -3 to 55 degree Celcius + */ +static int therm_tbl[] = { +30800, 29500, 28300, 27100, +26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, +17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, +11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, 8620, 8310, +8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, 6040, 5830, +5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, 4310, 4170, +4040, 3910, 3790, 3670, 3550 +}; + +/* + * Structure containing the registers + * of different conversion methods supported by MADC. + * Hardware or RT real time conversion request initiated by external host + * processor for RT Signal conversions. + * External host processors can also request for non RT conversions + * SW1 and SW2 software conversions also called asynchronous or GPC request. + */ +static +const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = { + [TWL4030_MADC_RT] = { + .sel = TWL4030_MADC_RTSELECT_LSB, + .avg = TWL4030_MADC_RTAVERAGE_LSB, + .rbase = TWL4030_MADC_RTCH0_LSB, + }, + [TWL4030_MADC_SW1] = { + .sel = TWL4030_MADC_SW1SELECT_LSB, + .avg = TWL4030_MADC_SW1AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW1, + }, + [TWL4030_MADC_SW2] = { + .sel = TWL4030_MADC_SW2SELECT_LSB, + .avg = TWL4030_MADC_SW2AVERAGE_LSB, + .rbase = TWL4030_MADC_GPCH0_LSB, + .ctrl = TWL4030_MADC_CTRL_SW2, + }, +}; + +/* + * Function to read a particular channel value. + * @madc - pointer to struct twl4030_madc_data + * @reg - lsb of ADC Channel + * If the i2c read fails it returns an error else returns 0. + */ +static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg) +{ + u8 msb, lsb; + int ret; + /* + * For each ADC channel, we have MSB and LSB register pair. MSB address + * is always LSB address+1. reg parameter is the address of LSB register + */ + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &msb, reg + 1); + if (ret) { + dev_err(madc->dev, "unable to read MSB register 0x%X\n", + reg + 1); + return ret; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &lsb, reg); + if (ret) { + dev_err(madc->dev, "unable to read LSB register 0x%X\n", reg); + return ret; + } + + return (int)(((msb << 8) | lsb) >> 6); +} + +/* + * Return battery temperature + * Or < 0 on failure. + */ +static int twl4030battery_temperature(int raw_volt) +{ + u8 val; + int temp, curr, volt, res, ret; + + volt = (raw_volt * TEMP_STEP_SIZE) / TEMP_PSR_R; + /* Getting and calculating the supply current in micro ampers */ + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + REG_BCICTL2); + if (ret < 0) + return ret; + curr = ((val & TWL4030_BCI_ITHEN) + 1) * 10; + /* Getting and calculating the thermistor resistance in ohms */ + res = volt * 1000 / curr; + /* calculating temperature */ + for (temp = 58; temp >= 0; temp--) { + int actual = therm_tbl[temp]; + + if ((actual - res) >= 0) + break; + } + + return temp + 1; +} + +static int twl4030battery_current(int raw_volt) +{ + int ret; + u8 val; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, + TWL4030_BCI_BCICTL1); + if (ret) + return ret; + if (val & TWL4030_BCI_CGAIN) /* slope of 0.44 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R1; + else /* slope of 0.88 mV/mA */ + return (raw_volt * CURR_STEP_SIZE) / CURR_PSR_R2; +} +/* + * Function to read channel values + * @madc - pointer to twl4030_madc_data struct + * @reg_base - Base address of the first channel + * @Channels - 16 bit bitmap. If the bit is set, channel value is read + * @buf - The channel values are stored here. if read fails error + * value is stored + * Returns the number of successfully read channels. + */ +static int twl4030_madc_read_channels(struct twl4030_madc_data *madc, + u8 reg_base, unsigned + long channels, int *buf) +{ + int count = 0, count_req = 0, i; + u8 reg; + + for_each_set_bit(i, &channels, TWL4030_MADC_MAX_CHANNELS) { + reg = reg_base + 2 * i; + buf[i] = twl4030_madc_channel_raw_read(madc, reg); + if (buf[i] < 0) { + dev_err(madc->dev, + "Unable to read register 0x%X\n", reg); + count_req++; + continue; + } + switch (i) { + case 10: + buf[i] = twl4030battery_current(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading current\n"); + count_req++; + } else { + count++; + buf[i] = buf[i] - 750; + } + break; + case 1: + buf[i] = twl4030battery_temperature(buf[i]); + if (buf[i] < 0) { + dev_err(madc->dev, "err reading temperature\n"); + count_req++; + } else { + buf[i] -= 3; + count++; + } + break; + default: + count++; + /* Analog Input (V) = conv_result * step_size / R + * conv_result = decimal value of 10-bit conversion + * result + * step size = 1.5 / (2 ^ 10 -1) + * R = Prescaler ratio for input channels. + * Result given in mV hence multiplied by 1000. + */ + buf[i] = (buf[i] * 3 * 1000 * + twl4030_divider_ratios[i].denominator) + / (2 * 1023 * + twl4030_divider_ratios[i].numerator); + } + } + if (count_req) + dev_err(madc->dev, "%d channel conversion failed\n", count_req); + + return count; +} + +/* + * Enables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be enabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * If the i2c read fails it returns an error else returns 0. + */ +static int twl4030_madc_enable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val &= ~(1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + + } + + return 0; +} + +/* + * Disables irq. + * @madc - pointer to twl4030_madc_data struct + * @id - irq number to be disabled + * can take one of TWL4030_MADC_RT, TWL4030_MADC_SW1, TWL4030_MADC_SW2 + * corresponding to RT, SW1, SW2 conversion requests. + * Returns error if i2c read/write fails. + */ +static int twl4030_madc_disable_irq(struct twl4030_madc_data *madc, u8 id) +{ + u8 val; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + return ret; + } + val |= (1 << id); + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, val, madc->imr); + if (ret) { + dev_err(madc->dev, + "unable to write imr register 0x%X\n", madc->imr); + return ret; + } + + return 0; +} + +static irqreturn_t twl4030_madc_threaded_irq_handler(int irq, void *_madc) +{ + struct twl4030_madc_data *madc = _madc; + const struct twl4030_madc_conversion_method *method; + u8 isr_val, imr_val; + int i, len, ret; + struct twl4030_madc_request *r; + + mutex_lock(&madc->lock); + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &isr_val, madc->isr); + if (ret) { + dev_err(madc->dev, "unable to read isr register 0x%X\n", + madc->isr); + goto err_i2c; + } + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, &imr_val, madc->imr); + if (ret) { + dev_err(madc->dev, "unable to read imr register 0x%X\n", + madc->imr); + goto err_i2c; + } + isr_val &= ~imr_val; + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + if (!(isr_val & (1 << i))) + continue; + ret = twl4030_madc_disable_irq(madc, i); + if (ret < 0) + dev_dbg(madc->dev, "Disable interrupt failed%d\n", i); + madc->requests[i].result_pending = 1; + } + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + /* No pending results for this method, move to next one */ + if (!r->result_pending) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + len = twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf); + /* Return results to caller */ + if (r->func_cb != NULL) { + r->func_cb(len, r->channels, r->rbuf); + r->func_cb = NULL; + } + /* Free request */ + r->result_pending = 0; + r->active = 0; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; + +err_i2c: + /* + * In case of error check whichever request is active + * and service the same. + */ + for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) { + r = &madc->requests[i]; + if (r->active == 0) + continue; + method = &twl4030_conversion_methods[r->method]; + /* Read results */ + len = twl4030_madc_read_channels(madc, method->rbase, + r->channels, r->rbuf); + /* Return results to caller */ + if (r->func_cb != NULL) { + r->func_cb(len, r->channels, r->rbuf); + r->func_cb = NULL; + } + /* Free request */ + r->result_pending = 0; + r->active = 0; + } + mutex_unlock(&madc->lock); + + return IRQ_HANDLED; +} + +static int twl4030_madc_set_irq(struct twl4030_madc_data *madc, + struct twl4030_madc_request *req) +{ + struct twl4030_madc_request *p; + int ret; + + p = &madc->requests[req->method]; + memcpy(p, req, sizeof(*req)); + ret = twl4030_madc_enable_irq(madc, req->method); + if (ret < 0) { + dev_err(madc->dev, "enable irq failed!!\n"); + return ret; + } + + return 0; +} + +/* + * Function which enables the madc conversion + * by writing to the control register. + * @madc - pointer to twl4030_madc_data struct + * @conv_method - can be TWL4030_MADC_RT, TWL4030_MADC_SW2, TWL4030_MADC_SW1 + * corresponding to RT SW1 or SW2 conversion methods. + * Returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_start_conversion(struct twl4030_madc_data *madc, + int conv_method) +{ + const struct twl4030_madc_conversion_method *method; + int ret = 0; + method = &twl4030_conversion_methods[conv_method]; + switch (conv_method) { + case TWL4030_MADC_SW1: + case TWL4030_MADC_SW2: + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, + TWL4030_MADC_SW_START, method->ctrl); + if (ret) { + dev_err(madc->dev, + "unable to write ctrl register 0x%X\n", + method->ctrl); + return ret; + } + break; + default: + break; + } + + return 0; +} + +/* + * Function that waits for conversion to be ready + * @madc - pointer to twl4030_madc_data struct + * @timeout_ms - timeout value in milliseconds + * @status_reg - ctrl register + * returns 0 if succeeds else a negative error value + */ +static int twl4030_madc_wait_conversion_ready(struct twl4030_madc_data *madc, + unsigned int timeout_ms, + u8 status_reg) +{ + unsigned long timeout; + int ret; + + timeout = jiffies + msecs_to_jiffies(timeout_ms); + do { + u8 reg; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MADC, ®, status_reg); + if (ret) { + dev_err(madc->dev, + "unable to read status register 0x%X\n", + status_reg); + return ret; + } + if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW)) + return 0; + usleep_range(500, 2000); + } while (!time_after(jiffies, timeout)); + dev_err(madc->dev, "conversion timeout!\n"); + + return -EAGAIN; +} + +/* + * An exported function which can be called from other kernel drivers. + * @req twl4030_madc_request structure + * req->rbuf will be filled with read values of channels based on the + * channel index. If a particular channel reading fails there will + * be a negative error value in the corresponding array element. + * returns 0 if succeeds else error value + */ +int twl4030_madc_conversion(struct twl4030_madc_request *req) +{ + const struct twl4030_madc_conversion_method *method; + u8 ch_msb, ch_lsb; + int ret; + + if (!req) + return -EINVAL; + mutex_lock(&twl4030_madc->lock); + if (req->method < TWL4030_MADC_RT || req->method > TWL4030_MADC_SW2) { + ret = -EINVAL; + goto out; + } + /* Do we have a conversion request ongoing */ + if (twl4030_madc->requests[req->method].active) { + ret = -EBUSY; + goto out; + } + ch_msb = (req->channels >> 8) & 0xff; + ch_lsb = req->channels & 0xff; + method = &twl4030_conversion_methods[req->method]; + /* Select channels to be converted */ + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_msb, method->sel + 1); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel + 1); + return ret; + } + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, ch_lsb, method->sel); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel register 0x%X\n", method->sel + 1); + return ret; + } + /* Select averaging for all channels if do_avg is set */ + if (req->do_avg) { + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, + ch_msb, method->avg + 1); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write avg register 0x%X\n", + method->avg + 1); + return ret; + } + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, + ch_lsb, method->avg); + if (ret) { + dev_err(twl4030_madc->dev, + "unable to write sel reg 0x%X\n", + method->sel + 1); + return ret; + } + } + if (req->type == TWL4030_MADC_IRQ_ONESHOT && req->func_cb != NULL) { + ret = twl4030_madc_set_irq(twl4030_madc, req); + if (ret < 0) + goto out; + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = 1; + ret = 0; + goto out; + } + /* With RT method we should not be here anymore */ + if (req->method == TWL4030_MADC_RT) { + ret = -EINVAL; + goto out; + } + ret = twl4030_madc_start_conversion(twl4030_madc, req->method); + if (ret < 0) + goto out; + twl4030_madc->requests[req->method].active = 1; + /* Wait until conversion is ready (ctrl register returns EOC) */ + ret = twl4030_madc_wait_conversion_ready(twl4030_madc, 5, method->ctrl); + if (ret) { + twl4030_madc->requests[req->method].active = 0; + goto out; + } + ret = twl4030_madc_read_channels(twl4030_madc, method->rbase, + req->channels, req->rbuf); + twl4030_madc->requests[req->method].active = 0; + +out: + mutex_unlock(&twl4030_madc->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(twl4030_madc_conversion); + +/* + * Return channel value + * Or < 0 on failure. + */ +int twl4030_get_madc_conversion(int channel_no) +{ + struct twl4030_madc_request req; + int temp = 0; + int ret; + + req.channels = (1 << channel_no); + req.method = TWL4030_MADC_SW2; + req.active = 0; + req.func_cb = NULL; + ret = twl4030_madc_conversion(&req); + if (ret < 0) + return ret; + if (req.rbuf[channel_no] > 0) + temp = req.rbuf[channel_no]; + + return temp; +} +EXPORT_SYMBOL_GPL(twl4030_get_madc_conversion); + +/* + * Function to enable or disable bias current for + * main battery type reading or temperature sensing + * @madc - pointer to twl4030_madc_data struct + * @chan - can be one of the two values + * TWL4030_BCI_ITHEN - Enables bias current for main battery type reading + * TWL4030_BCI_TYPEN - Enables bias current for main battery temperature + * sensing + * @on - enable or disable chan. + */ +static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc, + int chan, int on) +{ + int ret; + u8 regval; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to read BCICTL1 reg 0x%X", + TWL4030_BCI_BCICTL1); + return ret; + } + if (on) + regval |= chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN; + else + regval &= chan ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN; + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(madc->dev, "unable to write BCICTL1 reg 0x%X\n", + TWL4030_BCI_BCICTL1); + return ret; + } + + return 0; +} + +/* + * Function that sets MADC software power on bit to enable MADC + * @madc - pointer to twl4030_madc_data struct + * @on - Enable or disable MADC software powen on bit. + * returns error if i2c read/write fails else 0 + */ +static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) +{ + u8 regval; + int ret; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ®val, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to read madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + if (on) + regval |= TWL4030_MADC_MADCON; + else + regval &= ~TWL4030_MADC_MADCON; + ret = twl_i2c_write_u8(TWL4030_MODULE_MADC, regval, TWL4030_MADC_CTRL1); + if (ret) { + dev_err(madc->dev, "unable to write madc ctrl1 reg 0x%X\n", + TWL4030_MADC_CTRL1); + return ret; + } + + return 0; +} + +/* + * Initialize MADC and request for threaded irq + */ +static int __devinit twl4030_madc_probe(struct platform_device *pdev) +{ + struct twl4030_madc_data *madc; + struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data; + int ret; + u8 regval; + + if (!pdata) { + dev_err(&pdev->dev, "platform_data not available\n"); + return -EINVAL; + } + madc = kzalloc(sizeof(*madc), GFP_KERNEL); + if (!madc) + return -ENOMEM; + + /* + * Phoenix provides 2 interrupt lines. The first one is connected to + * the OMAP. The other one can be connected to the other processor such + * as modem. Hence two separate ISR and IMR registers. + */ + madc->imr = (pdata->irq_line == 1) ? + TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2; + madc->isr = (pdata->irq_line == 1) ? + TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2; + ret = twl4030_madc_set_power(madc, 1); + if (ret < 0) + goto err_power; + ret = twl4030_madc_set_current_generator(madc, 0, 1); + if (ret < 0) + goto err_current_generator; + + ret = twl_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, + ®val, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg BCI CTL1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + regval |= TWL4030_BCI_MESBAT; + ret = twl_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE, + regval, TWL4030_BCI_BCICTL1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg BCI Ctl1 0x%X\n", + TWL4030_BCI_BCICTL1); + goto err_i2c; + } + platform_set_drvdata(pdev, madc); + mutex_init(&madc->lock); + ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, + twl4030_madc_threaded_irq_handler, + IRQF_TRIGGER_RISING, "twl4030_madc", madc); + if (ret) { + dev_dbg(&pdev->dev, "could not request irq\n"); + goto err_irq; + } + twl4030_madc = madc; + return 0; +err_irq: + platform_set_drvdata(pdev, NULL); +err_i2c: + twl4030_madc_set_current_generator(madc, 0, 0); +err_current_generator: + twl4030_madc_set_power(madc, 0); +err_power: + kfree(madc); + + return ret; +} + +static int __devexit twl4030_madc_remove(struct platform_device *pdev) +{ + struct twl4030_madc_data *madc = platform_get_drvdata(pdev); + + free_irq(platform_get_irq(pdev, 0), madc); + platform_set_drvdata(pdev, NULL); + twl4030_madc_set_current_generator(madc, 0, 0); + twl4030_madc_set_power(madc, 0); + kfree(madc); + + return 0; +} + +static struct platform_driver twl4030_madc_driver = { + .probe = twl4030_madc_probe, + .remove = __exit_p(twl4030_madc_remove), + .driver = { + .name = "twl4030_madc", + .owner = THIS_MODULE, + }, +}; + +static int __init twl4030_madc_init(void) +{ + return platform_driver_register(&twl4030_madc_driver); +} + +module_init(twl4030_madc_init); + +static void __exit twl4030_madc_exit(void) +{ + platform_driver_unregister(&twl4030_madc_driver); +} + +module_exit(twl4030_madc_exit); + +MODULE_DESCRIPTION("TWL4030 ADC driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("J Keerthy"); +MODULE_ALIAS("platform:twl4030_madc"); diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index aaedb11..dfbae34 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -74,7 +74,7 @@ static int twl6030_interrupt_mapping[24] = { USBOTG_INTR_OFFSET, /* Bit 16 ID_WKUP */ USBOTG_INTR_OFFSET, /* Bit 17 VBUS_WKUP */ USBOTG_INTR_OFFSET, /* Bit 18 ID */ - USBOTG_INTR_OFFSET, /* Bit 19 VBUS */ + USB_PRES_INTR_OFFSET, /* Bit 19 VBUS */ CHARGER_INTR_OFFSET, /* Bit 20 CHRG_CTRL */ CHARGER_INTR_OFFSET, /* Bit 21 EXT_CHRG */ CHARGER_INTR_OFFSET, /* Bit 22 INT_CHRG */ @@ -128,27 +128,19 @@ static int twl6030_irq_thread(void *data) sts.bytes[3] = 0; /* Only 24 bits are valid*/ + /* + * Since VBUS status bit is not reliable for VBUS disconnect + * use CHARGER VBUS detection status bit instead. + */ + if (sts.bytes[2] & 0x10) + sts.bytes[2] |= 0x08; + for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) { local_irq_disable(); if (sts.int_sts & 0x1) { int module_irq = twl6030_irq_base + twl6030_interrupt_mapping[i]; - struct irq_desc *d = irq_to_desc(module_irq); - - if (!d) { - pr_err("twl6030: Invalid SIH IRQ: %d\n", - module_irq); - return -EINVAL; - } - - /* These can't be masked ... always warn - * if we get any surprises. - */ - if (d->status & IRQ_DISABLED) - note_interrupt(module_irq, d, - IRQ_NONE); - else - d->handle_irq(module_irq, d); + generic_handle_irq(module_irq); } local_irq_enable(); @@ -191,7 +183,7 @@ static inline void activate_irq(int irq) set_irq_flags(irq, IRQF_VALID); #else /* same effect on other architectures */ - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } @@ -237,7 +229,7 @@ int twl6030_mmc_card_detect_config(void) twl6030_interrupt_unmask(TWL6030_MMCDETECT_INT_MASK, REG_INT_MSK_STS_B); /* - * Intially Configuring MMC_CTRL for receving interrupts & + * Initially Configuring MMC_CTRL for receiving interrupts & * Card status on TWL6030 for MMC1 */ ret = twl_i2c_read_u8(TWL6030_MODULE_ID0, ®_val, TWL6030_MMCCTRL); @@ -283,7 +275,7 @@ int twl6030_mmc_card_detect(struct device *dev, int slot) /* TWL6030 provide's Card detect support for * only MMC1 controller. */ - pr_err("Unkown MMC controller %d in %s\n", pdev->id, __func__); + pr_err("Unknown MMC controller %d in %s\n", pdev->id, __func__); return ret; } /* @@ -325,11 +317,11 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) */ twl6030_irq_chip = dummy_irq_chip; twl6030_irq_chip.name = "twl6030"; - twl6030_irq_chip.set_type = NULL; + twl6030_irq_chip.irq_set_type = NULL; for (i = irq_base; i < irq_end; i++) { - set_irq_chip_and_handler(i, &twl6030_irq_chip, - handle_simple_irq); + irq_set_chip_and_handler(i, &twl6030_irq_chip, + handle_simple_irq); activate_irq(i); } @@ -358,7 +350,7 @@ fail_irq: fail_kthread: for (i = irq_base; i < irq_end; i++) - set_irq_chip_and_handler(i, NULL, NULL); + irq_set_chip_and_handler(i, NULL, NULL); return status; } diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index d73f84b..daf6952 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -8,7 +8,7 @@ * Copyright: MontaVista Software, Inc. * * Spliting done by: Marek Vasut <marek.vasut@gmail.com> - * If something doesnt work and it worked before spliting, e-mail me, + * If something doesn't work and it worked before spliting, e-mail me, * dont bother Nicolas please ;-) * * This program is free software; you can redistribute it and/or modify diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c index 000cb41..38ffbd5 100644 --- a/drivers/mfd/ucb1x00-ts.c +++ b/drivers/mfd/ucb1x00-ts.c @@ -60,6 +60,7 @@ static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x input_report_abs(idev, ABS_X, x); input_report_abs(idev, ABS_Y, y); input_report_abs(idev, ABS_PRESSURE, pressure); + input_report_key(idev, BTN_TOUCH, 1); input_sync(idev); } @@ -68,6 +69,7 @@ static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts) struct input_dev *idev = ts->idev; input_report_abs(idev, ABS_PRESSURE, 0); + input_report_key(idev, BTN_TOUCH, 0); input_sync(idev); } @@ -384,13 +386,20 @@ static int ucb1x00_ts_add(struct ucb1x00_dev *dev) idev->open = ucb1x00_ts_open; idev->close = ucb1x00_ts_close; - __set_bit(EV_ABS, idev->evbit); - __set_bit(ABS_X, idev->absbit); - __set_bit(ABS_Y, idev->absbit); - __set_bit(ABS_PRESSURE, idev->absbit); + idev->evbit[0] = BIT_MASK(EV_ABS) | BIT_MASK(EV_KEY); + idev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); input_set_drvdata(idev, ts); + ucb1x00_adc_enable(ts->ucb); + ts->x_res = ucb1x00_ts_read_xres(ts); + ts->y_res = ucb1x00_ts_read_yres(ts); + ucb1x00_adc_disable(ts->ucb); + + input_set_abs_params(idev, ABS_X, 0, ts->x_res, 0, 0); + input_set_abs_params(idev, ABS_Y, 0, ts->y_res, 0, 0); + input_set_abs_params(idev, ABS_PRESSURE, 0, 0, 0, 0); + err = input_register_device(idev); if (err) goto fail; diff --git a/drivers/mfd/vx855.c b/drivers/mfd/vx855.c index ebb0597..d698703 100644 --- a/drivers/mfd/vx855.c +++ b/drivers/mfd/vx855.c @@ -112,7 +112,7 @@ out: return ret; } -static void vx855_remove(struct pci_dev *pdev) +static void __devexit vx855_remove(struct pci_dev *pdev) { mfd_remove_devices(&pdev->dev); pci_disable_device(pdev); @@ -122,6 +122,7 @@ static struct pci_device_id vx855_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_VX855) }, { 0, } }; +MODULE_DEVICE_TABLE(pci, vx855_pci_tbl); static struct pci_driver vx855_pci_driver = { .name = "vx855", diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c new file mode 100644 index 0000000..04914f2 --- /dev/null +++ b/drivers/mfd/wl1273-core.c @@ -0,0 +1,289 @@ +/* + * MFD driver for wl1273 FM radio and audio codec submodules. + * + * Copyright (C) 2011 Nokia Corporation + * Author: Matti Aaltonen <matti.j.aaltonen@nokia.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include <linux/mfd/wl1273-core.h> +#include <linux/slab.h> + +#define DRIVER_DESC "WL1273 FM Radio Core" + +static const struct i2c_device_id wl1273_driver_id_table[] = { + { WL1273_FM_DRIVER_NAME, 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wl1273_driver_id_table); + +static int wl1273_fm_read_reg(struct wl1273_core *core, u8 reg, u16 *value) +{ + struct i2c_client *client = core->client; + u8 b[2]; + int r; + + r = i2c_smbus_read_i2c_block_data(client, reg, sizeof(b), b); + if (r != 2) { + dev_err(&client->dev, "%s: Read: %d fails.\n", __func__, reg); + return -EREMOTEIO; + } + + *value = (u16)b[0] << 8 | b[1]; + + return 0; +} + +static int wl1273_fm_write_cmd(struct wl1273_core *core, u8 cmd, u16 param) +{ + struct i2c_client *client = core->client; + u8 buf[] = { (param >> 8) & 0xff, param & 0xff }; + int r; + + r = i2c_smbus_write_i2c_block_data(client, cmd, sizeof(buf), buf); + if (r) { + dev_err(&client->dev, "%s: Cmd: %d fails.\n", __func__, cmd); + return r; + } + + return 0; +} + +static int wl1273_fm_write_data(struct wl1273_core *core, u8 *data, u16 len) +{ + struct i2c_client *client = core->client; + struct i2c_msg msg; + int r; + + msg.addr = client->addr; + msg.flags = 0; + msg.buf = data; + msg.len = len; + + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + dev_err(&client->dev, "%s: write error.\n", __func__); + return -EREMOTEIO; + } + + return 0; +} + +/** + * wl1273_fm_set_audio() - Set audio mode. + * @core: A pointer to the device struct. + * @new_mode: The new audio mode. + * + * Audio modes are WL1273_AUDIO_DIGITAL and WL1273_AUDIO_ANALOG. + */ +static int wl1273_fm_set_audio(struct wl1273_core *core, unsigned int new_mode) +{ + int r = 0; + + if (core->mode == WL1273_MODE_OFF || + core->mode == WL1273_MODE_SUSPENDED) + return -EPERM; + + if (core->mode == WL1273_MODE_RX && new_mode == WL1273_AUDIO_DIGITAL) { + r = wl1273_fm_write_cmd(core, WL1273_PCM_MODE_SET, + WL1273_PCM_DEF_MODE); + if (r) + goto out; + + r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, + core->i2s_mode); + if (r) + goto out; + + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, + WL1273_AUDIO_ENABLE_I2S); + if (r) + goto out; + + } else if (core->mode == WL1273_MODE_RX && + new_mode == WL1273_AUDIO_ANALOG) { + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_ENABLE, + WL1273_AUDIO_ENABLE_ANALOG); + if (r) + goto out; + + } else if (core->mode == WL1273_MODE_TX && + new_mode == WL1273_AUDIO_DIGITAL) { + r = wl1273_fm_write_cmd(core, WL1273_I2S_MODE_CONFIG_SET, + core->i2s_mode); + if (r) + goto out; + + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, + WL1273_AUDIO_IO_SET_I2S); + if (r) + goto out; + + } else if (core->mode == WL1273_MODE_TX && + new_mode == WL1273_AUDIO_ANALOG) { + r = wl1273_fm_write_cmd(core, WL1273_AUDIO_IO_SET, + WL1273_AUDIO_IO_SET_ANALOG); + if (r) + goto out; + } + + core->audio_mode = new_mode; +out: + return r; +} + +/** + * wl1273_fm_set_volume() - Set volume. + * @core: A pointer to the device struct. + * @volume: The new volume value. + */ +static int wl1273_fm_set_volume(struct wl1273_core *core, unsigned int volume) +{ + u16 val; + int r; + + if (volume > WL1273_MAX_VOLUME) + return -EINVAL; + + if (core->volume == volume) + return 0; + + r = wl1273_fm_write_cmd(core, WL1273_VOLUME_SET, volume); + if (r) + return r; + + core->volume = volume; + return 0; +} + +static int wl1273_core_remove(struct i2c_client *client) +{ + struct wl1273_core *core = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "%s\n", __func__); + + mfd_remove_devices(&client->dev); + kfree(core); + + return 0; +} + +static int __devinit wl1273_core_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct wl1273_fm_platform_data *pdata = client->dev.platform_data; + struct wl1273_core *core; + struct mfd_cell *cell; + int children = 0; + int r = 0; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!pdata) { + dev_err(&client->dev, "No platform data.\n"); + return -EINVAL; + } + + if (!(pdata->children & WL1273_RADIO_CHILD)) { + dev_err(&client->dev, "Cannot function without radio child.\n"); + return -EINVAL; + } + + core = kzalloc(sizeof(*core), GFP_KERNEL); + if (!core) + return -ENOMEM; + + core->pdata = pdata; + core->client = client; + mutex_init(&core->lock); + + i2c_set_clientdata(client, core); + + dev_dbg(&client->dev, "%s: Have V4L2.\n", __func__); + + cell = &core->cells[children]; + cell->name = "wl1273_fm_radio"; + cell->mfd_data = &core; + children++; + + core->read = wl1273_fm_read_reg; + core->write = wl1273_fm_write_cmd; + core->write_data = wl1273_fm_write_data; + core->set_audio = wl1273_fm_set_audio; + core->set_volume = wl1273_fm_set_volume; + + if (pdata->children & WL1273_CODEC_CHILD) { + cell = &core->cells[children]; + + dev_dbg(&client->dev, "%s: Have codec.\n", __func__); + cell->name = "wl1273-codec"; + cell->mfd_data = &core; + children++; + } + + dev_dbg(&client->dev, "%s: number of children: %d.\n", + __func__, children); + + r = mfd_add_devices(&client->dev, -1, core->cells, + children, NULL, 0); + if (r) + goto err; + + return 0; + +err: + pdata->free_resources(); + kfree(core); + + dev_dbg(&client->dev, "%s\n", __func__); + + return r; +} + +static struct i2c_driver wl1273_core_driver = { + .driver = { + .name = WL1273_FM_DRIVER_NAME, + }, + .probe = wl1273_core_probe, + .id_table = wl1273_driver_id_table, + .remove = __devexit_p(wl1273_core_remove), +}; + +static int __init wl1273_core_init(void) +{ + int r; + + r = i2c_add_driver(&wl1273_core_driver); + if (r) { + pr_err(WL1273_FM_DRIVER_NAME + ": driver registration failed\n"); + return r; + } + + return r; +} + +static void __exit wl1273_core_exit(void) +{ + i2c_del_driver(&wl1273_core_driver); +} +late_initcall(wl1273_core_init); +module_exit(wl1273_core_exit); + +MODULE_AUTHOR("Matti Aaltonen <matti.j.aaltonen@nokia.com>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 7d2563f..3fe9a58 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -1455,7 +1455,11 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret); goto err; } - if (ret != 0x6204) { + switch (ret) { + case 0x6204: + case 0x6246: + break; + default: dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret); ret = -EINVAL; goto err; @@ -1537,6 +1541,12 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) dev_info(wm831x->dev, "WM8325 revision %c\n", 'A' + rev); break; + case WM8326: + parent = WM8326; + wm831x->num_gpio = 12; + dev_info(wm831x->dev, "WM8326 revision %c\n", 'A' + rev); + break; + default: dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret); ret = -EINVAL; @@ -1606,21 +1616,12 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) break; case WM8320: - ret = mfd_add_devices(wm831x->dev, -1, - wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, 0); - break; - case WM8321: - ret = mfd_add_devices(wm831x->dev, -1, - wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, 0); - break; - case WM8325: + case WM8326: ret = mfd_add_devices(wm831x->dev, -1, wm8320_devs, ARRAY_SIZE(wm8320_devs), - NULL, 0); + NULL, wm831x->irq_base); break; default: diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index 156b198..a06cbc7 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -51,17 +51,25 @@ static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg, int bytes, void *src) { struct i2c_client *i2c = wm831x->control_data; - unsigned char msg[bytes + 2]; + struct i2c_msg xfer[2]; int ret; reg = cpu_to_be16(reg); - memcpy(&msg[0], ®, 2); - memcpy(&msg[2], src, bytes); - ret = i2c_master_send(i2c, msg, bytes + 2); + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = (char *)® + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_NOSTART; + xfer[1].len = bytes; + xfer[1].buf = (char *)src; + + ret = i2c_transfer(i2c->adapter, xfer, 2); if (ret < 0) return ret; - if (ret < bytes + 2) + if (ret != 2) return -EIO; return 0; @@ -94,9 +102,9 @@ static int wm831x_i2c_remove(struct i2c_client *i2c) return 0; } -static int wm831x_i2c_suspend(struct i2c_client *i2c, pm_message_t mesg) +static int wm831x_i2c_suspend(struct device *dev) { - struct wm831x *wm831x = i2c_get_clientdata(i2c); + struct wm831x *wm831x = dev_get_drvdata(dev); return wm831x_device_suspend(wm831x); } @@ -108,19 +116,23 @@ static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8320", WM8320 }, { "wm8321", WM8321 }, { "wm8325", WM8325 }, + { "wm8326", WM8326 }, { } }; MODULE_DEVICE_TABLE(i2c, wm831x_i2c_id); +static const struct dev_pm_ops wm831x_pm_ops = { + .suspend = wm831x_i2c_suspend, +}; static struct i2c_driver wm831x_i2c_driver = { .driver = { - .name = "wm831x", - .owner = THIS_MODULE, + .name = "wm831x", + .owner = THIS_MODULE, + .pm = &wm831x_pm_ops, }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, - .suspend = wm831x_i2c_suspend, .id_table = wm831x_i2c_id, }; diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index 294183b..23e66af 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -26,15 +26,6 @@ #include <linux/delay.h> -/* - * Since generic IRQs don't currently support interrupt controllers on - * interrupt driven buses we don't use genirq but instead provide an - * interface that looks very much like the standard ones. This leads - * to some bodges, including storing interrupt handler information in - * the static irq_data table we use to look up the data for individual - * interrupts, but hopefully won't last too long. - */ - struct wm831x_irq_data { int primary; int reg; @@ -345,22 +336,26 @@ static inline struct wm831x_irq_data *irq_to_wm831x_irq(struct wm831x *wm831x, return &wm831x_irqs[irq - wm831x->irq_base]; } -static void wm831x_irq_lock(unsigned int irq) +static void wm831x_irq_lock(struct irq_data *data) { - struct wm831x *wm831x = get_irq_chip_data(irq); + struct wm831x *wm831x = irq_data_get_irq_chip_data(data); mutex_lock(&wm831x->irq_lock); } -static void wm831x_irq_sync_unlock(unsigned int irq) +static void wm831x_irq_sync_unlock(struct irq_data *data) { - struct wm831x *wm831x = get_irq_chip_data(irq); + struct wm831x *wm831x = irq_data_get_irq_chip_data(data); int i; for (i = 0; i < ARRAY_SIZE(wm831x->irq_masks_cur); i++) { /* If there's been a change in the mask write it back * to the hardware. */ if (wm831x->irq_masks_cur[i] != wm831x->irq_masks_cache[i]) { + dev_dbg(wm831x->dev, "IRQ mask sync: %x = %x\n", + WM831X_INTERRUPT_STATUS_1_MASK + i, + wm831x->irq_masks_cur[i]); + wm831x->irq_masks_cache[i] = wm831x->irq_masks_cur[i]; wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1_MASK + i, @@ -371,28 +366,30 @@ static void wm831x_irq_sync_unlock(unsigned int irq) mutex_unlock(&wm831x->irq_lock); } -static void wm831x_irq_unmask(unsigned int irq) +static void wm831x_irq_enable(struct irq_data *data) { - struct wm831x *wm831x = get_irq_chip_data(irq); - struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq); + struct wm831x *wm831x = irq_data_get_irq_chip_data(data); + struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, + data->irq); wm831x->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } -static void wm831x_irq_mask(unsigned int irq) +static void wm831x_irq_disable(struct irq_data *data) { - struct wm831x *wm831x = get_irq_chip_data(irq); - struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, irq); + struct wm831x *wm831x = irq_data_get_irq_chip_data(data); + struct wm831x_irq_data *irq_data = irq_to_wm831x_irq(wm831x, + data->irq); wm831x->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; } -static int wm831x_irq_set_type(unsigned int irq, unsigned int type) +static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) { - struct wm831x *wm831x = get_irq_chip_data(irq); - int val; + struct wm831x *wm831x = irq_data_get_irq_chip_data(data); + int val, irq; - irq = irq - wm831x->irq_base; + irq = data->irq - wm831x->irq_base; if (irq < WM831X_IRQ_GPIO_1 || irq > WM831X_IRQ_GPIO_11) { /* Ignore internal-only IRQs */ @@ -421,12 +418,12 @@ static int wm831x_irq_set_type(unsigned int irq, unsigned int type) } static struct irq_chip wm831x_irq_chip = { - .name = "wm831x", - .bus_lock = wm831x_irq_lock, - .bus_sync_unlock = wm831x_irq_sync_unlock, - .mask = wm831x_irq_mask, - .unmask = wm831x_irq_unmask, - .set_type = wm831x_irq_set_type, + .name = "wm831x", + .irq_bus_lock = wm831x_irq_lock, + .irq_bus_sync_unlock = wm831x_irq_sync_unlock, + .irq_disable = wm831x_irq_disable, + .irq_enable = wm831x_irq_enable, + .irq_set_type = wm831x_irq_set_type, }; /* The processing of the primary interrupt occurs in a thread so that @@ -447,6 +444,18 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) goto out; } + /* The touch interrupts are visible in the primary register as + * an optimisation; open code this to avoid complicating the + * main handling loop and so we can also skip iterating the + * descriptors. + */ + if (primary & WM831X_TCHPD_INT) + handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHPD); + if (primary & WM831X_TCHDATA_INT) + handle_nested_irq(wm831x->irq_base + WM831X_IRQ_TCHDATA); + if (primary & (WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT)) + goto out; + for (i = 0; i < ARRAY_SIZE(wm831x_irqs); i++) { int offset = wm831x_irqs[i].reg - 1; @@ -479,6 +488,9 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) } out: + /* Touchscreen interrupts are handled specially in the driver */ + status_regs[0] &= ~(WM831X_TCHDATA_EINT | WM831X_TCHPD_EINT); + for (i = 0; i < ARRAY_SIZE(status_regs); i++) { if (status_regs[i]) wm831x_reg_write(wm831x, WM831X_INTERRUPT_STATUS_1 + i, @@ -515,6 +527,25 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) return 0; } + if (pdata->irq_cmos) + i = 0; + else + i = WM831X_IRQ_OD; + + wm831x_set_bits(wm831x, WM831X_IRQ_CONFIG, + WM831X_IRQ_OD, i); + + /* Try to flag /IRQ as a wake source; there are a number of + * unconditional wake sources in the PMIC so this isn't + * conditional but we don't actually care *too* much if it + * fails. + */ + ret = enable_irq_wake(irq); + if (ret != 0) { + dev_warn(wm831x->dev, "Can't enable IRQ as wake source: %d\n", + ret); + } + wm831x->irq = irq; wm831x->irq_base = pdata->irq_base; @@ -522,17 +553,17 @@ int wm831x_irq_init(struct wm831x *wm831x, int irq) for (cur_irq = wm831x->irq_base; cur_irq < ARRAY_SIZE(wm831x_irqs) + wm831x->irq_base; cur_irq++) { - set_irq_chip_data(cur_irq, wm831x); - set_irq_chip_and_handler(cur_irq, &wm831x_irq_chip, + irq_set_chip_data(cur_irq, wm831x); + irq_set_chip_and_handler(cur_irq, &wm831x_irq_chip, handle_edge_irq); - set_irq_nested_thread(cur_irq, 1); + irq_set_nested_thread(cur_irq, 1); /* ARM needs us to explicitly flag the IRQ as valid * and will set them noprobe when we do so. */ #ifdef CONFIG_ARM set_irq_flags(cur_irq, IRQF_VALID); #else - set_irq_noprobe(cur_irq); + irq_set_noprobe(cur_irq); #endif } diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 2789b15..eed8e4f 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -14,6 +14,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/pm.h> #include <linux/spi/spi.h> #include <linux/mfd/wm831x/core.h> @@ -81,6 +82,8 @@ static int __devinit wm831x_spi_probe(struct spi_device *spi) type = WM8321; else if (strcmp(spi->modalias, "wm8325") == 0) type = WM8325; + else if (strcmp(spi->modalias, "wm8326") == 0) + type = WM8326; else { dev_err(&spi->dev, "Unknown device type\n"); return -EINVAL; @@ -111,22 +114,27 @@ static int __devexit wm831x_spi_remove(struct spi_device *spi) return 0; } -static int wm831x_spi_suspend(struct spi_device *spi, pm_message_t m) +static int wm831x_spi_suspend(struct device *dev) { - struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + struct wm831x *wm831x = dev_get_drvdata(dev); return wm831x_device_suspend(wm831x); } +static const struct dev_pm_ops wm831x_spi_pm = { + .freeze = wm831x_spi_suspend, + .suspend = wm831x_spi_suspend, +}; + static struct spi_driver wm8310_spi_driver = { .driver = { .name = "wm8310", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8311_spi_driver = { @@ -134,10 +142,10 @@ static struct spi_driver wm8311_spi_driver = { .name = "wm8311", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8312_spi_driver = { @@ -145,10 +153,10 @@ static struct spi_driver wm8312_spi_driver = { .name = "wm8312", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8320_spi_driver = { @@ -156,10 +164,10 @@ static struct spi_driver wm8320_spi_driver = { .name = "wm8320", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8321_spi_driver = { @@ -167,10 +175,10 @@ static struct spi_driver wm8321_spi_driver = { .name = "wm8321", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static struct spi_driver wm8325_spi_driver = { @@ -178,10 +186,21 @@ static struct spi_driver wm8325_spi_driver = { .name = "wm8325", .bus = &spi_bus_type, .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, + }, + .probe = wm831x_spi_probe, + .remove = __devexit_p(wm831x_spi_remove), +}; + +static struct spi_driver wm8326_spi_driver = { + .driver = { + .name = "wm8326", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + .pm = &wm831x_spi_pm, }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), - .suspend = wm831x_spi_suspend, }; static int __init wm831x_spi_init(void) @@ -212,12 +231,17 @@ static int __init wm831x_spi_init(void) if (ret != 0) pr_err("Failed to register WM8325 SPI driver: %d\n", ret); + ret = spi_register_driver(&wm8326_spi_driver); + if (ret != 0) + pr_err("Failed to register WM8326 SPI driver: %d\n", ret); + return 0; } subsys_initcall(wm831x_spi_init); static void __exit wm831x_spi_exit(void) { + spi_unregister_driver(&wm8326_spi_driver); spi_unregister_driver(&wm8325_spi_driver); spi_unregister_driver(&wm8321_spi_driver); spi_unregister_driver(&wm8320_spi_driver); diff --git a/drivers/mfd/wm8350-irq.c b/drivers/mfd/wm8350-irq.c index f56c9ad..ed4b22a 100644 --- a/drivers/mfd/wm8350-irq.c +++ b/drivers/mfd/wm8350-irq.c @@ -417,16 +417,16 @@ static irqreturn_t wm8350_irq(int irq, void *irq_data) return IRQ_HANDLED; } -static void wm8350_irq_lock(unsigned int irq) +static void wm8350_irq_lock(struct irq_data *data) { - struct wm8350 *wm8350 = get_irq_chip_data(irq); + struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); mutex_lock(&wm8350->irq_lock); } -static void wm8350_irq_sync_unlock(unsigned int irq) +static void wm8350_irq_sync_unlock(struct irq_data *data) { - struct wm8350 *wm8350 = get_irq_chip_data(irq); + struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); int i; for (i = 0; i < ARRAY_SIZE(wm8350->irq_masks); i++) { @@ -442,28 +442,30 @@ static void wm8350_irq_sync_unlock(unsigned int irq) mutex_unlock(&wm8350->irq_lock); } -static void wm8350_irq_enable(unsigned int irq) +static void wm8350_irq_enable(struct irq_data *data) { - struct wm8350 *wm8350 = get_irq_chip_data(irq); - struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq); + struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); + struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, + data->irq); wm8350->irq_masks[irq_data->reg] &= ~irq_data->mask; } -static void wm8350_irq_disable(unsigned int irq) +static void wm8350_irq_disable(struct irq_data *data) { - struct wm8350 *wm8350 = get_irq_chip_data(irq); - struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, irq); + struct wm8350 *wm8350 = irq_data_get_irq_chip_data(data); + struct wm8350_irq_data *irq_data = irq_to_wm8350_irq(wm8350, + data->irq); wm8350->irq_masks[irq_data->reg] |= irq_data->mask; } static struct irq_chip wm8350_irq_chip = { - .name = "wm8350", - .bus_lock = wm8350_irq_lock, - .bus_sync_unlock = wm8350_irq_sync_unlock, - .disable = wm8350_irq_disable, - .enable = wm8350_irq_enable, + .name = "wm8350", + .irq_bus_lock = wm8350_irq_lock, + .irq_bus_sync_unlock = wm8350_irq_sync_unlock, + .irq_disable = wm8350_irq_disable, + .irq_enable = wm8350_irq_enable, }; int wm8350_irq_init(struct wm8350 *wm8350, int irq, @@ -516,17 +518,17 @@ int wm8350_irq_init(struct wm8350 *wm8350, int irq, for (cur_irq = wm8350->irq_base; cur_irq < ARRAY_SIZE(wm8350_irqs) + wm8350->irq_base; cur_irq++) { - set_irq_chip_data(cur_irq, wm8350); - set_irq_chip_and_handler(cur_irq, &wm8350_irq_chip, + irq_set_chip_data(cur_irq, wm8350); + irq_set_chip_and_handler(cur_irq, &wm8350_irq_chip, handle_edge_irq); - set_irq_nested_thread(cur_irq, 1); + irq_set_nested_thread(cur_irq, 1); /* ARM needs us to explicitly flag the IRQ as valid * and will set them noprobe when we do so. */ #ifdef CONFIG_ARM set_irq_flags(cur_irq, IRQF_VALID); #else - set_irq_noprobe(cur_irq); + irq_set_noprobe(cur_irq); #endif } diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 1bfef48..3a6e78c 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -245,7 +245,7 @@ static int wm8400_register_codec(struct wm8400 *wm8400) { struct mfd_cell cell = { .name = "wm8400-codec", - .driver_data = wm8400, + .mfd_data = wm8400, }; return mfd_add_devices(wm8400->dev, -1, &cell, 1, NULL, 0); diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index b3b2aaf..e198d40 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -18,6 +18,7 @@ #include <linux/i2c.h> #include <linux/delay.h> #include <linux/mfd/core.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/regulator/machine.h> @@ -39,10 +40,8 @@ static int wm8994_read(struct wm8994 *wm8994, unsigned short reg, return ret; for (i = 0; i < bytes / 2; i++) { - buf[i] = be16_to_cpu(buf[i]); - dev_vdbg(wm8994->dev, "Read %04x from R%d(0x%x)\n", - buf[i], reg + i, reg + i); + be16_to_cpu(buf[i]), reg + i, reg + i); } return 0; @@ -68,7 +67,7 @@ int wm8994_reg_read(struct wm8994 *wm8994, unsigned short reg) if (ret < 0) return ret; else - return val; + return be16_to_cpu(val); } EXPORT_SYMBOL_GPL(wm8994_reg_read); @@ -78,7 +77,7 @@ EXPORT_SYMBOL_GPL(wm8994_reg_read); * @wm8994: Device to read from * @reg: First register * @count: Number of registers - * @buf: Buffer to fill. + * @buf: Buffer to fill. The data will be returned big endian. */ int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, int count, u16 *buf) @@ -96,9 +95,9 @@ int wm8994_bulk_read(struct wm8994 *wm8994, unsigned short reg, EXPORT_SYMBOL_GPL(wm8994_bulk_read); static int wm8994_write(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *src) + int bytes, const void *src) { - u16 *buf = src; + const u16 *buf = src; int i; BUG_ON(bytes % 2); @@ -106,9 +105,7 @@ static int wm8994_write(struct wm8994 *wm8994, unsigned short reg, for (i = 0; i < bytes / 2; i++) { dev_vdbg(wm8994->dev, "Write %04x to R%d(0x%x)\n", - buf[i], reg + i, reg + i); - - buf[i] = cpu_to_be16(buf[i]); + be16_to_cpu(buf[i]), reg + i, reg + i); } return wm8994->write_dev(wm8994, reg, bytes, src); @@ -126,6 +123,8 @@ int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg, { int ret; + val = cpu_to_be16(val); + mutex_lock(&wm8994->io_lock); ret = wm8994_write(wm8994, reg, 2, &val); @@ -137,6 +136,29 @@ int wm8994_reg_write(struct wm8994 *wm8994, unsigned short reg, EXPORT_SYMBOL_GPL(wm8994_reg_write); /** + * wm8994_bulk_write: Write multiple WM8994 registers + * + * @wm8994: Device to write to + * @reg: First register + * @count: Number of registers + * @buf: Buffer to write from. Data must be big-endian formatted. + */ +int wm8994_bulk_write(struct wm8994 *wm8994, unsigned short reg, + int count, const u16 *buf) +{ + int ret; + + mutex_lock(&wm8994->io_lock); + + ret = wm8994_write(wm8994, reg, count * 2, buf); + + mutex_unlock(&wm8994->io_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(wm8994_bulk_write); + +/** * wm8994_set_bits: Set the value of a bitfield in a WM8994 register * * @wm8994: Device to write to. @@ -156,9 +178,13 @@ int wm8994_set_bits(struct wm8994 *wm8994, unsigned short reg, if (ret < 0) goto out; + r = be16_to_cpu(r); + r &= ~mask; r |= val; + r = cpu_to_be16(r); + ret = wm8994_write(wm8994, reg, 2, &r); out: @@ -169,8 +195,16 @@ out: EXPORT_SYMBOL_GPL(wm8994_set_bits); static struct mfd_cell wm8994_regulator_devs[] = { - { .name = "wm8994-ldo", .id = 1 }, - { .name = "wm8994-ldo", .id = 2 }, + { + .name = "wm8994-ldo", + .id = 1, + .pm_runtime_no_callbacks = true, + }, + { + .name = "wm8994-ldo", + .id = 2, + .pm_runtime_no_callbacks = true, + }, }; static struct resource wm8994_codec_resources[] = { @@ -200,6 +234,7 @@ static struct mfd_cell wm8994_devs[] = { .name = "wm8994-gpio", .num_resources = ARRAY_SIZE(wm8994_gpio_resources), .resources = wm8994_gpio_resources, + .pm_runtime_no_callbacks = true, }, }; @@ -218,12 +253,34 @@ static const char *wm8994_main_supplies[] = { "SPKVDD2", }; +static const char *wm8958_main_supplies[] = { + "DBVDD1", + "DBVDD2", + "DBVDD3", + "DCVDD", + "AVDD1", + "AVDD2", + "CPVDD", + "SPKVDD1", + "SPKVDD2", +}; + #ifdef CONFIG_PM -static int wm8994_device_suspend(struct device *dev) +static int wm8994_suspend(struct device *dev) { struct wm8994 *wm8994 = dev_get_drvdata(dev); int ret; + /* Don't actually go through with the suspend if the CODEC is + * still active (eg, for audio passthrough from CP. */ + ret = wm8994_reg_read(wm8994, WM8994_POWER_MANAGEMENT_1); + if (ret < 0) { + dev_err(dev, "Failed to read power status: %d\n", ret); + } else if (ret & WM8994_VMID_SEL_MASK) { + dev_dbg(dev, "CODEC still active, ignoring suspend\n"); + return 0; + } + /* GPIO configuration state is saved here since we may be configuring * the GPIO alternate functions even if we're not using the gpiolib * driver for them. @@ -239,7 +296,14 @@ static int wm8994_device_suspend(struct device *dev) if (ret < 0) dev_err(dev, "Failed to save LDO registers: %d\n", ret); - ret = regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), + /* Explicitly put the device into reset in case regulators + * don't get disabled in order to ensure consistent restart. + */ + wm8994_reg_write(wm8994, WM8994_SOFTWARE_RESET, 0x8994); + + wm8994->suspended = true; + + ret = regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(dev, "Failed to disable supplies: %d\n", ret); @@ -249,12 +313,16 @@ static int wm8994_device_suspend(struct device *dev) return 0; } -static int wm8994_device_resume(struct device *dev) +static int wm8994_resume(struct device *dev) { struct wm8994 *wm8994 = dev_get_drvdata(dev); int ret; - ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies), + /* We may have lied to the PM core about suspending */ + if (!wm8994->suspended) + return 0; + + ret = regulator_bulk_enable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(dev, "Failed to enable supplies: %d\n", ret); @@ -276,6 +344,8 @@ static int wm8994_device_resume(struct device *dev) if (ret < 0) dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + wm8994->suspended = false; + return 0; } #endif @@ -305,9 +375,10 @@ static int wm8994_ldo_in_use(struct wm8994_pdata *pdata, int ldo) /* * Instantiate the generic non-control parts of the device. */ -static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) +static int wm8994_device_init(struct wm8994 *wm8994, int irq) { struct wm8994_pdata *pdata = wm8994->dev->platform_data; + const char *devname; int ret, i; mutex_init(&wm8994->io_lock); @@ -323,25 +394,48 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) goto err; } + switch (wm8994->type) { + case WM8994: + wm8994->num_supplies = ARRAY_SIZE(wm8994_main_supplies); + break; + case WM8958: + wm8994->num_supplies = ARRAY_SIZE(wm8958_main_supplies); + break; + default: + BUG(); + return -EINVAL; + } + wm8994->supplies = kzalloc(sizeof(struct regulator_bulk_data) * - ARRAY_SIZE(wm8994_main_supplies), + wm8994->num_supplies, GFP_KERNEL); if (!wm8994->supplies) { ret = -ENOMEM; goto err; } - for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++) - wm8994->supplies[i].supply = wm8994_main_supplies[i]; - - ret = regulator_bulk_get(wm8994->dev, ARRAY_SIZE(wm8994_main_supplies), + switch (wm8994->type) { + case WM8994: + for (i = 0; i < ARRAY_SIZE(wm8994_main_supplies); i++) + wm8994->supplies[i].supply = wm8994_main_supplies[i]; + break; + case WM8958: + for (i = 0; i < ARRAY_SIZE(wm8958_main_supplies); i++) + wm8994->supplies[i].supply = wm8958_main_supplies[i]; + break; + default: + BUG(); + return -EINVAL; + } + + ret = regulator_bulk_get(wm8994->dev, wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to get supplies: %d\n", ret); goto err_supplies; } - ret = regulator_bulk_enable(ARRAY_SIZE(wm8994_main_supplies), + ret = regulator_bulk_enable(wm8994->num_supplies, wm8994->supplies); if (ret != 0) { dev_err(wm8994->dev, "Failed to enable supplies: %d\n", ret); @@ -353,7 +447,22 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) dev_err(wm8994->dev, "Failed to read ID register\n"); goto err_enable; } - if (ret != 0x8994) { + switch (ret) { + case 0x8994: + devname = "WM8994"; + if (wm8994->type != WM8994) + dev_warn(wm8994->dev, "Device registered as type %d\n", + wm8994->type); + wm8994->type = WM8994; + break; + case 0x8958: + devname = "WM8958"; + if (wm8994->type != WM8958) + dev_warn(wm8994->dev, "Device registered as type %d\n", + wm8994->type); + wm8994->type = WM8958; + break; + default: dev_err(wm8994->dev, "Device is not a WM8994, ID is %x\n", ret); ret = -EINVAL; @@ -370,14 +479,16 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) switch (ret) { case 0: case 1: - dev_warn(wm8994->dev, "revision %c not fully supported\n", - 'A' + ret); + if (wm8994->type == WM8994) + dev_warn(wm8994->dev, + "revision %c not fully supported\n", + 'A' + ret); break; default: - dev_info(wm8994->dev, "revision %c\n", 'A' + ret); break; } + dev_info(wm8994->dev, "%s revision %c\n", devname, 'A' + ret); if (pdata) { wm8994->irq_base = pdata->irq_base; @@ -418,15 +529,18 @@ static int wm8994_device_init(struct wm8994 *wm8994, unsigned long id, int irq) goto err_irq; } + pm_runtime_enable(wm8994->dev); + pm_runtime_resume(wm8994->dev); + return 0; err_irq: wm8994_irq_exit(wm8994); err_enable: - regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), + regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); err_get: - regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies); + regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); err_supplies: kfree(wm8994->supplies); err: @@ -437,11 +551,12 @@ err: static void wm8994_device_exit(struct wm8994 *wm8994) { + pm_runtime_disable(wm8994->dev); mfd_remove_devices(wm8994->dev); wm8994_irq_exit(wm8994); - regulator_bulk_disable(ARRAY_SIZE(wm8994_main_supplies), + regulator_bulk_disable(wm8994->num_supplies, wm8994->supplies); - regulator_bulk_free(ARRAY_SIZE(wm8994_main_supplies), wm8994->supplies); + regulator_bulk_free(wm8994->num_supplies, wm8994->supplies); kfree(wm8994->supplies); kfree(wm8994); } @@ -467,25 +582,29 @@ static int wm8994_i2c_read_device(struct wm8994 *wm8994, unsigned short reg, return 0; } -/* Currently we allocate the write buffer on the stack; this is OK for - * small writes - if we need to do large writes this will need to be - * revised. - */ static int wm8994_i2c_write_device(struct wm8994 *wm8994, unsigned short reg, - int bytes, void *src) + int bytes, const void *src) { struct i2c_client *i2c = wm8994->control_data; - unsigned char msg[bytes + 2]; + struct i2c_msg xfer[2]; int ret; reg = cpu_to_be16(reg); - memcpy(&msg[0], ®, 2); - memcpy(&msg[2], src, bytes); - ret = i2c_master_send(i2c, msg, bytes + 2); + xfer[0].addr = i2c->addr; + xfer[0].flags = 0; + xfer[0].len = 2; + xfer[0].buf = (char *)® + + xfer[1].addr = i2c->addr; + xfer[1].flags = I2C_M_NOSTART; + xfer[1].len = bytes; + xfer[1].buf = (char *)src; + + ret = i2c_transfer(i2c->adapter, xfer, 2); if (ret < 0) return ret; - if (ret < bytes + 2) + if (ret != 2) return -EIO; return 0; @@ -506,8 +625,9 @@ static int wm8994_i2c_probe(struct i2c_client *i2c, wm8994->read_dev = wm8994_i2c_read_device; wm8994->write_dev = wm8994_i2c_write_device; wm8994->irq = i2c->irq; + wm8994->type = id->driver_data; - return wm8994_device_init(wm8994, id->driver_data, i2c->irq); + return wm8994_device_init(wm8994, i2c->irq); } static int wm8994_i2c_remove(struct i2c_client *i2c) @@ -519,36 +639,24 @@ static int wm8994_i2c_remove(struct i2c_client *i2c) return 0; } -#ifdef CONFIG_PM -static int wm8994_i2c_suspend(struct i2c_client *i2c, pm_message_t state) -{ - return wm8994_device_suspend(&i2c->dev); -} - -static int wm8994_i2c_resume(struct i2c_client *i2c) -{ - return wm8994_device_resume(&i2c->dev); -} -#else -#define wm8994_i2c_suspend NULL -#define wm8994_i2c_resume NULL -#endif - static const struct i2c_device_id wm8994_i2c_id[] = { - { "wm8994", 0 }, + { "wm8994", WM8994 }, + { "wm8958", WM8958 }, { } }; MODULE_DEVICE_TABLE(i2c, wm8994_i2c_id); +static UNIVERSAL_DEV_PM_OPS(wm8994_pm_ops, wm8994_suspend, wm8994_resume, + NULL); + static struct i2c_driver wm8994_i2c_driver = { .driver = { - .name = "wm8994", - .owner = THIS_MODULE, + .name = "wm8994", + .owner = THIS_MODULE, + .pm = &wm8994_pm_ops, }, .probe = wm8994_i2c_probe, .remove = wm8994_i2c_remove, - .suspend = wm8994_i2c_suspend, - .resume = wm8994_i2c_resume, .id_table = wm8994_i2c_id, }; diff --git a/drivers/mfd/wm8994-irq.c b/drivers/mfd/wm8994-irq.c index 8400eb1..71c6e8f 100644 --- a/drivers/mfd/wm8994-irq.c +++ b/drivers/mfd/wm8994-irq.c @@ -156,16 +156,16 @@ static inline struct wm8994_irq_data *irq_to_wm8994_irq(struct wm8994 *wm8994, return &wm8994_irqs[irq - wm8994->irq_base]; } -static void wm8994_irq_lock(unsigned int irq) +static void wm8994_irq_lock(struct irq_data *data) { - struct wm8994 *wm8994 = get_irq_chip_data(irq); + struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); mutex_lock(&wm8994->irq_lock); } -static void wm8994_irq_sync_unlock(unsigned int irq) +static void wm8994_irq_sync_unlock(struct irq_data *data) { - struct wm8994 *wm8994 = get_irq_chip_data(irq); + struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); int i; for (i = 0; i < ARRAY_SIZE(wm8994->irq_masks_cur); i++) { @@ -182,28 +182,30 @@ static void wm8994_irq_sync_unlock(unsigned int irq) mutex_unlock(&wm8994->irq_lock); } -static void wm8994_irq_unmask(unsigned int irq) +static void wm8994_irq_enable(struct irq_data *data) { - struct wm8994 *wm8994 = get_irq_chip_data(irq); - struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq); + struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); + struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, + data->irq); wm8994->irq_masks_cur[irq_data->reg - 1] &= ~irq_data->mask; } -static void wm8994_irq_mask(unsigned int irq) +static void wm8994_irq_disable(struct irq_data *data) { - struct wm8994 *wm8994 = get_irq_chip_data(irq); - struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, irq); + struct wm8994 *wm8994 = irq_data_get_irq_chip_data(data); + struct wm8994_irq_data *irq_data = irq_to_wm8994_irq(wm8994, + data->irq); wm8994->irq_masks_cur[irq_data->reg - 1] |= irq_data->mask; } static struct irq_chip wm8994_irq_chip = { - .name = "wm8994", - .bus_lock = wm8994_irq_lock, - .bus_sync_unlock = wm8994_irq_sync_unlock, - .mask = wm8994_irq_mask, - .unmask = wm8994_irq_unmask, + .name = "wm8994", + .irq_bus_lock = wm8994_irq_lock, + .irq_bus_sync_unlock = wm8994_irq_sync_unlock, + .irq_disable = wm8994_irq_disable, + .irq_enable = wm8994_irq_enable, }; /* The processing of the primary interrupt occurs in a thread so that @@ -223,9 +225,11 @@ static irqreturn_t wm8994_irq_thread(int irq, void *data) return IRQ_NONE; } - /* Apply masking */ - for (i = 0; i < WM8994_NUM_IRQ_REGS; i++) + /* Bit swap and apply masking */ + for (i = 0; i < WM8994_NUM_IRQ_REGS; i++) { + status[i] = be16_to_cpu(status[i]); status[i] &= ~wm8994->irq_masks_cur[i]; + } /* Report */ for (i = 0; i < ARRAY_SIZE(wm8994_irqs); i++) { @@ -274,17 +278,17 @@ int wm8994_irq_init(struct wm8994 *wm8994) for (cur_irq = wm8994->irq_base; cur_irq < ARRAY_SIZE(wm8994_irqs) + wm8994->irq_base; cur_irq++) { - set_irq_chip_data(cur_irq, wm8994); - set_irq_chip_and_handler(cur_irq, &wm8994_irq_chip, + irq_set_chip_data(cur_irq, wm8994); + irq_set_chip_and_handler(cur_irq, &wm8994_irq_chip, handle_edge_irq); - set_irq_nested_thread(cur_irq, 1); + irq_set_nested_thread(cur_irq, 1); /* ARM needs us to explicitly flag the IRQ as valid * and will set them noprobe when we do so. */ #ifdef CONFIG_ARM set_irq_flags(cur_irq, IRQF_VALID); #else - set_irq_noprobe(cur_irq); + irq_set_noprobe(cur_irq); #endif } |