From ebb3f320edcc6c4665a71ea060ef2ce6a83402b0 Mon Sep 17 00:00:00 2001 From: merge Date: Mon, 8 Dec 2008 22:50:52 +0000 Subject: MERGE-via-pending-tracking-hist-MERGE-via-stable-tracking-fix-s3c2410_ts-fifo-allocation-1228776491 pending-tracking-hist top was MERGE-via-stable-tracking-fix-s3c2410_ts-fifo-allocation-1228776491 / a85a8a282939b4f6800081f67e1d568e0b97bd7a ... parent commitmessage: From: merge MERGE-via-stable-tracking-hist-fix-s3c2410_ts-fifo-allocation stable-tracking-hist top was fix-s3c2410_ts-fifo-allocation / 56a57ba0d4c1d60869250d5f89fae61544f01012 ... parent commitmessage: From: Nelson Castillo Fix s3c2410_ts FIFO allocation When I added the FIFO improving the interrupts handlers I introduced a bug. The FIFO is allocated after the interrupts are requested. This makes the kernel crash if the touchscreen generates activity before the allocation takes place. This patch fixes the bug. I reproduced it and tested the fix in a GTA02. - Fix bug - Fix a typo Reported-by: Andy Green Signed-off-by: Nelson Castillo --- arch/arm/mach-s3c2440/mach-gta02.c | 92 +++++++++++++++++++++++++--------- drivers/input/touchscreen/s3c2410_ts.c | 19 +++---- drivers/mfd/pcf50633-core.c | 74 ++++++++++++++++----------- drivers/power/pcf50633-charger.c | 81 ++++++++++++++++++++++++++++++ include/linux/mfd/pcf50633/core.h | 7 ++- include/linux/mfd/pcf50633/mbc.h | 13 +++++ include/linux/ts_filter.h | 2 +- 7 files changed, 222 insertions(+), 66 deletions(-) diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c index e90f82bdfc3..d2556ed3a3a 100644 --- a/arch/arm/mach-s3c2440/mach-gta02.c +++ b/arch/arm/mach-s3c2440/mach-gta02.c @@ -557,6 +557,9 @@ static void gta02_charger_worker(struct work_struct *work) #define GTA02_CHARGER_CONFIGURE_TIMEOUT ((3000 * HZ) / 1000) static void gta02_pmu_event_callback(struct pcf50633 *pcf, int irq) { + int ret; + int happy_with_power = 0; + if (irq == PCF50633_IRQ_USBINS) { schedule_delayed_work(>a02_charger_work, GTA02_CHARGER_CONFIGURE_TIMEOUT); @@ -564,6 +567,44 @@ static void gta02_pmu_event_callback(struct pcf50633 *pcf, int irq) } else if (irq == PCF50633_IRQ_USBREM) { cancel_delayed_work_sync(>a02_charger_work); gta02_usb_vbus_draw = 0; + } else if (irq == PCF50633_ABOUT_TO_INCREASE_POWER) { + + /* + * we can't let this proceed until we are happy about + * power arrangements, because it is going to bring up + * the backlight and double our power consumption. + * + * With Qi/A6 anyway, until now we could boot just from + * USB 100mA limit and no or low battery. So we need + * to check for low battery and if so, spin here until + * USB enumerated power, or charger comes + */ + + while (!happy_with_power) { + ret = pcf50633_check_power_available(pcf); +// dev_info(pcf->dev, "available power = %d\n", ret); + switch (ret) { + case PCF50633_PA_DEAD_BATTERY_ONLY: + /* turn ourselves off */ + pcf50633_reg_write(pcf, PCF50633_REG_OOSHDWN, + 0x01); + break; + case PCF50633_PA_USB_100mA_AND_DEAD_BATTERY: + /* + * loop waiting for eg 500mA USB + * enumeration by host + */ + break; + default: + /* + * one way or another we can afford to + * use more power + */ + happy_with_power = 1; + continue; + } + msleep(1000); + } } } @@ -628,6 +669,7 @@ static char *gta02_batteries[] = { "battery", }; + struct pcf50633_platform_data gta02_pcf_pdata = { .resumers = { [0] = PCF50633_INT1_USBINS | @@ -765,6 +807,7 @@ struct pcf50633_platform_data gta02_pcf_pdata = { .probe_done = gta02_pmu_attach_child_devices, .regulator_registered = gta02_pmu_regulator_registered, .mbc_event_callback = gta02_pmu_event_callback, + .good_main_battery_adc_threshold = 500 }; static void mangle_pmu_pdata_by_system_rev(void) @@ -1004,6 +1047,8 @@ static void gta02_udc_vbus_draw(unsigned int ma) return; } + printk(KERN_ERR "******** UDC gta02_pcf_pdata.pcf ma = %d\n", ma); + gta02_usb_vbus_draw = ma; schedule_delayed_work(>a02_charger_work, @@ -1608,20 +1653,7 @@ __setup("hardware_ecc=", hardware_ecc_setup); /* these are the guys that don't need to be children of PMU */ static struct platform_device *gta02_devices[] __initdata = { - >a02_version_device, - &s3c_device_usb, - &s3c_device_wdt, - >a02_memconfig_device, - &s3c_device_sdi, &s3c_device_usbgadget, - &s3c_device_nand, - >a02_nor_flash, - - &sc32440_fiq_device, - &s3c24xx_pwm_device, - >a02_led_dev, - >a02_pm_wlan_dev, /* not dependent on PMU */ - &s3c_device_iis, &s3c_device_i2c0, }; @@ -1629,6 +1661,15 @@ static struct platform_device *gta02_devices[] __initdata = { /* these guys DO need to be children of PMU */ static struct platform_device *gta02_devices_pmu_children[] = { + &s3c_device_usb, + &sc32440_fiq_device, + &s3c24xx_pwm_device, + &s3c_device_nand, + >a02_nor_flash, + >a02_pm_wlan_dev, /* not dependent on PMU */ + >a02_version_device, + &s3c_device_wdt, + >a02_memconfig_device, &s3c_device_ts, /* input 1 */ >a02_pm_gsm_dev, >a02_pm_usbhost_dev, @@ -1636,6 +1677,8 @@ static struct platform_device *gta02_devices_pmu_children[] = { &s3c_device_spi_acc2, /* input 3 */ >a02_button_dev, /* input 4 */ >a02_resume_reason_device, + &s3c_device_sdi, + >a02_led_dev, }; static void gta02_pmu_regulator_registered(struct pcf50633 *pcf, int id) @@ -1645,23 +1688,24 @@ static void gta02_pmu_regulator_registered(struct pcf50633 *pcf, int id) regulator = pcf->pmic.pdev[id]; switch(id) { - case PCF50633_REGULATOR_LDO4: - pdev = >a01_pm_bt_dev; - break; - case PCF50633_REGULATOR_LDO5: - pdev = >a01_pm_gps_dev; - break; - case PCF50633_REGULATOR_HCLDO: - pdev = >a02_glamo_dev; - break; - default: - return; + case PCF50633_REGULATOR_LDO4: + pdev = >a01_pm_bt_dev; + break; + case PCF50633_REGULATOR_LDO5: + pdev = >a01_pm_gps_dev; + break; + case PCF50633_REGULATOR_HCLDO: + pdev = >a02_glamo_dev; + break; + default: + return; } pdev->dev.parent = ®ulator->dev; platform_device_register(pdev); } + /* this is called when pc50633 is probed, unfortunately quite late in the * day since it is an I2C bus device. Here we can belatedly define some * platform devices with the advantage that we can mark the pcf50633 as the diff --git a/drivers/input/touchscreen/s3c2410_ts.c b/drivers/input/touchscreen/s3c2410_ts.c index 8e6bc0a0371..4159adaec7c 100644 --- a/drivers/input/touchscreen/s3c2410_ts.c +++ b/drivers/input/touchscreen/s3c2410_ts.c @@ -427,6 +427,11 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) ts.dev->id.product = 0xBEEF; ts.dev->id.version = S3C2410TSVERSION; ts.state = TS_STATE_STANDBY; + ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL); + if (IS_ERR(ts.event_fifo)) { + ret = -EIO; + goto bail2; + } /* create the filter chain set up for the 2 coordinates we produce */ ret = ts_filter_create_chain( @@ -459,26 +464,17 @@ static int __init s3c2410ts_probe(struct platform_device *pdev) goto bail4; } - ts.event_fifo = kfifo_alloc(TS_EVENT_FIFO_SIZE, GFP_KERNEL, NULL); - - if (IS_ERR(ts.event_fifo)) { - ret = -EIO; - goto bail5; - } - dev_info(&pdev->dev, "successfully loaded\n"); /* All went ok, so register to the input system */ rc = input_register_device(ts.dev); if (rc) { ret = -EIO; - goto bail6; + goto bail5; } return 0; -bail6: - kfifo_free(ts.event_fifo); bail5: free_irq(IRQ_TC, ts.dev); free_irq(IRQ_ADC, ts.dev); @@ -489,7 +485,8 @@ bail4: disable_irq(IRQ_ADC); bail3: ts_filter_destroy_chain(ts.tsf); - + kfifo_free(ts.event_fifo); +bail2: input_unregister_device(ts.dev); bail1: iounmap(base_addr); diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index 6634558ea91..2079d303000 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -28,6 +28,7 @@ #include #include #include +#include #include @@ -292,10 +293,11 @@ static void pcf50633_irq_worker(struct work_struct *work) pcf_int[0] &= ~(1 << PCF50633_INT1_ADPINS); } +#if 0 dev_info(pcf->dev, "INT1=0x%02x INT2=0x%02x INT3=0x%02x " "INT4=0x%02x INT5=0x%02x\n", pcf_int[0], pcf_int[1], pcf_int[2], pcf_int[3], pcf_int[4]); - +#endif /* Some revisions of the chip don't have a 8s standby mode on * ONKEY1S press. We try to manually do it in such cases. */ @@ -377,8 +379,6 @@ static irqreturn_t pcf50633_irq(int irq, void *data) { struct pcf50633 *pcf = data; - printk(KERN_ERR "pcf50633_irq\n"); - get_device(pcf->dev); disable_irq(pcf->irq); @@ -474,15 +474,17 @@ static int pcf50633_resume(struct device *dev) #define pcf50633_resume NULL #endif + static int pcf50633_probe(struct i2c_client *client, const struct i2c_device_id *ids) { struct pcf50633 *pcf; struct pcf50633_platform_data *pdata; - int i, ret = 0; - u8 mbcs1; + int ret = 0; int version; int variant; + u8 mbcs1; + int i; pdata = client->dev.platform_data; @@ -518,7 +520,7 @@ static int pcf50633_probe(struct i2c_client *client, dev_info(pcf->dev, "Probed device version %d variant %d\n", version, variant); - /* Enable all inteerupts except RTC SECOND */ + /* Enable all interrupts except RTC SECOND */ pcf->mask_regs[0] = 0x80; pcf50633_reg_write(pcf, PCF50633_REG_INT1M, 0x80); @@ -535,22 +537,15 @@ static int pcf50633_probe(struct i2c_client *client, &pcf->mbc.pdev); pcf50633_client_dev_register(pcf, "pcf50633-adc", &pcf->adc.pdev); - for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { - struct platform_device *pdev; - pdev = platform_device_alloc("pcf50633-regltr", i); - if (!pdev) { - dev_err(pcf->dev, "Cannot create regulator\n"); - continue; - } + /* Cold Intialization */ + mbcs1 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS1); - pdev->dev.parent = pcf->dev; - pdev->dev.platform_data = &pdata->reg_init_data[i]; - pdev->dev.driver_data = pcf; - pcf->pmic.pdev[i] = pdev; + if (mbcs1 & 0x01) + pcf50633_irq_call_handler(pcf, PCF50633_IRQ_USBINS); + if (mbcs1 & 0x04) + pcf50633_irq_call_handler(pcf, PCF50633_IRQ_ADPINS); - platform_device_add(pdev); - } pcf->irq = client->irq; @@ -571,20 +566,41 @@ static int pcf50633_probe(struct i2c_client *client, dev_err(pcf->dev, "IRQ %u cannot be enabled as wake-up " "source in this hardware revision\n", client->irq); - /* Cold Intialization */ - mbcs1 = pcf50633_reg_read(pcf, PCF50633_REG_MBCS1); - - if (mbcs1 & 0x01) - pcf50633_irq_call_handler(pcf, PCF50633_IRQ_USBINS); - if (mbcs1 & 0x04) - pcf50633_irq_call_handler(pcf, PCF50633_IRQ_ADPINS); - ret = sysfs_create_group(&client->dev.kobj, &pcf_attr_group); if (ret) dev_err(pcf->dev, "error creating sysfs entries\n"); - if (pdata->probe_done) - pdata->probe_done(pcf); + /* + * give the system a chance to spin until power situation becomes + * good enough to continue boot. USB-based systems are only allowed to + * pull 100mA until enumerated when they can have up to 500mA for + * example + */ + + if (pcf->pdata->mbc_event_callback) + pcf->pdata->mbc_event_callback(pcf, + PCF50633_ABOUT_TO_INCREASE_POWER); + + for (i = 0; i < PCF50633_NUM_REGULATORS; i++) { + struct platform_device *pdev; + + pdev = platform_device_alloc("pcf50633-regltr", i); + if (!pdev) { + dev_err(pcf->dev, "Cannot create regulator\n"); + continue; + } + + pdev->dev.parent = pcf->dev; + pdev->dev.platform_data = &pcf->pdata->reg_init_data[i]; + pdev->dev.driver_data = pcf; + pcf->pmic.pdev[i] = pdev; + + platform_device_add(pdev); + } + + if (pcf->pdata->probe_done) + pcf->pdata->probe_done(pcf); + return 0; diff --git a/drivers/power/pcf50633-charger.c b/drivers/power/pcf50633-charger.c index 9bbfaac48a1..8e302c6c40e 100644 --- a/drivers/power/pcf50633-charger.c +++ b/drivers/power/pcf50633-charger.c @@ -23,9 +23,90 @@ * MA 02111-1307 USA */ +#include #include #include +/* this tells us the answer to "how am I set for power?" */ + +enum pcf50633_power_avail pcf50633_check_power_available(struct pcf50633 *pcf) +{ + int ret; + int battery_ok; + int usb_present; + + /* first look for adapter and USB power status */ + + ret = pcf50633_reg_read(pcf, PCF50633_REG_MBCS1); + if (ret < 0) + return ret; + + if (ret & 8) /* adapter power present */ + return PCF50633_PA_ADAPTER; /* don't care about anything else */ + + usb_present = ret & 2; + + /* measure battery using PMU ADC */ + /* disable charging momentarily so we can measure battery */ + + ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, 1, 0); + if (ret < 0) + return ret; + + ret = pcf50633_adc_sync_read(pcf, PCF50633_ADCC1_MUX_BATSNS_RES, + PCF50633_ADCC1_AVERAGE_16); + if (ret < 0) + return ret; + + battery_ok = (ret > pcf->pdata->good_main_battery_adc_threshold); + + /* enable charging again */ + + ret = pcf50633_reg_set_bit_mask(pcf, PCF50633_REG_MBCC1, 1, 1); + if (ret < 0) + return ret; + + /* + * now we know battery and USB situation + * make the decision now if possible + */ + + if (!usb_present && !battery_ok) + return PCF50633_PA_DEAD_BATTERY_ONLY; + + if (!usb_present && battery_ok) + return PCF50633_PA_LIVE_BATTERY_ONLY; + + /* So USB is present... what current limit? */ + + ret = pcf50633_reg_read(pcf, PCF50633_REG_MBCC7); + if (ret < 0) + return ret; + + switch (ret & PCF50633_MBCC7_USB_MASK) { + + case PCF50633_MBCC7_USB_100mA: + if (battery_ok) + return PCF50633_PA_USB_100mA_AND_LIVE_BATTERY; + else + return PCF50633_PA_USB_100mA_AND_DEAD_BATTERY; + + case PCF50633_MBCC7_USB_500mA: + return PCF50633_PA_USB_500mA; + + case PCF50633_MBCC7_USB_1000mA: + return PCF50633_PA_USB_1A; + + default: + if (battery_ok) + return PCF50633_PA_LIVE_BATTERY_ONLY; + else + return PCF50633_PA_DEAD_BATTERY_ONLY; + } + + return PCF50633_PA_DEAD_BATTERY_ONLY; +} + void pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma) { int ret; diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index bf107d3794e..4ee3f527172 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -34,6 +34,8 @@ struct pcf50633_platform_data { char **batteries; int num_batteries; + int good_main_battery_adc_threshold; + /* Callbacks */ void (*probe_done)(struct pcf50633 *); void (*mbc_event_callback)(struct pcf50633 *, int); @@ -78,6 +80,7 @@ int pcf50633_reg_clear_bits(struct pcf50633 *pcf, u8 reg, u8 bits); #define PCF50633_REG_INT3M 0x09 #define PCF50633_REG_INT4M 0x0a #define PCF50633_REG_INT5M 0x0b +#define PCF50633_REG_OOSHDWN 0x0c enum { /* Chip IRQs */ @@ -122,8 +125,10 @@ enum { PCF50633_IRQ_HCLDOPWRFAIL, PCF50633_IRQ_HCLDOOVL, - /* Always last */ + /* Always last of real IRQs */ PCF50633_NUM_IRQ, + /* fake IRQ */ + PCF50633_ABOUT_TO_INCREASE_POWER }; struct pcf50633 { diff --git a/include/linux/mfd/pcf50633/mbc.h b/include/linux/mfd/pcf50633/mbc.h index 9a7938ad132..a1cd44957ac 100644 --- a/include/linux/mfd/pcf50633/mbc.h +++ b/include/linux/mfd/pcf50633/mbc.h @@ -15,6 +15,7 @@ #include +#define PCF50633_REG_BVMCTL 0x19 #define PCF50633_REG_MBCC1 0x43 #define PCF50633_REG_MBCC2 0x44 #define PCF50633_REG_MBCC3 0x45 @@ -122,6 +123,18 @@ struct pcf50633; void pcf50633_mbc_usb_curlim_set(struct pcf50633 *pcf, int ma); +enum pcf50633_power_avail { + PCF50633_PA_DEAD_BATTERY_ONLY, + PCF50633_PA_LIVE_BATTERY_ONLY, + PCF50633_PA_ADAPTER, + PCF50633_PA_USB_100mA_AND_LIVE_BATTERY, + PCF50633_PA_USB_100mA_AND_DEAD_BATTERY, + PCF50633_PA_USB_500mA, + PCF50633_PA_USB_1A +}; + +enum pcf50633_power_avail pcf50633_check_power_available(struct pcf50633 *pcf); + struct pcf50633_mbc { int adapter_active; int adapter_online; diff --git a/include/linux/ts_filter.h b/include/linux/ts_filter.h index bfb8a221964..715f1badbf4 100644 --- a/include/linux/ts_filter.h +++ b/include/linux/ts_filter.h @@ -8,7 +8,7 @@ */ #define MAX_TS_FILTER_CHAIN 4 /* max filters you can chain up */ -#define MAX_TS_FILTER_COORDS 3 /* Y, Y and Z (pressure) */ +#define MAX_TS_FILTER_COORDS 3 /* X, Y and Z (pressure) */ struct ts_filter; -- cgit v1.2.3