From 1e816753721365b52c6aa63b0deabc3ccd648449 Mon Sep 17 00:00:00 2001 From: Andy Park Date: Tue, 1 May 2012 09:03:43 -0700 Subject: tracing: Add tracepoints for CPU scaling Simple tracepoints for measuring CPU scaling latencies. Bug 960307 Change-Id: I470994e9542f17f8ffadf53d162382ae86bd05c5 Reviewed-on: http://git-master/r/99808 Reviewed-by: Andy Park Tested-by: Andy Park Reviewed-by: Prajakta Gudadhe --- drivers/cpufreq/cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 96adadf59e9e..3acc42805e21 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1449,8 +1449,10 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, pr_debug("target for CPU %u: %u kHz, relation %u\n", policy->cpu, target_freq, relation); + trace_cpu_scale(policy->cpu, policy->cur, POWER_CPU_SCALE_START); if (cpu_online(policy->cpu) && cpufreq_driver->target) retval = cpufreq_driver->target(policy, target_freq, relation); + trace_cpu_scale(policy->cpu, target_freq, POWER_CPU_SCALE_DONE); return retval; } -- cgit v1.2.3 From da0d098d0ecc5cb99b8367f7067b4e57604347de Mon Sep 17 00:00:00 2001 From: Kevin Huang Date: Mon, 23 Apr 2012 13:59:55 -0700 Subject: video: tegra: dsi: Ref-count pllp_out3 clock in DSI. Bug 933653 Change-Id: If7ce4dc5129782a7e3487028d2dba01c9380ba90 Signed-off-by: Kevin Huang Reviewed-on: http://git-master/r/98256 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Aleksandr Frid Reviewed-by: Animesh Kishore Reviewed-by: Yu-Huan Hsu --- drivers/video/tegra/dc/dsi.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c index 4b055ec529c6..def5b38b8b9d 100644 --- a/drivers/video/tegra/dc/dsi.c +++ b/drivers/video/tegra/dc/dsi.c @@ -109,6 +109,7 @@ struct tegra_dc_dsi_data { struct clk *dc_clk; struct clk *dsi_clk; + struct clk *dsi_fixed_clk; bool clk_ref; struct mutex lock; @@ -1453,6 +1454,7 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc, if (!dsi->clk_ref) { dsi->clk_ref = true; clk_enable(dsi->dsi_clk); + clk_enable(dsi->dsi_fixed_clk); tegra_periph_reset_deassert(dsi->dsi_clk); } dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000; @@ -2861,6 +2863,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) void __iomem *base; struct clk *dc_clk = NULL; struct clk *dsi_clk = NULL; + struct clk *dsi_fixed_clk = NULL; struct tegra_dsi_out *dsi_pdata; int err; @@ -2903,8 +2906,9 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) dsi_clk = clk_get(&dc->ndev->dev, "dsib"); else dsi_clk = clk_get(&dc->ndev->dev, "dsia"); + dsi_fixed_clk = clk_get(&dc->ndev->dev, "dsi-fixed"); - if (IS_ERR_OR_NULL(dsi_clk)) { + if (IS_ERR_OR_NULL(dsi_clk) || IS_ERR_OR_NULL(dsi_fixed_clk)) { dev_err(&dc->ndev->dev, "dsi: can't get clock\n"); err = -EBUSY; goto err_release_regs; @@ -2924,6 +2928,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) dsi->base_res = base_res; dsi->dc_clk = dc_clk; dsi->dsi_clk = dsi_clk; + dsi->dsi_fixed_clk = dsi_fixed_clk; err = tegra_dc_dsi_cp_info(dsi, dsi_pdata); if (err < 0) @@ -2937,6 +2942,7 @@ static int tegra_dc_dsi_init(struct tegra_dc *dc) err_dsi_data: err_clk_put: clk_put(dsi_clk); + clk_put(dsi_fixed_clk); err_release_regs: release_resource(base_res); err_free_dsi: @@ -3050,6 +3056,7 @@ static int tegra_dsi_deep_sleep(struct tegra_dc *dc, /* Disable dsi source clock */ clk_disable(dsi->dsi_clk); + clk_disable(dsi->dsi_fixed_clk); dsi->clk_ref = false; dsi->enabled = false; -- cgit v1.2.3 From 38c804ed53aae503693338e0135a506a81ce2c11 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 24 Apr 2012 19:03:26 +0530 Subject: gpio: tegra: implement gpio_request and gpio_free. Recent pinctrl discussions concluded that gpiolib APIs should in fact do whatever is required to mux a GPIO onto pins. This change is based on the work done by Stephen Warren in mainline kernel. ----- commit 3e215d0a19c2a0c389bd9117573b6dd8e46f96a8 gpio: tegra: Hide tegra_gpio_enable/disable() Recent pinctrl discussions concluded that gpiolib APIs should in fact do whatever is required to mux a GPIO onto pins, by calling pinctrl APIs if required. This change implements this for the Tegra GPIO driver, and removes calls to the Tegra-specific APIs from drivers and board files. ---- Change-Id: I482ea5c177cf2ee6fa06ddac48b556f1508efacb Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/98466 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Pritesh Raithatha Reviewed-by: Stephen Warren --- drivers/gpio/gpio-tegra.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 3a0893f9cdc3..17e9c1cfdf4e 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -191,6 +191,7 @@ static int tegra_gpio_get(struct gpio_chip *chip, unsigned offset) static int tegra_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 0); + tegra_gpio_enable(offset); return 0; } @@ -199,6 +200,7 @@ static int tegra_gpio_direction_output(struct gpio_chip *chip, unsigned offset, { tegra_gpio_set(chip, offset, value); tegra_gpio_mask_write(GPIO_MSK_OE(offset), offset, 1); + tegra_gpio_enable(offset); return 0; } @@ -213,8 +215,20 @@ static int tegra_gpio_to_irq(struct gpio_chip *chip, unsigned offset) return TEGRA_GPIO_TO_IRQ(offset); } +static int tegra_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return 0; +} + +static void tegra_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + tegra_gpio_disable(offset); +} + static struct gpio_chip tegra_gpio_chip = { .label = "tegra-gpio", + .request = tegra_gpio_request, + .free = tegra_gpio_free, .direction_input = tegra_gpio_direction_input, .get = tegra_gpio_get, .direction_output = tegra_gpio_direction_output, -- cgit v1.2.3 From 9c36a7d25b978864c25a5c5649cd803898872600 Mon Sep 17 00:00:00 2001 From: Hunk Lin Date: Thu, 3 May 2012 05:35:47 +0800 Subject: Revert "video: tegra: dc: Set default videomode" This reverts commit 9349cedf17f9b3c10760c8d48f831473f87a3a15. It is reviewed on http://git-master/r/99635 It will cause HDMI power ON and emc clock bump up to 667Mhz after resume from LP0. bug 930136 Change-Id: I130494fdb381b3d322ac0e3fc8be2e44f2c2d7a7 Signed-off-by: Hunk Lin Reviewed-on: http://git-master/r/100202 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Jon Mayo --- drivers/video/tegra/dc/dc.c | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 7161d8bf1694..11e62815f45a 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -64,20 +64,6 @@ #endif static int no_vsync; -static struct fb_videomode tegra_dc_hdmi_fallback_mode = { - .refresh = 60, - .xres = 640, - .yres = 480, - .pixclock = KHZ2PICOS(25200), - .hsync_len = 96, /* h_sync_width */ - .vsync_len = 2, /* v_sync_width */ - .left_margin = 48, /* h_back_porch */ - .upper_margin = 33, /* v_back_porch */ - .right_margin = 16, /* h_front_porch */ - .lower_margin = 10, /* v_front_porch */ - .vmode = 0, - .sync = 0, -}; static void _tegra_dc_controller_disable(struct tegra_dc *dc); @@ -2636,19 +2622,10 @@ static bool _tegra_dc_controller_reset_enable(struct tegra_dc *dc) } #endif -static int _tegra_dc_set_default_videomode(struct tegra_dc *dc) -{ - return tegra_dc_set_fb_mode(dc, &tegra_dc_hdmi_fallback_mode, 0); -} - static bool _tegra_dc_enable(struct tegra_dc *dc) { - if (dc->mode.pclk == 0) { - /* DC enable called but no videomode is loaded. - Set fallback mode to enable driver with.*/ - if (_tegra_dc_set_default_videomode(dc)) - return false; - } + if (dc->mode.pclk == 0) + return false; if (!dc->out) return false; -- cgit v1.2.3 From 09e0328c0bb6ed533949ec768f47cf2f8f765d46 Mon Sep 17 00:00:00 2001 From: Syed Rafiuddin Date: Fri, 13 Apr 2012 15:39:05 +0530 Subject: power: smb349: addition of regulator interface Addition of regulator interface to detect the usb cable plug/unplug detection. bug 974806 Reviewed-on: http://git-master/r/95063 (cherry picked from commit 0ff4f3f13f3751a7d861888094ff213b6022640e) Change-Id: If6522a56511549bb0faa45b0654fcb390d27af21 Signed-off-by: Syed Rafiuddin Signed-off-by: Chandler Zhang Signed-off-by: Shridhar Rasal Reviewed-on: http://git-master/r/99604 GVS: Gerrit_Virtual_Submit Reviewed-by: Bharat Nihalani --- drivers/power/smb349-charger.c | 254 +++++++++++++++++++---------------------- 1 file changed, 116 insertions(+), 138 deletions(-) (limited to 'drivers') diff --git a/drivers/power/smb349-charger.c b/drivers/power/smb349-charger.c index 1f230baadcb5..9ee8b288d225 100644 --- a/drivers/power/smb349-charger.c +++ b/drivers/power/smb349-charger.c @@ -84,7 +84,6 @@ static struct smb349_charger *charger; static int smb349_configure_charger(struct i2c_client *client, int value); -static int smb349_configure_interrupts(struct i2c_client *client); static int smb349_read(struct i2c_client *client, int reg) { @@ -158,16 +157,6 @@ int smb349_volatile_writes(struct i2c_client *client, uint8_t value) return ret; } -static void smb349_clear_interrupts(struct i2c_client *client) -{ - uint8_t val, buf[6]; - - val = i2c_smbus_read_i2c_block_data(client, SMB349_INTR_STS_A, 6, buf); - if (val < 0) - dev_err(&client->dev, "%s(): Failed in clearing interrupts\n", - __func__); -} - static int smb349_configure_otg(struct i2c_client *client, int enable) { int ret = 0; @@ -283,59 +272,6 @@ error: return ret; } -static irqreturn_t smb349_status_isr(int irq, void *dev_id) -{ - struct i2c_client *client = charger->client; - int ret, val; - - val = smb349_read(client, SMB349_STS_REG_D); - if (val < 0) { - dev_err(&client->dev, "%s(): Failed in reading register" - "0x%02x\n", __func__, SMB349_STS_REG_D); - goto irq_error; - } else if (val != 0) { - if (val & DEDICATED_CHARGER) - charger->chrg_type = AC; - else if (val & CHRG_DOWNSTRM_PORT) - charger->chrg_type = USB; - - /* configure charger */ - ret = smb349_configure_charger(client, 1); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring" - "charger..\n", __func__); - goto irq_error; - } - - charger->state = progress; - } else { - charger->state = stopped; - - /* Disable charger */ - ret = smb349_configure_charger(client, 0); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring" - "charger..\n", __func__); - goto irq_error; - } - - ret = smb349_configure_interrupts(client); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring" - "charger..\n", __func__); - goto irq_error; - } - - } - - if (charger->charger_cb) - charger->charger_cb(charger->state, charger->chrg_type, - charger->charger_cb_data); -irq_error: - smb349_clear_interrupts(client); - return IRQ_HANDLED; -} - int update_charger_status(void) { struct i2c_client *client = charger->client; @@ -349,28 +285,12 @@ int update_charger_status(void) } else if (val != 0) { if (val & DEDICATED_CHARGER) charger->chrg_type = AC; - else if (val & CHRG_DOWNSTRM_PORT) + else charger->chrg_type = USB; - /* configure charger */ - ret = smb349_configure_charger(client, 1); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring" - "charger..\n", __func__); - goto ret_error; - } - charger->state = progress; } else { charger->state = stopped; - - /* Disable charger */ - ret = smb349_configure_charger(client, 0); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring" - "charger..\n", __func__); - goto ret_error; - } } if (charger->charger_cb) @@ -413,43 +333,6 @@ int smb349_battery_online(void) return 1; } -static int smb349_configure_interrupts(struct i2c_client *client) -{ - int ret = 0; - - /* Enable volatile writes to registers */ - ret = smb349_volatile_writes(client, SMB349_ENABLE_WRITE); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring charger..\n", - __func__); - goto error; - } - - ret = smb349_update_reg(client, SMB349_FAULT_INTR, 0xff); - if (ret < 0) { - dev_err(&client->dev, "%s(): Failed in writing register" - "0x%02x\n", __func__, SMB349_CMD_REG); - goto error; - } - - ret = smb349_update_reg(client, SMB349_STS_INTR_1, 0xff); - if (ret < 0) { - dev_err(&client->dev, "%s: err %d\n", __func__, ret); - goto error; - } - - /* Disable volatile writes to registers */ - ret = smb349_volatile_writes(client, SMB349_DISABLE_WRITE); - if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring charger..\n", - __func__); - goto error; - } - -error: - return ret; -} - static void smb349_otg_status(enum usb_otg_state to, enum usb_otg_state from, void *data) { struct i2c_client *client = charger->client; @@ -482,18 +365,61 @@ static void smb349_otg_status(enum usb_otg_state to, enum usb_otg_state from, vo if (ret < 0) dev_err(&client->dev, "%s() error in configuring" "otg..\n", __func__); + } +} - ret = smb349_configure_interrupts(client); - if (ret < 0) +static int smb349_enable_charging(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct i2c_client *client = charger->client; + int ret; + + if (!max_uA) { + charger->state = stopped; + /* Disable charger */ + ret = smb349_configure_charger(client, 0); + if (ret < 0) { dev_err(&client->dev, "%s() error in configuring" - "otg..\n", __func__); + "charger..\n", __func__); + return ret; + } + } else { + ret = smb349_read(client, SMB349_STS_REG_D); + if (ret < 0) { + dev_err(&client->dev, "%s(): Failed in reading register" + "0x%02x\n", __func__, SMB349_STS_REG_D); + return ret; + } else if (ret != 0) { + if (ret & DEDICATED_CHARGER) + charger->chrg_type = AC; + else + charger->chrg_type = USB; + + /* configure charger */ + ret = smb349_configure_charger(client, 1); + if (ret < 0) { + dev_err(&client->dev, "%s() error in" + "configuring charger..\n", __func__); + return ret; + } + charger->state = progress; + } } + if (charger->charger_cb) + charger->charger_cb(charger->state, charger->chrg_type, + charger->charger_cb_data); +return 0; } +static struct regulator_ops smb349_tegra_regulator_ops = { + .set_current_limit = smb349_enable_charging, +}; + static int __devinit smb349_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct smb349_charger_platform_data *pdata; int ret, irq_num; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) @@ -505,6 +431,12 @@ static int __devinit smb349_probe(struct i2c_client *client, charger->client = client; charger->dev = &client->dev; + pdata = client->dev.platform_data; + if(!pdata) { + ret = -ENXIO; + goto error; + } + i2c_set_clientdata(client, charger); /* Check battery presence */ @@ -515,31 +447,77 @@ static int __devinit smb349_probe(struct i2c_client *client, goto error; } - ret = register_otg_callback(smb349_otg_status, charger); - if (ret < 0) + charger->reg_desc.name = "vbus_charger"; + charger->reg_desc.ops = &smb349_tegra_regulator_ops; + charger->reg_desc.type = REGULATOR_CURRENT; + charger->reg_desc.id = pdata->regulator_id; + charger->reg_desc.owner = THIS_MODULE; + + charger->reg_init_data.supply_regulator = NULL; + charger->reg_init_data.num_consumer_supplies = + pdata->num_consumer_supplies; + charger->reg_init_data.regulator_init = NULL; + charger->reg_init_data.consumer_supplies = + pdata->consumer_supplies; + charger->reg_init_data.driver_data = charger; + charger->reg_init_data.constraints.name = "vbus_charger"; + charger->reg_init_data.constraints.min_uA = 0; + charger->reg_init_data.constraints.max_uA = + pdata->max_charge_current_mA * 1000; + + charger->reg_init_data.constraints.valid_modes_mask = + REGULATOR_MODE_NORMAL | + REGULATOR_MODE_STANDBY; + + charger->reg_init_data.constraints.valid_ops_mask = + REGULATOR_CHANGE_MODE | + REGULATOR_CHANGE_STATUS | + REGULATOR_CHANGE_CURRENT; + + charger->rdev = regulator_register(&charger->reg_desc, charger->dev, + &charger->reg_init_data, charger); + if (IS_ERR(charger->rdev)) { + dev_err(&client->dev, "failed to register %s\n", + charger->reg_desc.name); + ret = PTR_ERR(charger->rdev); goto error; + } - ret = smb349_configure_charger(client, 1); - if (ret < 0) - return ret; - - ret = smb349_configure_interrupts(client); + /* disable OTG */ + ret = smb349_configure_otg(client, 0); if (ret < 0) { - dev_err(&client->dev, "%s() error in configuring charger..\n", - __func__); + dev_err(&client->dev, "%s() error in configuring" + "charger..\n", __func__); goto error; } - irq_num = gpio_to_irq(client->irq); - ret = request_threaded_irq(irq_num, - NULL, smb349_status_isr, IRQ_TYPE_EDGE_FALLING, - "smb349", charger); - if (ret) { - dev_err(&client->dev, "%s(): Failed in requesting isr\n", - __func__); + ret = smb349_read(client, SMB349_STS_REG_D); + if (ret < 0) { + dev_err(&client->dev, "%s(): Failed in reading register" + "0x%02x\n", __func__, SMB349_STS_REG_D); goto error; + } else if (ret != 0) { + /* configure charger */ + ret = smb349_configure_charger(client, 1); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring" + "charger..\n", __func__); + goto error; + } + } else { + /* disable charger */ + ret = smb349_configure_charger(client, 0); + if (ret < 0) { + dev_err(&client->dev, "%s() error in configuring" + "charger..\n", __func__); + goto error; + } } + ret = register_otg_callback(smb349_otg_status, charger); + if (ret < 0) + goto error; + return 0; error: kfree(charger); @@ -575,7 +553,7 @@ static int __init smb349_init(void) { return i2c_add_driver(&smb349_i2c_driver); } -module_init(smb349_init); +subsys_initcall(smb349_init); static void __exit smb349_exit(void) { -- cgit v1.2.3 From d5252b48f3c2c0a4be383311cae25752bcdc07b7 Mon Sep 17 00:00:00 2001 From: Steve Kuo Date: Fri, 30 Mar 2012 15:00:38 +0800 Subject: mfd: tps80031: fix missed irq issue We found missed irq could be happened if clear all INT_STS_x register in one time. Shadow register pushes the irq status after the first byte of INT_STS_x was cleared The proposed way to clear interrupt is to write only one INT_STS_x register. It will also clear the other two ones. Bug 952476 Reviewed-on: http://git-master/r/93453 Signed-off-by: Steve Kuo (cherry picked from commit 0c92f32e9e03defaeac991518b26134e59ef4db6) Change-Id: I76179be4847f59a1687926b9b0dde6ebd3f58aa4 Signed-off-by: Laxman Dewangan Reviewed-on: http://git-master/r/100306 --- drivers/mfd/tps80031.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/tps80031.c b/drivers/mfd/tps80031.c index e8ea75c424c6..94ba777bcdb3 100644 --- a/drivers/mfd/tps80031.c +++ b/drivers/mfd/tps80031.c @@ -874,8 +874,22 @@ static irqreturn_t tps80031_irq(int irq, void *data) acks = (tmp[2] << 16) | (tmp[1] << 8) | tmp[0]; if (acks) { - ret = tps80031_writes(tps80031->dev, SLAVE_ID2, - TPS80031_INT_STS_A, 3, tmp); + /* + * Hardware behavior: hardware have the shadow register for + * interrupt status register which is updated if interrupt + * comes just after the interrupt status read. This shadow + * register gets written to main status register and cleared + * if any byte write happens in any of status register like + * STS_A, STS_B or STS_C. + * Hence here to clear the original interrupt status and + * updating the STS register with the shadow register, it is + * require to write only one byte in any of STS register. + * Having multiple register write can cause the STS register + * to clear without handling those interrupt and can cause + * interrupt miss. + */ + ret = tps80031_write(tps80031->dev, SLAVE_ID2, + TPS80031_INT_STS_A, 0); if (ret < 0) { dev_err(tps80031->dev, "failed to write " "interrupt status\n"); -- cgit v1.2.3 From 1b441dac644532d9d50c2dc8712a3cf0c5003856 Mon Sep 17 00:00:00 2001 From: Charlie Huang Date: Mon, 23 Apr 2012 19:09:15 -0700 Subject: media: video: tegra: ov5650: read sensor fuse id The sequence of read fuse id is: 1. write to OTP index register 0x3d00. 2. read out byte from ox3d04. 3. repeat step 1 to the next byte with its index respectively. also fixed ov5650_read_reg always fail issue. bug 957657 Change-Id: I649a7765320d0d4be8111a7f523d8487b872b620 Signed-off-by: Charlie Huang Reviewed-on: http://git-master/r/98330 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Wei Chen Reviewed-by: Frank Chen Reviewed-by: Jon Mayo --- drivers/media/video/tegra/ov5650.c | 96 +++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/media/video/tegra/ov5650.c b/drivers/media/video/tegra/ov5650.c index 3adb46a3bb80..3cfb9560d889 100644 --- a/drivers/media/video/tegra/ov5650.c +++ b/drivers/media/video/tegra/ov5650.c @@ -39,6 +39,11 @@ struct ov5650_info { enum StereoCameraMode camera_mode; struct ov5650_sensor left; struct ov5650_sensor right; + struct ov5650_sensordata sensor_data; + struct mutex mutex_le; + struct mutex mutex_ri; + int power_refcnt_le; + int power_refcnt_ri; u8 i2c_trans_buf[SIZEOF_I2C_TRANSBUF]; }; @@ -788,7 +793,7 @@ static int ov5650_read_reg(struct i2c_client *client, u16 addr, u8 *val) err = i2c_transfer(client->adapter, msg, 2); - if (err != 1) + if (err != 2) return -EINVAL; *val = data[2]; @@ -1245,31 +1250,71 @@ static int ov5650_test_pattern(struct ov5650_info *info, } static int set_power_helper(struct ov5650_platform_data *pdata, - int powerLevel) + int powerLevel, int *ref_cnt) { if (pdata) { - if (powerLevel && pdata->power_on) - pdata->power_on(); - else if (pdata->power_off) - pdata->power_off(); + if (powerLevel && pdata->power_on) { + if (*ref_cnt == 0) + pdata->power_on(); + *ref_cnt = *ref_cnt + 1; + } + else if (pdata->power_off) { + *ref_cnt = *ref_cnt - 1; + if (*ref_cnt <= 0) + pdata->power_off(); + } } return 0; } -static int ov5650_set_power(int powerLevel) +static int ov5650_set_power(struct ov5650_info *info, int powerLevel) { pr_info("%s: powerLevel=%d camera mode=%d\n", __func__, powerLevel, - stereo_ov5650_info->camera_mode); + info->camera_mode); - if (StereoCameraMode_Left & stereo_ov5650_info->camera_mode) - set_power_helper(stereo_ov5650_info->left.pdata, powerLevel); + if (StereoCameraMode_Left & info->camera_mode) { + mutex_lock(&info->mutex_le); + set_power_helper(info->left.pdata, powerLevel, + &info->power_refcnt_le); + mutex_unlock(&info->mutex_le); + } - if (StereoCameraMode_Right & stereo_ov5650_info->camera_mode) - set_power_helper(stereo_ov5650_info->right.pdata, powerLevel); + if (StereoCameraMode_Right & info->camera_mode) { + mutex_lock(&info->mutex_ri); + set_power_helper(info->right.pdata, powerLevel, + &info->power_refcnt_ri); + mutex_unlock(&info->mutex_ri); + } return 0; } +static int ov5650_get_sensor_id(struct ov5650_info *info) +{ + int ret = 0; + int i; + u8 bak; + + pr_info("%s\n", __func__); + if (info->sensor_data.fuse_id_size) + return 0; + + ov5650_set_power(info, 1); + + for (i = 0; i < 5; i++) { + ret |= ov5650_write_reg_helper(info, 0x3d00, i); + ret |= ov5650_read_reg_helper(info, 0x3d04, + &bak); + info->sensor_data.fuse_id[i] = bak; + } + + if (!ret) + info->sensor_data.fuse_id_size = i; + + ov5650_set_power(info, 0); + return ret; +} + static long ov5650_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { @@ -1280,13 +1325,13 @@ static long ov5650_ioctl(struct file *file, case OV5650_IOCTL_SET_CAMERA_MODE: { if (info->camera_mode != arg) { - err = ov5650_set_power(0); + err = ov5650_set_power(info, 0); if (err) { pr_info("%s %d\n", __func__, __LINE__); return err; } info->camera_mode = arg; - err = ov5650_set_power(1); + err = ov5650_set_power(info, 1); if (err) return err; } @@ -1344,6 +1389,21 @@ static long ov5650_ioctl(struct file *file, } return ov5650_set_group_hold(info, &ae); } + case OV5650_IOCTL_GET_SENSORDATA: + { + err = ov5650_get_sensor_id(info); + if (err) { + pr_err("%s %d %d\n", __func__, __LINE__, err); + return err; + } + if (copy_to_user((void __user *)arg, + &info->sensor_data, + sizeof(struct ov5650_sensordata))) { + pr_info("%s %d\n", __func__, __LINE__); + return -EFAULT; + } + return 0; + } default: return -EINVAL; } @@ -1354,13 +1414,15 @@ static int ov5650_open(struct inode *inode, struct file *file) { pr_info("%s\n", __func__); file->private_data = stereo_ov5650_info; - ov5650_set_power(1); + ov5650_set_power(stereo_ov5650_info, 1); return 0; } int ov5650_release(struct inode *inode, struct file *file) { - ov5650_set_power(0); + struct ov5650_info *info = file->private_data; + + ov5650_set_power(info, 0); file->private_data = NULL; return 0; } @@ -1426,6 +1488,8 @@ static int left_ov5650_probe(struct i2c_client *client, stereo_ov5650_info->left.pdata = client->dev.platform_data; stereo_ov5650_info->left.i2c_client = client; + mutex_init(&stereo_ov5650_info->mutex_le); + mutex_init(&stereo_ov5650_info->mutex_ri); return 0; } -- cgit v1.2.3 From 8d709aefa05d224373c0337f1aa35c39fd50431c Mon Sep 17 00:00:00 2001 From: Terje Bergstrom Date: Thu, 26 Apr 2012 12:26:30 +0300 Subject: video: tegra: host: Clean up includes Clean up #includes. Replace #includes with forward declarations where possible, and remove extraneous #includes. Bug 871237 Change-Id: I6942e0c632b42ad7009589ebdd78def88ae4baa4 Signed-off-by: Terje Bergstrom Reviewed-on: http://git-master/r/99046 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Mayuresh Kulkarni GVS: Gerrit_Virtual_Submit Reviewed-by: Juha Tukkinen --- drivers/video/tegra/dc/dc_priv.h | 1 + drivers/video/tegra/host/bus_client.c | 5 +++++ drivers/video/tegra/host/chip_support.h | 10 +++++----- drivers/video/tegra/host/debug.c | 2 ++ drivers/video/tegra/host/dev.c | 9 ++++++--- drivers/video/tegra/host/dev.h | 4 ++-- drivers/video/tegra/host/gr3d/gr3d.c | 1 + drivers/video/tegra/host/gr3d/gr3d_t20.c | 1 + drivers/video/tegra/host/gr3d/gr3d_t30.c | 2 ++ drivers/video/tegra/host/host1x/host1x_cdma.c | 2 ++ drivers/video/tegra/host/host1x/host1x_channel.c | 2 ++ drivers/video/tegra/host/host1x/host1x_debug.c | 4 ++-- drivers/video/tegra/host/host1x/host1x_hwctx.h | 1 + drivers/video/tegra/host/host1x/host1x_intr.c | 1 + drivers/video/tegra/host/host1x/host1x_syncpt.c | 2 ++ drivers/video/tegra/host/mpe/mpe.c | 1 + drivers/video/tegra/host/nvhost_cdma.c | 3 +++ drivers/video/tegra/host/nvhost_cdma.h | 2 -- drivers/video/tegra/host/nvhost_channel.c | 1 + drivers/video/tegra/host/nvhost_channel.h | 8 +++----- drivers/video/tegra/host/nvhost_intr.c | 7 +++---- drivers/video/tegra/host/nvhost_job.c | 1 + drivers/video/tegra/host/nvhost_syncpt.c | 1 + drivers/video/tegra/host/nvhost_syncpt.h | 4 +--- drivers/video/tegra/host/t20/t20.c | 6 ++++-- drivers/video/tegra/host/t30/t30.c | 6 ++++-- 26 files changed, 57 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h index a91c42a5edc4..c08cd12f5a22 100644 --- a/drivers/video/tegra/dc/dc_priv.h +++ b/drivers/video/tegra/dc/dc_priv.h @@ -27,6 +27,7 @@ #include #include "../host/dev.h" +#include "../host/nvhost_acm.h" #include "../host/host1x/host1x_syncpt.h" #include diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c index b49c26e04f29..a3dc3781f81c 100644 --- a/drivers/video/tegra/host/bus_client.c +++ b/drivers/video/tegra/host/bus_client.c @@ -44,6 +44,11 @@ #include "debug.h" #include "bus_client.h" #include "dev.h" +#include "nvhost_acm.h" + +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "nvhost_hwctx.h" void nvhost_read_module_regs(struct nvhost_device *ndev, u32 offset, int count, u32 *values) diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h index 6727e7a69fb4..6e32feb9676e 100644 --- a/drivers/video/tegra/host/chip_support.h +++ b/drivers/video/tegra/host/chip_support.h @@ -22,20 +22,20 @@ #include struct output; + +struct nvhost_master; +struct nvhost_intr; +struct nvhost_syncpt; struct nvhost_waitchk; struct nvhost_userctx_timeout; -struct nvhost_master; struct nvhost_channel; struct nvmap_handle; struct nvmap_client; struct nvhost_hwctx; struct nvhost_cdma; -struct nvhost_intr; +struct nvhost_job; struct push_buffer; -struct nvhost_syncpt; -struct nvhost_master; struct dentry; -struct nvhost_job; struct nvhost_chip_support { struct { diff --git a/drivers/video/tegra/host/debug.c b/drivers/video/tegra/host/debug.c index 91436c903fc6..b7adb0e573eb 100644 --- a/drivers/video/tegra/host/debug.c +++ b/drivers/video/tegra/host/debug.c @@ -24,6 +24,8 @@ #include "dev.h" #include "debug.h" +#include "nvhost_acm.h" +#include "nvhost_channel.h" pid_t nvhost_debug_null_kickoff_pid; unsigned int nvhost_debug_trace_cmdbuf; diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c index 2e1ffeea7729..d21719bcda56 100644 --- a/drivers/video/tegra/host/dev.c +++ b/drivers/video/tegra/host/dev.c @@ -36,16 +36,18 @@ #include #include -#include #include #include #include #include "debug.h" -#include "nvhost_job.h" #include "t20/t20.h" #include "t30/t30.h" #include "bus_client.h" +#include "nvhost_acm.h" +#include +#include "nvhost_channel.h" +#include "nvhost_job.h" #define DRIVER_NAME "host1x" @@ -79,7 +81,8 @@ static int nvhost_ctrlrelease(struct inode *inode, struct file *filp) static int nvhost_ctrlopen(struct inode *inode, struct file *filp) { - struct nvhost_master *host = container_of(inode->i_cdev, struct nvhost_master, cdev); + struct nvhost_master *host = + container_of(inode->i_cdev, struct nvhost_master, cdev); struct nvhost_ctrl_userctx *priv; u32 *mod_locks; diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h index 74d7e16fc272..70199d8a5914 100644 --- a/drivers/video/tegra/host/dev.h +++ b/drivers/video/tegra/host/dev.h @@ -21,10 +21,9 @@ #ifndef __NVHOST_DEV_H #define __NVHOST_DEV_H -#include "nvhost_acm.h" +#include #include "nvhost_syncpt.h" #include "nvhost_intr.h" -#include "nvhost_channel.h" #include "chip_support.h" #define TRACE_MAX_LENGTH 128U @@ -34,6 +33,7 @@ extern int nvhost_major; extern int nvhost_minor; struct nvhost_hwctx; +struct nvhost_channel; struct nvhost_master { void __iomem *aperture; diff --git a/drivers/video/tegra/host/gr3d/gr3d.c b/drivers/video/tegra/host/gr3d/gr3d.c index f387d54e585e..da3d498a398f 100644 --- a/drivers/video/tegra/host/gr3d/gr3d.c +++ b/drivers/video/tegra/host/gr3d/gr3d.c @@ -29,6 +29,7 @@ #include "dev.h" #include "gr3d.h" #include "bus_client.h" +#include "nvhost_channel.h" #ifndef TEGRA_POWERGATE_3D1 #define TEGRA_POWERGATE_3D1 -1 diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c index 9ca990f89077..e3a887d34b96 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t20.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c @@ -19,6 +19,7 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" #include "dev.h" #include "host1x/host1x_channel.h" #include "host1x/host1x_hardware.h" diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c index ab4d04f89ab2..01eac53d0608 100644 --- a/drivers/video/tegra/host/gr3d/gr3d_t30.c +++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c @@ -19,6 +19,8 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" +#include "nvhost_cdma.h" #include "dev.h" #include "host1x/host1x_hardware.h" #include "host1x/host1x_syncpt.h" diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.c b/drivers/video/tegra/host/host1x/host1x_cdma.c index cdd6026718b1..08762020f366 100644 --- a/drivers/video/tegra/host/host1x/host1x_cdma.c +++ b/drivers/video/tegra/host/host1x/host1x_cdma.c @@ -19,7 +19,9 @@ */ #include +#include "nvhost_acm.h" #include "nvhost_cdma.h" +#include "nvhost_channel.h" #include "dev.h" #include "host1x_hardware.h" diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c index b16a34f416ab..04c5d0629ec2 100644 --- a/drivers/video/tegra/host/host1x/host1x_channel.c +++ b/drivers/video/tegra/host/host1x/host1x_channel.c @@ -20,6 +20,8 @@ #include "nvhost_channel.h" #include "dev.h" +#include "nvhost_acm.h" +#include "nvhost_job.h" #include "nvhost_hwctx.h" #include #include diff --git a/drivers/video/tegra/host/host1x/host1x_debug.c b/drivers/video/tegra/host/host1x/host1x_debug.c index 1a1d764bbd63..46ae9c1db019 100644 --- a/drivers/video/tegra/host/host1x/host1x_debug.c +++ b/drivers/video/tegra/host/host1x/host1x_debug.c @@ -25,10 +25,10 @@ #include "dev.h" #include "debug.h" +#include "host1x_hardware.h" #include "nvhost_cdma.h" +#include "nvhost_channel.h" #include "../../nvmap/nvmap.h" - -#include "host1x_hardware.h" #include "host1x_cdma.h" #define NVHOST_DEBUG_MAX_PAGE_OFFSET 102400 diff --git a/drivers/video/tegra/host/host1x/host1x_hwctx.h b/drivers/video/tegra/host/host1x/host1x_hwctx.h index 7587642d0e14..b5046c461d9d 100644 --- a/drivers/video/tegra/host/host1x/host1x_hwctx.h +++ b/drivers/video/tegra/host/host1x/host1x_hwctx.h @@ -24,6 +24,7 @@ #define __NVHOST_HOST1X_HWCTX_H #include +#include "nvhost_hwctx.h" struct nvhost_hwctx_handler; struct nvhost_channel; diff --git a/drivers/video/tegra/host/host1x/host1x_intr.c b/drivers/video/tegra/host/host1x/host1x_intr.c index 47e984e2943e..6d2aedbb9803 100644 --- a/drivers/video/tegra/host/host1x/host1x_intr.c +++ b/drivers/video/tegra/host/host1x/host1x_intr.c @@ -20,6 +20,7 @@ #include #include +#include #include "nvhost_intr.h" #include "dev.h" diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c index b431fa350638..d0de49463993 100644 --- a/drivers/video/tegra/host/host1x/host1x_syncpt.c +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c @@ -19,7 +19,9 @@ */ #include +#include #include "nvhost_syncpt.h" +#include "nvhost_acm.h" #include "dev.h" #include "host1x_syncpt.h" #include "host1x_hardware.h" diff --git a/drivers/video/tegra/host/mpe/mpe.c b/drivers/video/tegra/host/mpe/mpe.c index 36d1d6f26682..8d599a44adc3 100644 --- a/drivers/video/tegra/host/mpe/mpe.c +++ b/drivers/video/tegra/host/mpe/mpe.c @@ -19,6 +19,7 @@ */ #include "nvhost_hwctx.h" +#include "nvhost_channel.h" #include "dev.h" #include "host1x/host1x_hardware.h" #include "host1x/host1x_channel.h" diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index 775d761e65c9..7ebb0c8b4020 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -19,6 +19,9 @@ */ #include "nvhost_cdma.h" +#include "nvhost_channel.h" +#include "nvhost_job.h" +#include "nvhost_hwctx.h" #include "dev.h" #include diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h index 9cb9b8277254..9583fbbe5b16 100644 --- a/drivers/video/tegra/host/nvhost_cdma.h +++ b/drivers/video/tegra/host/nvhost_cdma.h @@ -28,8 +28,6 @@ #include #include -#include "nvhost_acm.h" - struct nvhost_syncpt; struct nvhost_userctx_timeout; struct nvhost_job; diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c index afbac6fe4c4e..129a41cc2f42 100644 --- a/drivers/video/tegra/host/nvhost_channel.c +++ b/drivers/video/tegra/host/nvhost_channel.c @@ -20,6 +20,7 @@ #include "nvhost_channel.h" #include "dev.h" +#include "nvhost_acm.h" #include "nvhost_job.h" #include #include diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h index 7b946c8ee853..4a67596c7cd7 100644 --- a/drivers/video/tegra/host/nvhost_channel.h +++ b/drivers/video/tegra/host/nvhost_channel.h @@ -21,13 +21,9 @@ #ifndef __NVHOST_CHANNEL_H #define __NVHOST_CHANNEL_H -#include "nvhost_cdma.h" -#include "nvhost_acm.h" -#include "nvhost_hwctx.h" -#include "nvhost_job.h" - #include #include +#include "nvhost_cdma.h" #define NVHOST_MAX_WAIT_CHECKS 256 #define NVHOST_MAX_GATHERS 512 @@ -37,6 +33,8 @@ struct nvhost_master; struct nvhost_waitchk; struct nvhost_device; +struct nvhost_channel; +struct nvhost_hwctx; struct nvhost_channel_gather { u32 words; diff --git a/drivers/video/tegra/host/nvhost_intr.c b/drivers/video/tegra/host/nvhost_intr.c index 7c4bdc7bafb6..d1f7d69fa5f1 100644 --- a/drivers/video/tegra/host/nvhost_intr.c +++ b/drivers/video/tegra/host/nvhost_intr.c @@ -20,14 +20,13 @@ #include "nvhost_intr.h" #include "dev.h" +#include "nvhost_acm.h" #include #include #include #include - - - - +#include "nvhost_channel.h" +#include "nvhost_hwctx.h" /*** Wait list management ***/ diff --git a/drivers/video/tegra/host/nvhost_job.c b/drivers/video/tegra/host/nvhost_job.c index a4f0cfc44212..5cd04a3a78a1 100644 --- a/drivers/video/tegra/host/nvhost_job.c +++ b/drivers/video/tegra/host/nvhost_job.c @@ -25,6 +25,7 @@ #include #include "nvhost_channel.h" #include "nvhost_job.h" +#include "nvhost_hwctx.h" #include "dev.h" /* Magic to use to fill freed handle slots */ diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c index 13ad0fc3a3cf..d69883ee51b2 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.c +++ b/drivers/video/tegra/host/nvhost_syncpt.c @@ -22,6 +22,7 @@ #include #include #include "nvhost_syncpt.h" +#include "nvhost_acm.h" #include "dev.h" #define MAX_STUCK_CHECK_COUNT 15 diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h index b71cb3e6287e..966a49fbd92c 100644 --- a/drivers/video/tegra/host/nvhost_syncpt.h +++ b/drivers/video/tegra/host/nvhost_syncpt.h @@ -27,9 +27,6 @@ #include #include -struct nvhost_syncpt; -struct nvhost_waitchk; - /* host managed and invalid syncpt id */ #define NVSYNCPT_GRAPHICS_HOST (0) #define NVSYNCPT_INVALID (-1) @@ -151,6 +148,7 @@ static inline int nvhost_syncpt_wait(struct nvhost_syncpt *sp, u32 id, u32 thres * @param: wait - start of filled in array of waitchk structs * @param: waitend - end ptr (one beyond last valid waitchk) */ +struct nvhost_waitchk; int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp, struct nvmap_client *nvmap, u32 mask, diff --git a/drivers/video/tegra/host/t20/t20.c b/drivers/video/tegra/host/t20/t20.c index 24ddedc842e4..19967cb8c7a4 100644 --- a/drivers/video/tegra/host/t20/t20.c +++ b/drivers/video/tegra/host/t20/t20.c @@ -19,17 +19,19 @@ */ #include +#include #include #include "dev.h" #include "t20.h" -#include "host1x/host1x_channel.h" #include "host1x/host1x_syncpt.h" #include "host1x/host1x_hardware.h" -#include "host1x/host1x_cdma.h" #include "gr3d/gr3d.h" #include "gr3d/gr3d_t20.h" #include "mpe/mpe.h" #include "nvhost_hwctx.h" +#include "nvhost_channel.h" +#include "host1x/host1x_channel.h" +#include "host1x/host1x_cdma.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c index fb69d4e0f57a..fce8081df332 100644 --- a/drivers/video/tegra/host/t30/t30.c +++ b/drivers/video/tegra/host/t30/t30.c @@ -19,19 +19,21 @@ */ #include +#include #include #include #include "dev.h" #include "t20/t20.h" #include "t30.h" #include "gr3d/gr3d.h" -#include "mpe/mpe.h" #include "gr3d/gr3d_t30.h" #include "gr3d/scale3d.h" +#include "mpe/mpe.h" #include "host1x/host1x_hardware.h" -#include "host1x/host1x_cdma.h" #include "host1x/host1x_syncpt.h" #include "chip_support.h" +#include "nvhost_channel.h" +#include "host1x/host1x_cdma.h" #define NVMODMUTEX_2D_FULL (1) #define NVMODMUTEX_2D_SIMPLE (2) -- cgit v1.2.3 From 91eb75c90ef8acd92573ca5145fee67f1781719f Mon Sep 17 00:00:00 2001 From: Pranav Chaturvedi Date: Mon, 16 Apr 2012 09:09:44 +0530 Subject: video: early exit from update_cdma when update_cdma_locked() is invoked, CDMA is not running implies that the queue is cleared and we can return immediately. Bug 960487 Change-Id: I599027906dc405f4490590443d4f4d5a3202b5b0 Reviewed-on: http://git-master/r/96650 (cherry picked from commit f297b4812d15540f4b14c87178662a7ca6575ce9) Reviewed-on: http://git-master/r/99994 Reviewed-by: Varun Colbert Tested-by: Varun Colbert --- drivers/video/tegra/host/nvhost_cdma.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c index 7ebb0c8b4020..f1f4405d0745 100644 --- a/drivers/video/tegra/host/nvhost_cdma.c +++ b/drivers/video/tegra/host/nvhost_cdma.c @@ -156,7 +156,9 @@ static void update_cdma_locked(struct nvhost_cdma *cdma) struct nvhost_syncpt *sp = &dev->syncpt; struct nvhost_job *job, *n; - BUG_ON(!cdma->running); + /* If CDMA is stopped, queue is cleared and we can return */ + if (!cdma->running) + return; /* * Walk the sync queue, reading the sync point registers as necessary, -- cgit v1.2.3 From e5e614baf45f670edfab4533ae8a91e4a35cf115 Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Fri, 27 Apr 2012 22:41:08 +0530 Subject: usb: tegra: add USB device controller driver for tegra chip Add High-speed USB device controller driver for tegra chips. This can work in OTG device mode with tegra OTG driver. Driver currently supports only UMTIP PHY. Bug 887361 Change-Id: I63774a44e3bb607c93007b170ba8b811f96e43f8 Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/97918 Reviewed-by: Venkat Moganty Reviewed-by: Automatic_Commit_Validation_User --- drivers/usb/gadget/Kconfig | 11 + drivers/usb/gadget/Makefile | 4 +- drivers/usb/gadget/gadget_chips.h | 4 +- drivers/usb/gadget/tegra_udc.c | 2867 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/tegra_udc.h | 440 ++++++ 5 files changed, 3323 insertions(+), 3 deletions(-) create mode 100644 drivers/usb/gadget/tegra_udc.c create mode 100644 drivers/usb/gadget/tegra_udc.h (limited to 'drivers') diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index f92c3df69195..8e051d72d6aa 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -178,6 +178,17 @@ config USB_OMAP dynamically linked module called "omap_udc" and force all gadget drivers to also be dynamically linked. +config USB_TEGRA + tristate "Nvidia Highspeed USB 2.0 device controller" + depends on ARCH_TEGRA + select USB_GADGET_DUALSPEED + help + Enables TEGRA USB 2.0 pheripheral driver. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "tegra_udc" and force all + gadget drivers to also be dynamically linked. + config USB_PXA25X tristate "PXA 25x or IXP 4xx" depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 581a5ae7337e..fedd8eef4ae2 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -2,7 +2,7 @@ # USB peripheral controller drivers # GCOV_PROFILE_fsl_tegra_udc.o := y -GCOV_PROFILE_fsl_udc_core.o := y +GCOV_PROFILE_tegra_udc.o := y ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG @@ -22,7 +22,7 @@ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o fsl_usb2_udc-y := fsl_udc_core.o fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o -fsl_usb2_udc-$(CONFIG_ARCH_TEGRA) += fsl_tegra_udc.o +obj-$(CONFIG_USB_TEGRA) += tegra_udc.o obj-$(CONFIG_USB_M66592) += m66592-udc.o obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index f3a83cd0ef50..db7f521e5d0a 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -49,6 +49,7 @@ #define gadget_is_s3c2410(g) (!strcmp("s3c2410_udc", (g)->name)) #define gadget_is_s3c_hsotg(g) (!strcmp("s3c-hsotg", (g)->name)) #define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name)) +#define gadget_is_tegra(g) (!strcmp("tegra-udc", (g)->name)) /** * usb_gadget_controller_number - support bcdDevice id convention @@ -115,7 +116,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x30; else if (gadget_is_net2272(gadget)) return 0x31; - + else if (gadget_is_tegra(gadget)) + return 0x32; return -ENOENT; } diff --git a/drivers/usb/gadget/tegra_udc.c b/drivers/usb/gadget/tegra_udc.c new file mode 100644 index 000000000000..978db7ece1de --- /dev/null +++ b/drivers/usb/gadget/tegra_udc.c @@ -0,0 +1,2867 @@ +/* + * Copyright (C) 2012 NVIDIA Corporation + * + * Description: + * High-speed USB device controller driver. + * The driver is based on Freescale driver code from Li Yang and Jiang Bo. + * + * 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. + */ + +#undef VERBOSE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* check this */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "tegra_udc.h" + +/* #define IS_NEW_PHY_DRIVER 1 */ + +#ifndef IS_NEW_PHY_DRIVER +#include "fsl_tegra_udc.c" +#endif + +#define DRIVER_DESC "Nvidia Tegra High-Speed USB SOC \ + Device Controller driver" +#define DRIVER_AUTHOR "Venkat Moganty/Rakesh Bodla" +#define DRIVER_VERSION "Apr 30, 2012" +#define USB1_PREFETCH_ID 6 + +#define get_ep_by_pipe(udc, pipe) ((pipe == 1) ? &udc->eps[0] : \ + &udc->eps[pipe]) +#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \ + * 2 + ((windex & USB_DIR_IN) ? 1 : 0)) + +#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF) +#define ep_is_in(EP) ((ep_index(EP) == 0) ? (EP->udc->ep0_dir == \ + USB_DIR_IN) : ((EP)->desc->bEndpointAddress \ + & USB_DIR_IN) == USB_DIR_IN) + + + +static const char driver_name[] = "tegra-udc"; +static const char driver_desc[] = DRIVER_DESC; + +static void tegra_ep_fifo_flush(struct usb_ep *_ep); +static int reset_queues(struct tegra_udc *udc); + +/* + * High speed test mode packet(53 bytes). + * See USB 2.0 spec, section 7.1.20. + */ +static const u8 tegra_udc_test_packet[53] = { + /* JKJKJKJK x9 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* JJKKJJKK x8 */ + 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, + /* JJJJKKKK x8 */ + 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, + /* JJJJJJJKKKKKKK x8 */ + 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + /* JJJJJJJK x8 */ + 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, + /* JKKKKKKK x10, JK */ + 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e +}; + +static struct tegra_udc *the_udc; + +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + static struct pm_qos_request_list boost_cpu_freq_req; + static u32 ep_queue_request_count; + static u8 boost_cpufreq_work_flag; +#endif + +static inline void udc_writel(struct tegra_udc *udc, u32 val, u32 offset) +{ + writel(val, udc->regs + offset); +} + +static inline unsigned int udc_readl(struct tegra_udc *udc, u32 offset) +{ + return readl(udc->regs + offset); +} + +/* checks vbus status */ +static inline bool vbus_enabled(struct tegra_udc *udc) +{ + bool status = false; + status = (udc_readl(udc, VBUS_WAKEUP_REG_OFFSET) & USB_SYS_VBUS_STATUS); + return status; +} + +/** + * done() - retire a request; caller blocked irqs + * @status : request status to be set, only works when + * request is still in progress. + */ +static void done(struct tegra_ep *ep, struct tegra_req *req, int status) +{ + struct tegra_udc *udc = NULL; + unsigned char stopped = ep->stopped; + struct ep_td_struct *curr_td, *next_td; + int j; + + udc = (struct tegra_udc *)ep->udc; + /* Removed the req from tegra_ep->queue */ + list_del_init(&req->queue); + + /* req.status should be set as -EINPROGRESS in ep_queue() */ + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + /* Free dtd for the request */ + next_td = req->head; + for (j = 0; j < req->dtd_count; j++) { + curr_td = next_td; + if (j != req->dtd_count - 1) + next_td = curr_td->next_td_virt; + + dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma); + } + + if (req->mapped) { + dma_unmap_single(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } else + dma_sync_single_for_cpu(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + if (status && (status != -ESHUTDOWN)) + VDBG("complete %s req %p stat %d len %u/%u", + ep->ep.name, &req->req, status, + req->req.actual, req->req.length); + + ep->stopped = 1; +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + if (req->req.complete && req->req.length >= BOOST_TRIGGER_SIZE) { + ep_queue_request_count--; + if (!ep_queue_request_count) + schedule_work(&udc->boost_cpufreq_work); + } +#endif + + spin_unlock(&ep->udc->lock); + /* complete() is from gadget layer, + * eg fsg->bulk_in_complete() */ + if (req->req.complete) + req->req.complete(&ep->ep, &req->req); + + spin_lock(&ep->udc->lock); + ep->stopped = stopped; +} + +/* + * nuke(): delete all requests related to this ep + * called with spinlock held + */ +static void nuke(struct tegra_ep *ep, int status) +{ + ep->stopped = 1; + + /* Flush fifo */ + tegra_ep_fifo_flush(&ep->ep); + + /* Whether this eq has request linked */ + while (!list_empty(&ep->queue)) { + struct tegra_req *req = NULL; + + req = list_entry(ep->queue.next, struct tegra_req, queue); + done(ep, req, status); + } +} + +static int can_pullup(struct tegra_udc *udc) +{ + DBG("%s(%d) udc->softconnect = %d udc->vbus_active = %d\n", + __func__, __LINE__, udc->softconnect, udc->vbus_active); + return udc->driver && udc->softconnect && udc->vbus_active; +} + +static int dr_controller_reset(struct tegra_udc *udc) +{ + unsigned int tmp; + unsigned long timeout; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Stop and reset the usb controller */ + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + tmp &= ~USB_CMD_RUN_STOP; + udc_writel(udc, tmp, USB_CMD_REG_OFFSET); + + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + tmp |= USB_CMD_CTRL_RESET; + udc_writel(udc, tmp, USB_CMD_REG_OFFSET); + + /* Wait for reset to complete */ + timeout = jiffies + UDC_RESET_TIMEOUT_MS; + while (udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_CTRL_RESET) { + if (time_after(jiffies, timeout)) { + ERR("udc reset timeout!\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + +static int dr_controller_setup(struct tegra_udc *udc) +{ + unsigned int tmp, portctrl; + unsigned long timeout; + int status; + unsigned int port_control_reg_offset; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (udc->has_hostpc) + port_control_reg_offset = USB_HOSTPCX_DEVLC_REG_OFFSET; + else + port_control_reg_offset = PORTSCX_REG_OFFSET; + + /* Config PHY interface */ + portctrl = udc_readl(udc, port_control_reg_offset); + portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH); + portctrl |= PORTSCX_PTS_UTMI; + udc_writel(udc, portctrl, port_control_reg_offset); + + status = dr_controller_reset(udc); + if (status) + return status; + + /* Set the controller as device mode */ + tmp = udc_readl(udc, USB_MODE_REG_OFFSET); + tmp |= USB_MODE_CTRL_MODE_DEVICE; + /* Disable Setup Lockout */ + tmp |= USB_MODE_SETUP_LOCK_OFF; + udc_writel(udc, tmp, USB_MODE_REG_OFFSET); + + /* Wait for controller to switch to device mode */ + timeout = jiffies + UDC_RESET_TIMEOUT_MS; + while ((udc_readl(udc, USB_MODE_REG_OFFSET) & + USB_MODE_CTRL_MODE_DEVICE) != USB_MODE_CTRL_MODE_DEVICE) { + if (time_after(jiffies, timeout)) { + ERR("udc device mode setup timeout!\n"); + return -ETIMEDOUT; + } + cpu_relax(); + } + + /* Clear the setup status */ + udc_writel(udc, 0, USB_STS_REG_OFFSET); + + tmp = udc->ep_qh_dma; + tmp &= USB_EP_LIST_ADDRESS_MASK; + udc_writel(udc, tmp, USB_EP_LIST_ADDRESS_REG_OFFSET); + + VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x", + udc->ep_qh, (int)tmp, + udc_readl(udc, USB_EP_LIST_ADDRESS_REG_OFFSET)); + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + +/* Enable DR irq and set controller to run state */ +static void dr_controller_run(struct tegra_udc *udc) +{ + u32 temp; + unsigned long timeout; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Clear stopped bit */ + udc->stopped = 0; + + /* If OTG transceiver is available, then it handles the VBUS detection*/ + if (!udc->transceiver) { + /* Enable cable detection interrupt, without setting the + * USB_SYS_VBUS_WAKEUP_INT bit. USB_SYS_VBUS_WAKEUP_INT is + * clear on write */ + temp = udc_readl(udc, VBUS_WAKEUP_REG_OFFSET); + temp |= (USB_SYS_VBUS_WAKEUP_INT_ENABLE + | USB_SYS_VBUS_WAKEUP_ENABLE); + temp &= ~USB_SYS_VBUS_WAKEUP_INT_STATUS; + udc_writel(udc, temp, VBUS_WAKEUP_REG_OFFSET); + } + /* Enable DR irq reg */ + temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN + | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN + | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN; + + udc_writel(udc, temp, USB_INTR_REG_OFFSET); + + /* Set the controller as device mode */ + temp = udc_readl(udc, USB_MODE_REG_OFFSET); + temp |= USB_MODE_CTRL_MODE_DEVICE; + udc_writel(udc, temp, USB_MODE_REG_OFFSET); + + /* Set controller to Run */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + if (can_pullup(udc)) + temp |= USB_CMD_RUN_STOP; + else + temp &= ~USB_CMD_RUN_STOP; + udc_writel(udc, temp, USB_CMD_REG_OFFSET); + + if (can_pullup(udc)) { + /* Wait for controller to start */ + timeout = jiffies + UDC_RUN_TIMEOUT_MS; + while ((udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_RUN_STOP) + != USB_CMD_RUN_STOP) { + if (time_after(jiffies, timeout)) { + ERR("udc start timeout!\n"); + return; + } + cpu_relax(); + } + } + + DBG("%s(%d) END\n", __func__, __LINE__); + return; +} + +static void dr_controller_stop(struct tegra_udc *udc) +{ + unsigned int tmp; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* Clear pending interrupt status bits */ + tmp = udc_readl(udc, USB_STS_REG_OFFSET); + udc_writel(udc, tmp, USB_STS_REG_OFFSET); + + /* disable all INTR */ + udc_writel(udc, 0, USB_INTR_REG_OFFSET); + + /* Set stopped bit for isr */ + udc->stopped = 1; + + /* set controller to Stop */ + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + tmp &= ~USB_CMD_RUN_STOP; + udc_writel(udc, tmp, USB_CMD_REG_OFFSET); + + DBG("%s(%d) END\n", __func__, __LINE__); + return; +} + +static void dr_ep_setup(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir, unsigned char ep_type) +{ + unsigned int tmp_epctrl = 0; + + tmp_epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (dir) { + if (ep_num) + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_TX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_TX_EP_TYPE_SHIFT); + } else { + if (ep_num) + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + tmp_epctrl |= EPCTRL_RX_ENABLE; + tmp_epctrl |= ((unsigned int)(ep_type) + << EPCTRL_RX_EP_TYPE_SHIFT); + } + + udc_writel(udc, tmp_epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); +} + +static void dr_ep_change_stall(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir, int value) +{ + u32 tmp_epctrl = 0; + + tmp_epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (value) { + /* set the stall bit */ + if (dir) + tmp_epctrl |= EPCTRL_TX_EP_STALL; + else + tmp_epctrl |= EPCTRL_RX_EP_STALL; + } else { + /* clear the stall bit and reset data toggle */ + if (dir) { + tmp_epctrl &= ~EPCTRL_TX_EP_STALL; + tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST; + } else { + tmp_epctrl &= ~EPCTRL_RX_EP_STALL; + tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST; + } + } + udc_writel(udc, tmp_epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); +} + +/* Get stall status of a specific ep + Return: 0: not stalled; 1:stalled */ +static int dr_ep_get_stall(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir) +{ + u32 epctrl; + + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (dir) + return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0; + else + return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0; +} + +/** + * struct_ep_qh_setup(): set the Endpoint Capabilites field of QH + * @zlt: Zero Length Termination Select (1: disable; 0: enable) + * @mult: Mult field + */ +static void struct_ep_qh_setup(struct tegra_udc *udc, unsigned char ep_num, + unsigned char dir, unsigned char ep_type, + unsigned int max_pkt_len, unsigned int zlt, unsigned char mult) +{ + struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir]; + unsigned int tmp = 0; + + /* set the Endpoint Capabilites in QH */ + switch (ep_type) { + case USB_ENDPOINT_XFER_CONTROL: + /* Interrupt On Setup (IOS). for control ep */ + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | EP_QUEUE_HEAD_IOS; + break; + case USB_ENDPOINT_XFER_ISOC: + tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS) + | (mult << EP_QUEUE_HEAD_MULT_POS); + break; + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS; + break; + default: + VDBG("error ep type is %d", ep_type); + return; + } + if (zlt) + tmp |= EP_QUEUE_HEAD_ZLT_SEL; + + p_QH->max_pkt_length = cpu_to_le32(tmp); + p_QH->next_dtd_ptr = 1; + p_QH->size_ioc_int_sts = 0; + + return; +} + +/* Setup qh structure and ep register for ep0. */ +static void ep0_setup(struct tegra_udc *udc) +{ + /* the intialization of an ep includes: fields in QH, Regs, + * tegra_ep struct */ + struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 1, 0); + struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL, + USB_MAX_CTRL_PAYLOAD, 1, 0); + dr_ep_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL); + dr_ep_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL); + + return; + +} + +/** + * when configurations are set, or when interface settings change + * for example the do_set_interface() in gadget layer, + * the driver will enable or disable the relevant endpoints + * ep0 doesn't use this routine. It is always enabled. + */ +static int tegra_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct tegra_udc *udc = NULL; + struct tegra_ep *ep = NULL; + unsigned short max = 0; + unsigned char mult = 0, zlt; + int retval = -EINVAL; + unsigned long flags = 0; + + ep = container_of(_ep, struct tegra_ep, ep); + + /* catch various bogus parameters */ + if (!_ep || !desc || ep->desc + || (desc->bDescriptorType != USB_DT_ENDPOINT)) + return -EINVAL; + + udc = ep->udc; + + if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN)) + return -ESHUTDOWN; + + max = le16_to_cpu(desc->wMaxPacketSize); + + /* Disable automatic zlp generation. Driver is responsible to indicate + * explicitly through req->req.zero. This is needed to enable multi-td + * request. + */ + zlt = 1; + + /* Assume the max packet size from gadget is always correct */ + switch (desc->bmAttributes & 0x03) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + case USB_ENDPOINT_XFER_INT: + /* mult = 0. Execute N Transactions as demonstrated by + * the USB variable length packet protocol where N is + * computed using the Maximum Packet Length (dQH) and + * the Total Bytes field (dTD) */ + mult = 0; + break; + case USB_ENDPOINT_XFER_ISOC: + /* Calculate transactions needed for high bandwidth iso */ + mult = (unsigned char)(1 + ((max >> 11) & 0x03)); + max = max & 0x7ff; /* bit 0~10 */ + /* 3 transactions at most */ + if (mult > 3) + goto en_done; + break; + default: + goto en_done; + } + + spin_lock_irqsave(&udc->lock, flags); + ep->ep.maxpacket = max; + ep->desc = desc; + ep->stopped = 0; + + /* Controller related setup + * Init EPx Queue Head (Ep Capabilites field in QH + * according to max, zlt, mult) + */ + struct_ep_qh_setup(udc, (unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK), + max, zlt, mult); + + /* Init endpoint ctrl register */ + dr_ep_setup(udc, (unsigned char) ep_index(ep), + (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN) + ? USB_SEND : USB_RECV), + (unsigned char) (desc->bmAttributes + & USB_ENDPOINT_XFERTYPE_MASK)); + + spin_unlock_irqrestore(&udc->lock, flags); + retval = 0; + + VDBG("enabled %s (ep%d%s) maxpacket %d", ep->ep.name, + ep->desc->bEndpointAddress & 0x0f, + (desc->bEndpointAddress & USB_DIR_IN) + ? "in" : "out", max); +en_done: + return retval; +} + +/** + * @ep : the ep being unconfigured. May not be ep0 + * Any pending and uncomplete req will complete with status (-ESHUTDOWN) + */ +static int tegra_ep_disable(struct usb_ep *_ep) +{ + struct tegra_udc *udc = NULL; + struct tegra_ep *ep = NULL; + + unsigned long flags = 0; + u32 epctrl; + int ep_num; + + ep = container_of(_ep, struct tegra_ep, ep); + if (!_ep || !ep->desc) { + VDBG("%s not enabled", _ep ? ep->ep.name : NULL); + return -EINVAL; + } + udc = (struct tegra_udc *)ep->udc; + + /* disable ep on controller */ + ep_num = ep_index(ep); + + /* Touch the registers if cable is connected and phy is on */ + if (vbus_enabled(udc)) { + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + udc_writel(udc, epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + } + + spin_lock_irqsave(&udc->lock, flags); + + /* nuke all pending requests (does flush) */ + nuke(ep, -ESHUTDOWN); + + ep->desc = NULL; + ep->stopped = 1; + spin_unlock_irqrestore(&udc->lock, flags); + + VDBG("disabled %s OK", _ep->name); + return 0; +} + +/** + * Allocate a request object used by this endpoint + * the main operation is to insert the req->queue to the eq->queue + * Returns the request, or null if one could not be allocated + */ +static struct usb_request * +tegra_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) +{ + struct tegra_req *req = NULL; + + req = kzalloc(sizeof *req, gfp_flags); + if (!req) + return NULL; + + req->req.dma = DMA_ADDR_INVALID; + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void tegra_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct tegra_req *req = NULL; + + req = container_of(_req, struct tegra_req, req); + + if (_req) + kfree(req); +} + +static void tegra_queue_td(struct tegra_ep *ep, struct tegra_req *req) +{ + int i = ep_index(ep) * 2 + ep_is_in(ep); + u32 temp, bitmask, tmp_stat; + struct ep_queue_head *dQH = &ep->udc->ep_qh[i]; + struct tegra_udc *udc = ep->udc; + + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + /* Flush all the dTD structs out to memory */ + wmb(); + + /* check if the pipe is empty */ + if (!(list_empty(&ep->queue))) { + /* Add td to the end */ + struct tegra_req *lastreq; + lastreq = list_entry(ep->queue.prev, struct tegra_req, queue); + lastreq->tail->next_td_ptr = + cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK); + wmb(); + /* Read prime bit, if 1 goto done */ + if (udc_readl(udc, EP_PRIME_REG_OFFSET) & bitmask) + goto out; + + do { + /* Set ATDTW bit in USBCMD */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + temp |= USB_CMD_ATDTW; + udc_writel(udc, temp, USB_CMD_REG_OFFSET); + + /* Read correct status bit */ + tmp_stat = udc_readl(udc, EP_STATUS_REG_OFFSET) + & bitmask; + + } while (!(udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_ATDTW)); + + /* Write ATDTW bit to 0 */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + udc_writel(udc, temp & ~USB_CMD_ATDTW, USB_CMD_REG_OFFSET); + + if (tmp_stat) + goto out; + } + + /* Write dQH next pointer and terminate bit to 0 */ + temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK; + dQH->next_dtd_ptr = cpu_to_le32(temp); + + /* Clear active and halt bit */ + temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE + | EP_QUEUE_HEAD_STATUS_HALT)); + dQH->size_ioc_int_sts &= temp; + +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_memory_prefetch_on(udc->phy); +#else + fsl_udc_ep_barrier(); +#endif + + /* Ensure that updates to the QH will occur before priming. */ + wmb(); + + /* Prime endpoint by writing 1 to ENDPTPRIME */ + temp = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + udc_writel(udc, temp, EP_PRIME_REG_OFFSET); +out: + return; +} + +/** + * Fill in the dTD structure + * @req : request that the transfer belongs to + * @length : return actually data length of the dTD + * @dma : return dma address of the dTD + * @is_last : return flag if it is the last dTD of the request + * return : pointer to the built dTD + */ +static struct ep_td_struct *tegra_build_dtd(struct tegra_req *req, + unsigned *length, dma_addr_t *dma, int *is_last, gfp_t gfp_flags) +{ + u32 swap_temp; + struct ep_td_struct *dtd; + + /* how big will this transfer be? */ + *length = min(req->req.length - req->req.actual, + (unsigned)EP_MAX_LENGTH_TRANSFER); + + dtd = dma_pool_alloc(the_udc->td_pool, gfp_flags, dma); + if (dtd == NULL) + return dtd; + + dtd->td_dma = *dma; + /* Clear reserved field */ + swap_temp = cpu_to_le32(dtd->size_ioc_sts); + swap_temp &= ~DTD_RESERVED_FIELDS; + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + /* Init all of buffer page pointers */ + swap_temp = (u32) (req->req.dma + req->req.actual); + dtd->buff_ptr0 = cpu_to_le32(swap_temp); + dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000); + dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000); + dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000); + dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000); + + req->req.actual += *length; + + /* zlp is needed if req->req.zero is set */ + if (req->req.zero) { + if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0) + *is_last = 1; + else + *is_last = 0; + } else if (req->req.length == req->req.actual) + *is_last = 1; + else + *is_last = 0; + + if ((*is_last) == 0) + VDBG("multi-dtd request!"); + + /* Fill in the transfer size; set active bit */ + swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE); + + /* Enable interrupt for the last dtd of a request */ + if (*is_last && !req->req.no_interrupt) + swap_temp |= DTD_IOC; + + dtd->size_ioc_sts = cpu_to_le32(swap_temp); + + mb(); + + VDBG("length = %d address= 0x%x", *length, (int)*dma); + + return dtd; +} + +/* Generate dtd chain for a request */ +static int tegra_req_to_dtd(struct tegra_req *req, gfp_t gfp_flags) +{ + unsigned count; + int is_last; + int is_first = 1; + struct ep_td_struct *last_dtd = NULL, *dtd; + dma_addr_t dma; + +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_memory_prefetch_off(the_udc->phy); +#else + fsl_udc_dtd_prepare(); +#endif + + do { + dtd = tegra_build_dtd(req, &count, &dma, &is_last, gfp_flags); + if (dtd == NULL) + return -ENOMEM; + + if (is_first) { + is_first = 0; + req->head = dtd; + } else { + last_dtd->next_td_ptr = cpu_to_le32(dma); + last_dtd->next_td_virt = dtd; + } + last_dtd = dtd; + + req->dtd_count++; + } while (!is_last); + + dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE); + + req->tail = dtd; + + return 0; +} + +/* queues (submits) an I/O request to an endpoint */ +static int +tegra_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) +{ + struct tegra_ep *ep = container_of(_ep, struct tegra_ep, ep); + struct tegra_req *req = container_of(_req, struct tegra_req, req); + struct tegra_udc *udc = ep->udc; + unsigned long flags; + enum dma_data_direction dir; + int status; + + /* catch various bogus parameters */ + if (!_req || !req->req.complete || !req->req.buf + || !list_empty(&req->queue)) { + VDBG("%s, bad params", __func__); + return -EINVAL; + } + + spin_lock_irqsave(&udc->lock, flags); + + if (unlikely(!ep->desc)) { + VDBG("%s, bad ep", __func__); + spin_unlock_irqrestore(&udc->lock, flags); + return -EINVAL; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + if (req->req.length > ep->ep.maxpacket) { + spin_unlock_irqrestore(&udc->lock, flags); + return -EMSGSIZE; + } + } + +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + if (req->req.length >= BOOST_TRIGGER_SIZE) { + ep_queue_request_count++; + if (ep_queue_request_count && boost_cpufreq_work_flag) + schedule_work(&udc->boost_cpufreq_work); + } +#endif + + dir = ep_is_in(ep) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; + + spin_unlock_irqrestore(&udc->lock, flags); + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + req->ep = ep; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(udc->gadget.dev.parent, + req->req.buf, req->req.length, dir); + req->mapped = 1; + } else { + dma_sync_single_for_device(udc->gadget.dev.parent, + req->req.dma, req->req.length, dir); + req->mapped = 0; + } + + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->dtd_count = 0; + + + /* build dtds and push them to device queue */ + status = tegra_req_to_dtd(req, gfp_flags); + if (status) + goto err_unmap; + + spin_lock_irqsave(&udc->lock, flags); + + /* re-check if the ep has not been disabled */ + if (unlikely(!ep->desc)) { + spin_unlock_irqrestore(&udc->lock, flags); + status = -EINVAL; + goto err_unmap; + } + + tegra_queue_td(ep, req); + + /* Update ep0 state */ + if ((ep_index(ep) == 0)) + udc->ep0_state = DATA_STATE_XMIT; + + /* irq handler advances the queue */ + if (req != NULL) + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; + +err_unmap: + if (req->mapped) { + dma_unmap_single(udc->gadget.dev.parent, + req->req.dma, req->req.length, dir); + req->req.dma = DMA_ADDR_INVALID; + req->mapped = 0; + } + return status; +} + +/* dequeues (cancels, unlinks) an I/O request from an endpoint */ +static int tegra_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct tegra_ep *ep = container_of(_ep, struct tegra_ep, ep); + struct tegra_req *req; + struct tegra_udc *udc = ep->udc; + unsigned long flags; + int ep_num, stopped, ret = 0; + u32 epctrl; + + if (!_ep || !_req) + return -EINVAL; + + spin_lock_irqsave(&ep->udc->lock, flags); + stopped = ep->stopped; + + /* Stop the ep before we deal with the queue */ + ep->stopped = 1; + ep_num = ep_index(ep); + + /* Touch the registers if cable is connected and phy is on */ + if (vbus_enabled(udc)) { + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (ep_is_in(ep)) + epctrl &= ~EPCTRL_TX_ENABLE; + else + epctrl &= ~EPCTRL_RX_ENABLE; + udc_writel(udc, epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + } + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + ret = -EINVAL; + goto out; + } + + /* The request is in progress, or completed but not dequeued */ + if (ep->queue.next == &req->queue) { + _req->status = -ECONNRESET; + tegra_ep_fifo_flush(_ep); /* flush current transfer */ + + /* The request isn't the last request in this ep queue */ + if (req->queue.next != &ep->queue) { + struct ep_queue_head *qh; + struct tegra_req *next_req; + + qh = ep->qh; + next_req = list_entry(req->queue.next, struct tegra_req, + queue); + + /* Point the QH to the first TD of next request */ + writel((u32) next_req->head, &qh->curr_dtd_ptr); + } + + /* The request hasn't been processed, patch up the TD chain */ + } else { + struct tegra_req *prev_req; + + prev_req = list_entry(req->queue.prev, struct tegra_req, queue); + writel(readl(&req->tail->next_td_ptr), + &prev_req->tail->next_td_ptr); + } + + done(ep, req, -ECONNRESET); + + /* Enable EP */ +out: + /* Touch the registers if cable is connected and phy is on */ + if (vbus_enabled(udc)) { + epctrl = udc_readl(udc, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + if (ep_is_in(ep)) + epctrl |= EPCTRL_TX_ENABLE; + else + epctrl |= EPCTRL_RX_ENABLE; + udc_writel(udc, epctrl, EP_CONTROL_REG_OFFSET + (ep_num * 4)); + } + ep->stopped = stopped; + + spin_unlock_irqrestore(&ep->udc->lock, flags); + return ret; +} + +/** + * modify the endpoint halt feature + * @ep: the non-isochronous endpoint being stalled + * @value: 1--set halt 0--clear halt + * Returns zero, or a negative error code. + */ +static int tegra_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct tegra_ep *ep = NULL; + unsigned long flags = 0; + int status = -EOPNOTSUPP; /* operation not supported */ + unsigned char ep_dir = 0, ep_num = 0; + struct tegra_udc *udc = NULL; + + ep = container_of(_ep, struct tegra_ep, ep); + udc = ep->udc; + if (!_ep || !ep->desc) { + status = -EINVAL; + goto out; + } + + if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) { + status = -EOPNOTSUPP; + goto out; + } + + /* Attempt to halt IN ep will fail if any transfer requests + * are still queue */ + if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { + status = -EAGAIN; + goto out; + } + + status = 0; + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + ep_num = (unsigned char)(ep_index(ep)); + spin_lock_irqsave(&ep->udc->lock, flags); + dr_ep_change_stall(udc, ep_num, ep_dir, value); + spin_unlock_irqrestore(&ep->udc->lock, flags); + + if (ep_index(ep) == 0) { + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + } +out: + VDBG(" %s %s halt stat %d", ep->ep.name, + value ? "set" : "clear", status); + + return status; +} + +static int tegra_ep_fifo_status(struct usb_ep *_ep) +{ + struct tegra_ep *ep; + struct tegra_udc *udc; + int size = 0; + u32 bitmask; + struct ep_queue_head *d_qh; + + ep = container_of(_ep, struct tegra_ep, ep); + if (!_ep || (!ep->desc && ep_index(ep) != 0)) + return -ENODEV; + + udc = (struct tegra_udc *)ep->udc; + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + d_qh = &ep->udc->ep_qh[ep_index(ep) * 2 + ep_is_in(ep)]; + + bitmask = (ep_is_in(ep)) ? (1 << (ep_index(ep) + 16)) : + (1 << (ep_index(ep))); + + if (udc_readl(udc, EP_STATUS_REG_OFFSET) & bitmask) + size = (d_qh->size_ioc_int_sts & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + + pr_debug("%s %u\n", __func__, size); + return size; +} + +static void tegra_ep_fifo_flush(struct usb_ep *_ep) +{ + struct tegra_ep *ep; + struct tegra_udc *udc; + int ep_num, ep_dir; + u32 bits; + unsigned long timeout; + + if (!_ep) { + return; + } else { + ep = container_of(_ep, struct tegra_ep, ep); + if (!ep->desc) + return; + } + ep_num = ep_index(ep); + ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; + udc = ep->udc; + + if (ep_num == 0) + bits = (1 << 16) | 1; + else if (ep_dir == USB_SEND) + bits = 1 << (16 + ep_num); + else + bits = 1 << ep_num; + + /* Touch the registers if cable is connected and phy is on */ + if (!vbus_enabled(udc)) + return; + + timeout = jiffies + UDC_FLUSH_TIMEOUT_MS; + do { + udc_writel(udc, bits, EPFLUSH_REG_OFFSET); + + /* Wait until flush complete */ + while (udc_readl(udc, EPFLUSH_REG_OFFSET)) { + if (time_after(jiffies, timeout)) { + ERR("ep flush timeout\n"); + return; + } + cpu_relax(); + } + /* See if we need to flush again */ + } while (udc_readl(udc, EP_STATUS_REG_OFFSET) & bits); +} + +static struct usb_ep_ops tegra_ep_ops = { + .enable = tegra_ep_enable, + .disable = tegra_ep_disable, + + .alloc_request = tegra_alloc_request, + .free_request = tegra_free_request, + + .queue = tegra_ep_queue, + .dequeue = tegra_ep_dequeue, + + .set_halt = tegra_ep_set_halt, + .fifo_status = tegra_ep_fifo_status, + .fifo_flush = tegra_ep_fifo_flush, /* flush fifo */ +}; + +/* Get the current frame number (from DR frame_index Reg ) */ +static int tegra_get_frame(struct usb_gadget *gadget) +{ + struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); + return (int)(udc_readl(udc, USB_FRINDEX_REG_OFFSET) + & USB_FRINDEX_MASKS); +} + +#ifndef CONFIG_USB_ANDROID +/* Tries to wake up the host connected to this gadget */ +static int tegra_wakeup(struct usb_gadget *gadget) +{ + struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); + u32 portsc; + + /* Remote wakeup feature not enabled by host */ + if (!udc->remote_wakeup) + return -ENOTSUPP; + + portsc = udc_readl(udc, PORTSCX_REG_OFFSET); + /* not suspended? */ + if (!(portsc & PORTSCX_PORT_SUSPEND)) + return 0; + + /* trigger force resume */ + portsc |= PORTSCX_PORT_FORCE_RESUME; + udc_writel(udc, portsc, PORTSCX_REG_OFFSET); + return 0; +} +#endif + +static int tegra_set_selfpowered(struct usb_gadget *gadget, int is_on) +{ + struct tegra_udc *udc; + udc = container_of(gadget, struct tegra_udc, gadget); + udc->selfpowered = (is_on != 0); + return 0; +} + +/** + * Notify controller that VBUS is powered, Called by whatever + * detects VBUS sessions + */ +static int tegra_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct tegra_udc *udc = container_of(gadget, struct tegra_udc, gadget); + + DBG("%s(%d) turn VBUS state from %s to %s", __func__, __LINE__, + udc->vbus_active ? "on" : "off", is_active ? "on" : "off"); + + if (udc->vbus_active && !is_active) { + /* If cable disconnected, cancel any delayed work */ + cancel_delayed_work(&udc->work); + spin_lock(&udc->lock); + /* reset all internal Queues and inform client driver */ + reset_queues(udc); + /* stop the controller and turn off the clocks */ + dr_controller_stop(udc); + dr_controller_reset(udc); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + spin_unlock(&udc->lock); +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_off(udc->phy); +#else + fsl_udc_clk_suspend(false); +#endif + if (udc->vbus_reg) { + /* set the current limit to 0mA */ + regulator_set_current_limit( + udc->vbus_reg, 0, 0); + } + } else if (!udc->vbus_active && is_active) { +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_on(udc->phy); +#else + fsl_udc_clk_resume(false); +#endif + /* setup the controller in the device mode */ + dr_controller_setup(udc); + /* setup EP0 for setup packet */ + ep0_setup(udc); + /* initialize the USB and EP states */ + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + udc->vbus_active = 1; + /* start the controller */ + dr_controller_run(udc); + if (udc->vbus_reg) { + /* set the current limit to 100mA */ + regulator_set_current_limit( + udc->vbus_reg, 0, 100); + } + /* Schedule work to wait for 1000 msec and check for + * charger if setup packet is not received */ + schedule_delayed_work(&udc->work, + USB_CHARGER_DETECTION_WAIT_TIME_MS); + } + return 0; +} + +/** + * Constrain controller's VBUS power usage. + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +static int tegra_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + struct tegra_udc *udc; + + udc = container_of(gadget, struct tegra_udc, gadget); + /* check udc regulator is available for drawing the vbus current */ + if (udc->vbus_reg) { + udc->current_limit = mA; + schedule_work(&udc->charger_work); + } + + if (udc->transceiver) + return otg_set_power(udc->transceiver, mA); + return -ENOTSUPP; +} + +/** + * Change Data+ pullup status + * this func is used by usb_gadget_connect/disconnect + */ +static int tegra_pullup(struct usb_gadget *gadget, int is_on) +{ + struct tegra_udc *udc; + u32 tmp; + + udc = container_of(gadget, struct tegra_udc, gadget); + udc->softconnect = (is_on != 0); + if (udc->transceiver && udc->transceiver->state != + OTG_STATE_B_PERIPHERAL) + return 0; + + tmp = udc_readl(udc, USB_CMD_REG_OFFSET); + if (can_pullup(udc)) + udc_writel(udc, tmp | USB_CMD_RUN_STOP, + USB_CMD_REG_OFFSET); + else + udc_writel(udc, (tmp & ~USB_CMD_RUN_STOP), + USB_CMD_REG_OFFSET); + + return 0; +} + +/* Release udc structures */ +static void tegra_udc_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct tegra_udc *udc = platform_get_drvdata(pdev); + + complete(udc->done); +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_close(udc->phy); +#else + fsl_udc_clk_suspend(false); +#endif + + kfree(udc); +} + +static int tegra_udc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)); +static int tegra_udc_stop(struct usb_gadget_driver *driver); +/* defined in gadget.h */ +static struct usb_gadget_ops tegra_gadget_ops = { + .get_frame = tegra_get_frame, +#ifndef CONFIG_USB_ANDROID + .wakeup = tegra_wakeup, +#endif + .set_selfpowered = tegra_set_selfpowered, + .vbus_session = tegra_vbus_session, + .vbus_draw = tegra_vbus_draw, + .pullup = tegra_pullup, + .start = tegra_udc_start, + .stop = tegra_udc_stop, +}; + +static int tegra_udc_setup_gadget_dev(struct tegra_udc *udc) +{ + /* Setup gadget structure */ + udc->gadget.ops = &tegra_gadget_ops; + udc->gadget.is_dualspeed = 1; + udc->gadget.ep0 = &udc->eps[0].ep; + INIT_LIST_HEAD(&udc->gadget.ep_list); + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.name = driver_name; + + /* Setup gadget.dev and register with kernel */ + dev_set_name(&udc->gadget.dev, "gadget"); + udc->gadget.dev.release = tegra_udc_release; + udc->gadget.dev.parent = &udc->pdev->dev; + + return device_register(&udc->gadget.dev); +} + + +/** + * Set protocol stall on ep0, protocol stall will automatically be cleared + * on new transaction. + */ +static void ep0stall(struct tegra_udc *udc) +{ + u32 tmp; + + /* must set tx and rx to stall at the same time */ + tmp = udc_readl(udc, EP_CONTROL_REG_OFFSET); + tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; + udc_writel(udc, tmp, EP_CONTROL_REG_OFFSET); + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; +} + +/* Prime a status phase for ep0 */ +static int ep0_prime_status(struct tegra_udc *udc, int direction) +{ + struct tegra_req *req = udc->status_req; + struct tegra_ep *ep; + + if (direction == EP_DIR_IN) + udc->ep0_dir = USB_DIR_IN; + else + udc->ep0_dir = USB_DIR_OUT; + + ep = &udc->eps[0]; + udc->ep0_state = WAIT_FOR_OUT_STATUS; + + req->ep = ep; + req->req.length = 0; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + if (tegra_req_to_dtd(req, GFP_ATOMIC) == 0) + tegra_queue_td(ep, req); + else + return -ENOMEM; + + list_add_tail(&req->queue, &ep->queue); + + return 0; +} + +static void udc_reset_ep_queue(struct tegra_udc *udc, u8 pipe) +{ + struct tegra_ep *ep = get_ep_by_pipe(udc, pipe); + + if (ep->name) + nuke(ep, -ESHUTDOWN); +} + +/* ch9 Set address */ +static void ch9setaddress(struct tegra_udc *udc, u16 value, u16 index, + u16 length) +{ + /* Save the new address to device struct */ + udc->device_address = (u8) value; + /* Update usb state */ + udc->usb_state = USB_STATE_ADDRESS; + /* Status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); +} + +/* ch9 Get status */ +static void ch9getstatus(struct tegra_udc *udc, u8 request_type, u16 value, + u16 index, u16 length) +{ + u16 tmp = 0; /* Status, cpu endian */ + struct tegra_req *req; + struct tegra_ep *ep; + + ep = &udc->eps[0]; + + if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { + /* Get device status */ + if (udc->selfpowered) + tmp = 1 << USB_DEVICE_SELF_POWERED; + tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { + /* Get interface status + * We don't have interface information in udc driver */ + tmp = 0; + } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { + /* Get endpoint status */ + struct tegra_ep *target_ep; + + target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index)); + + /* stall if endpoint doesn't exist */ + if (!target_ep->desc) + goto stall; + tmp = dr_ep_get_stall(udc, ep_index(target_ep), + ep_is_in(target_ep)) << USB_ENDPOINT_HALT; + } + + udc->ep0_dir = USB_DIR_IN; + /* Borrow the per device status_req */ + req = udc->status_req; + /* Fill in the reqest structure */ + *((u16 *) req->req.buf) = cpu_to_le16(tmp); + req->ep = ep; + req->req.length = 2; + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + + /* map virtual address to hardware */ + if (req->req.dma == DMA_ADDR_INVALID) { + req->req.dma = dma_map_single(ep->udc->gadget.dev.parent, + req->req.buf, + req->req.length, ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 1; + } else { + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + req->mapped = 0; + } + + /* prime the data phase */ + if ((tegra_req_to_dtd(req, GFP_ATOMIC) == 0)) + tegra_queue_td(ep, req); + else /* no mem */ + goto stall; + + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + return; +stall: + ep0stall(udc); +} + +static void udc_test_mode(struct tegra_udc *udc, u32 test_mode) +{ + struct tegra_req *req; + struct tegra_ep *ep; + u32 portsc, bitmask; + unsigned long timeout; + + /* Ack the ep0 IN */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + + /* get the ep0 */ + ep = &udc->eps[0]; + bitmask = ep_is_in(ep) + ? (1 << (ep_index(ep) + 16)) + : (1 << (ep_index(ep))); + + timeout = jiffies + HZ; + /* Wait until ep0 IN endpoint txfr is complete */ + while (!(udc_readl(udc, EP_COMPLETE_REG_OFFSET) & bitmask)) { + if (time_after(jiffies, timeout)) { + pr_err("Timeout for Ep0 IN Ack\n"); + break; + } + cpu_relax(); + } + + switch (test_mode << PORTSCX_PTC_BIT_POS) { + case PORTSCX_PTC_JSTATE: + VDBG("TEST_J\n"); + break; + case PORTSCX_PTC_KSTATE: + VDBG("TEST_K\n"); + break; + case PORTSCX_PTC_SEQNAK: + VDBG("TEST_SE0_NAK\n"); + break; + case PORTSCX_PTC_PACKET: + VDBG("TEST_PACKET\n"); + + /* get the ep and configure for IN direction */ + ep = &udc->eps[0]; + udc->ep0_dir = USB_DIR_IN; + + /* Initialize ep0 status request structure */ + req = container_of(tegra_alloc_request(NULL, GFP_ATOMIC), + struct tegra_req, req); + /* allocate a small amount of memory to get valid address */ + req->req.buf = kmalloc(sizeof(tegra_udc_test_packet), + GFP_ATOMIC); + req->req.dma = virt_to_phys(req->req.buf); + + /* Fill in the reqest structure */ + memcpy(req->req.buf, tegra_udc_test_packet, + sizeof(tegra_udc_test_packet)); + req->ep = ep; + req->req.length = sizeof(tegra_udc_test_packet); + req->req.status = -EINPROGRESS; + req->req.actual = 0; + req->req.complete = NULL; + req->dtd_count = 0; + req->mapped = 0; + + dma_sync_single_for_device(ep->udc->gadget.dev.parent, + req->req.dma, req->req.length, + ep_is_in(ep) + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE); + + /* prime the data phase */ + if ((tegra_req_to_dtd(req, GFP_ATOMIC) == 0)) + tegra_queue_td(ep, req); + else /* no mem */ + goto stall; + + list_add_tail(&req->queue, &ep->queue); + udc->ep0_state = DATA_STATE_XMIT; + break; + case PORTSCX_PTC_FORCE_EN: + VDBG("TEST_FORCE_EN\n"); + break; + default: + ERR("udc unknown test mode[%d]!\n", test_mode); + goto stall; + } + + /* read the portsc register */ + portsc = udc_readl(udc, PORTSCX_REG_OFFSET); + /* set the test mode selector */ + portsc |= test_mode << PORTSCX_PTC_BIT_POS; + udc_writel(udc, portsc, PORTSCX_REG_OFFSET); + + /* + * The device must have its power cycled to exit test mode. + * See USB 2.0 spec, section 9.4.9 for test modes operation + * in "Set Feature". + * See USB 2.0 spec, section 7.1.20 for test modes. + */ + pr_info("udc entering the test mode, power cycle to exit test mode\n"); + return; +stall: + ep0stall(udc); +} + +static void setup_received_irq(struct tegra_udc *udc, + struct usb_ctrlrequest *setup) +{ + u16 wValue = le16_to_cpu(setup->wValue); + u16 wIndex = le16_to_cpu(setup->wIndex); + u16 wLength = le16_to_cpu(setup->wLength); + + udc_reset_ep_queue(udc, 0); + + /* We process some stardard setup requests here */ + switch (setup->bRequest) { + case USB_REQ_GET_STATUS: + /* Data+Status phase from udc */ + if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) + != (USB_DIR_IN | USB_TYPE_STANDARD)) + break; + ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength); + return; + + case USB_REQ_SET_ADDRESS: + /* Status phase from udc */ + if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD + | USB_RECIP_DEVICE)) + break; + /* This delay is necessary for some windows drivers to + * properly recognize the device */ + mdelay(1); + ch9setaddress(udc, wValue, wIndex, wLength); + return; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + /* Status phase from udc */ + { + int rc = -EOPNOTSUPP; + + if (setup->bRequestType == USB_RECIP_DEVICE && + wValue == USB_DEVICE_TEST_MODE) { + /* + * If the feature selector is TEST_MODE, then the most + * significant byte of wIndex is used to specify the + * specific test mode and the lower byte of wIndex must + * be zero. + */ + udc_test_mode(udc, wIndex >> 8); + return; + + } else if ((setup->bRequestType & + (USB_RECIP_MASK | USB_TYPE_MASK)) == + (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { + int pipe = get_pipe_by_windex(wIndex); + struct tegra_ep *ep; + + if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) + break; + ep = get_ep_by_pipe(udc, pipe); + + spin_unlock(&udc->lock); + rc = tegra_ep_set_halt(&ep->ep, + (setup->bRequest == USB_REQ_SET_FEATURE) + ? 1 : 0); + spin_lock(&udc->lock); + + } else if ((setup->bRequestType & (USB_RECIP_MASK + | USB_TYPE_MASK)) == (USB_RECIP_DEVICE + | USB_TYPE_STANDARD)) { + /* Note: The driver has not include OTG support yet. + * This will be set when OTG support is added */ + if (!gadget_is_otg(&udc->gadget)) + break; + else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) + udc->gadget.b_hnp_enable = 1; + else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) + udc->gadget.a_hnp_support = 1; + else if (setup->bRequest == + USB_DEVICE_A_ALT_HNP_SUPPORT) + udc->gadget.a_alt_hnp_support = 1; + else + break; + rc = 0; + } else + break; + + if (rc == 0) { + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + } + return; + } + + default: + break; + } + + /* Requests handled by gadget */ + if (wLength) { + /* Data phase from gadget, status phase from udc */ + udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) + ? USB_DIR_IN : USB_DIR_OUT; + spin_unlock(&udc->lock); + if (udc->driver && udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = (setup->bRequestType & USB_DIR_IN) + ? DATA_STATE_XMIT : DATA_STATE_RECV; + } else { + /* No data phase, IN status from gadget */ + udc->ep0_dir = USB_DIR_IN; + spin_unlock(&udc->lock); + if (udc->driver && udc->driver->setup(&udc->gadget, + &udc->local_setup_buff) < 0) + ep0stall(udc); + spin_lock(&udc->lock); + udc->ep0_state = WAIT_FOR_OUT_STATUS; + } +} + +/* Process request for Data or Status phase of ep0 + * prime status phase if needed */ +static void ep0_req_complete(struct tegra_udc *udc, struct tegra_ep *ep0, + struct tegra_req *req) +{ + if (udc->usb_state == USB_STATE_ADDRESS) { + /* Set the new address */ + u32 new_address = (u32) udc->device_address; + udc_writel(udc, new_address << USB_DEVICE_ADDRESS_BIT_POS, + USB_DEVICE_ADDR_REG_OFFSET); + } + + done(ep0, req, 0); + + switch (udc->ep0_state) { + case DATA_STATE_XMIT: + /* receive status phase */ + if (ep0_prime_status(udc, EP_DIR_OUT)) + ep0stall(udc); + break; + case DATA_STATE_RECV: + /* send status phase */ + if (ep0_prime_status(udc, EP_DIR_IN)) + ep0stall(udc); + break; + case WAIT_FOR_OUT_STATUS: + udc->ep0_state = WAIT_FOR_SETUP; + break; + case WAIT_FOR_SETUP: + ERR("Unexpect ep0 packets\n"); + break; + default: + ep0stall(udc); + break; + } +} + +/* Tripwire mechanism to ensure a setup packet payload is extracted without + * being corrupted by another incoming setup packet */ +static void tripwire_handler(struct tegra_udc *udc, u8 ep_num, u8 *buffer_ptr) +{ + u32 temp; + struct ep_queue_head *qh; + + qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; + + /* Clear bit in ENDPTSETUPSTAT */ + temp = udc_readl(udc, EP_SETUP_STATUS_REG_OFFSET); + udc_writel(udc, temp | (1 << ep_num), EP_SETUP_STATUS_REG_OFFSET); + + /* while a hazard exists when setup package arrives */ + do { + /* Set Setup Tripwire */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + udc_writel(udc, temp | USB_CMD_SUTW, USB_CMD_REG_OFFSET); + + /* Copy the setup packet to local buffer */ + memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); + } while (!(udc_readl(udc, USB_CMD_REG_OFFSET) & USB_CMD_SUTW)); + + /* Clear Setup Tripwire */ + temp = udc_readl(udc, USB_CMD_REG_OFFSET); + udc_writel(udc, temp & ~USB_CMD_SUTW, USB_CMD_REG_OFFSET); +} + +/* process-ep_req(): free the completed Tds for this req */ +static int process_ep_req(struct tegra_udc *udc, int pipe, + struct tegra_req *curr_req) +{ + struct ep_td_struct *curr_td; + int td_complete, actual, remaining_length, j, tmp; + int status = 0; + int errors = 0; + struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; + int direction = pipe % 2; + + curr_td = curr_req->head; + td_complete = 0; + actual = curr_req->req.length; + + for (j = 0; j < curr_req->dtd_count; j++) { + /* Fence read for coherency of AHB master intiated writes */ + readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); + + dma_sync_single_for_cpu(udc->gadget.dev.parent, curr_td->td_dma, + sizeof(struct ep_td_struct), DMA_FROM_DEVICE); + + remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_PACKET_SIZE) + >> DTD_LENGTH_BIT_POS; + actual -= remaining_length; + errors = le32_to_cpu(curr_td->size_ioc_sts); + if (errors & DTD_ERROR_MASK) { + if (errors & DTD_STATUS_HALTED) { + ERR("dTD error %08x QH=%d\n", errors, pipe); + /* Clear the errors and Halt condition */ + tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); + tmp &= ~errors; + curr_qh->size_ioc_int_sts = cpu_to_le32(tmp); + status = -EPIPE; + /* FIXME: continue with next queued TD? */ + + break; + } + if (errors & DTD_STATUS_DATA_BUFF_ERR) { + VDBG("Transfer overflow"); + status = -EPROTO; + break; + } else if (errors & DTD_STATUS_TRANSACTION_ERR) { + VDBG("ISO error"); + status = -EILSEQ; + break; + } else + ERR("Unknown error has occurred (0x%x)!\n", + errors); + + } else if (le32_to_cpu(curr_td->size_ioc_sts) + & DTD_STATUS_ACTIVE) { + VDBG("Request not complete"); + status = REQ_UNCOMPLETE; + return status; + } else if (remaining_length) { + if (direction) { + VDBG("Transmit dTD remaining length not zero"); + status = -EPROTO; + break; + } else { + td_complete++; + break; + } + } else { + td_complete++; + VDBG("dTD transmitted successful"); + } + + if (j != curr_req->dtd_count - 1) + curr_td = (struct ep_td_struct *)curr_td->next_td_virt; + } + + if (status) + return status; + + curr_req->req.actual = actual; + + return 0; +} + +/* Process a DTD completion interrupt */ +static void dtd_complete_irq(struct tegra_udc *udc) +{ + u32 bit_pos; + int i, ep_num, direction, bit_mask, status; + struct tegra_ep *curr_ep; + struct tegra_req *curr_req, *temp_req; + + /* Clear the bits in the register */ + bit_pos = udc_readl(udc, EP_COMPLETE_REG_OFFSET); + udc_writel(udc, bit_pos, EP_COMPLETE_REG_OFFSET); + + if (!bit_pos) + return; + + for (i = 0; i < udc->max_ep; i++) { + ep_num = i >> 1; + direction = i % 2; + + bit_mask = 1 << (ep_num + 16 * direction); + + if (!(bit_pos & bit_mask)) + continue; + + curr_ep = get_ep_by_pipe(udc, i); + + /* If the ep is configured */ + if (curr_ep->name == NULL) { + WARNING("Invalid EP?"); + continue; + } + + /* process the req queue until an uncomplete request */ + list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue, + queue) { + status = process_ep_req(udc, i, curr_req); + + VDBG("status of process_ep_req= %d, ep = %d", + status, ep_num); + if (status == REQ_UNCOMPLETE) + break; + /* write back status to req */ + curr_req->req.status = status; + + if (ep_num == 0) { + ep0_req_complete(udc, curr_ep, curr_req); + break; + } else + done(curr_ep, curr_req, status); + } + } +} + +/* Process a port change interrupt */ +static void port_change_irq(struct tegra_udc *udc) +{ + u32 speed; + unsigned int port_control_reg_offset; + + if (udc->has_hostpc) + port_control_reg_offset = USB_HOSTPCX_DEVLC_REG_OFFSET; + else + port_control_reg_offset = PORTSCX_REG_OFFSET; + + /* Bus resetting is finished */ + if (!(udc_readl(udc, port_control_reg_offset) & PORTSCX_PORT_RESET)) { + /* Get the speed */ + speed = (udc_readl(udc, port_control_reg_offset) + & PORTSCX_PORT_SPEED_MASK); + if (speed == PORTSCX_PORT_SPEED_HIGH) + udc->gadget.speed = USB_SPEED_HIGH; + else if (speed == PORTSCX_PORT_SPEED_FULL) + udc->gadget.speed = USB_SPEED_FULL; + else if (speed == PORTSCX_PORT_SPEED_LOW) + udc->gadget.speed = USB_SPEED_LOW; + else + udc->gadget.speed = USB_SPEED_UNKNOWN; + } + + /* Update USB state */ + if (!udc->resume_state) + udc->usb_state = USB_STATE_DEFAULT; +} + +/* Process suspend interrupt */ +static void suspend_irq(struct tegra_udc *udc) +{ + udc->resume_state = udc->usb_state; + udc->usb_state = USB_STATE_SUSPENDED; + + /* report suspend to the driver, serial.c does not support this */ + if (udc->driver && udc->driver->suspend) + udc->driver->suspend(&udc->gadget); +} + +static void bus_resume(struct tegra_udc *udc) +{ + udc->usb_state = udc->resume_state; + udc->resume_state = 0; + + /* report resume to the driver, serial.c does not support this */ + if (udc->driver && udc->driver->resume) + udc->driver->resume(&udc->gadget); +} + +/* Clear up all ep queues */ +static int reset_queues(struct tegra_udc *udc) +{ + u8 pipe; + + for (pipe = 0; pipe < udc->max_pipes; pipe++) + udc_reset_ep_queue(udc, pipe); + + /* report disconnect; the driver is already quiesced */ + spin_unlock(&udc->lock); + if (udc->driver && udc->driver->disconnect) + udc->driver->disconnect(&udc->gadget); + spin_lock(&udc->lock); + + return 0; +} + +/* Process reset interrupt */ +static void reset_irq(struct tegra_udc *udc) +{ + u32 temp; + unsigned long timeout; + + /* Clear the device address */ + temp = udc_readl(udc, USB_DEVICE_ADDR_REG_OFFSET); + udc_writel(udc, temp & ~USB_DEVICE_ADDRESS_MASK, + USB_DEVICE_ADDR_REG_OFFSET); + + udc->device_address = 0; + + /* Clear usb state */ + udc->resume_state = 0; + udc->ep0_dir = 0; + udc->ep0_state = WAIT_FOR_SETUP; + udc->remote_wakeup = 0; /* default to 0 on reset */ + udc->gadget.b_hnp_enable = 0; + udc->gadget.a_hnp_support = 0; + udc->gadget.a_alt_hnp_support = 0; + + /* Clear all the setup token semaphores */ + temp = udc_readl(udc, EP_SETUP_STATUS_REG_OFFSET); + udc_writel(udc, temp, EP_SETUP_STATUS_REG_OFFSET); + + /* Clear all the endpoint complete status bits */ + temp = udc_readl(udc, EP_COMPLETE_REG_OFFSET); + udc_writel(udc, temp, EP_COMPLETE_REG_OFFSET); + + timeout = jiffies + 100; + while (udc_readl(udc, EP_PRIME_REG_OFFSET)) { + /* Wait until all endptprime bits cleared */ + if (time_after(jiffies, timeout)) { + ERR("Timeout for reset\n"); + break; + } + cpu_relax(); + } + + /* Write 1s to the flush register */ + udc_writel(udc, 0xffffffff, EPFLUSH_REG_OFFSET); + + /* When the bus reset is seen on Tegra, the PORTSCX_PORT_RESET bit + * is not set. Reset all the queues, include XD, dTD, EP queue + * head and TR Queue */ + VDBG("Bus reset"); + reset_queues(udc); + udc->usb_state = USB_STATE_DEFAULT; +} + +static void tegra_udc_set_current_limit_work(struct work_struct *work) +{ + struct tegra_udc *udc = container_of(work, struct tegra_udc, + charger_work); + /* check udc regulator is available for drawing vbus current*/ + if (udc->vbus_reg) { + /* set the current limit in uA */ + regulator_set_current_limit( + udc->vbus_reg, 0, + udc->current_limit * 1000); + } +} + +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ +static void tegra_udc_boost_cpu_frequency_work(struct work_struct *work) +{ + if (ep_queue_request_count && boost_cpufreq_work_flag) { + pm_qos_update_request(&boost_cpu_freq_req, + (s32)CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ * 1000); + boost_cpufreq_work_flag = 0; + } else if (!ep_queue_request_count && !boost_cpufreq_work_flag) { + pm_qos_update_request(&boost_cpu_freq_req, + PM_QOS_DEFAULT_VALUE); + boost_cpufreq_work_flag = 1; + } +} +#endif + +static void tegra_udc_irq_work(struct work_struct *irq_work) +{ + struct tegra_udc *udc = container_of(irq_work, struct tegra_udc, + irq_work); + + /* Check whether cable is connected*/ + if (vbus_enabled(udc)) + tegra_vbus_session(&udc->gadget, 1); + else + tegra_vbus_session(&udc->gadget, 0); +} + +/* + * If VBUS is detected and setup packet is not received in 100ms then + * work thread starts and checks for the USB charger detection. + */ +static void tegra_udc_charger_detect_work(struct work_struct *work) +{ + struct tegra_udc *udc = container_of(work, struct tegra_udc, work.work); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* check for the platform charger detection */ +#ifdef IS_NEW_PHY_DRIVER + if (tegra_usb_phy_charger_detected(udc->phy)) { +#else + if (fsl_udc_charger_detect()) { +#endif + printk(KERN_INFO "USB compliant charger detected\n"); + /* check udc regulator is available for drawing vbus current*/ + if (udc->vbus_reg) { + /* set the current limit in uA */ + regulator_set_current_limit( + udc->vbus_reg, 0, + USB_CHARGING_CURRENT_LIMIT_MA*1000); + } + } + + DBG("%s(%d) END\n", __func__, __LINE__); +} + +/* Restart device controller in the OTG mode on VBUS detection */ +static void tegra_udc_restart(struct tegra_udc *udc) +{ + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* setup the controller in the device mode */ + dr_controller_setup(udc); + /* setup EP0 for setup packet */ + ep0_setup(udc); + udc->vbus_active = 1; + /* start the controller */ + dr_controller_run(udc); + /* initialize the USB and EP states */ + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + + DBG("%s(%d) END\n", __func__, __LINE__); +} + +/* USB device controller interrupt handler */ +static irqreturn_t tegra_udc_irq(int irq, void *_udc) +{ + struct tegra_udc *udc = _udc; + u32 irq_src, temp; + irqreturn_t status = IRQ_NONE; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + if (!udc->transceiver) { + temp = udc_readl(udc, VBUS_WAKEUP_REG_OFFSET); + /* write back the register to clear the interrupt */ + udc_writel(udc, temp, VBUS_WAKEUP_REG_OFFSET); + if (temp & USB_SYS_VBUS_WAKEUP_INT_STATUS) + schedule_work(&udc->irq_work); + status = IRQ_HANDLED; + } + + /* Disable ISR for OTG host mode */ + if (udc->stopped) { + spin_unlock_irqrestore(&udc->lock, flags); + return status; + } + + /* Fence read for coherency of AHB master intiated writes */ + readb(IO_ADDRESS(IO_PPCS_PHYS + USB1_PREFETCH_ID)); + + irq_src = udc_readl(udc, USB_STS_REG_OFFSET) & + udc_readl(udc, USB_INTR_REG_OFFSET); + + /* Clear notification bits */ + udc_writel(udc, irq_src, USB_STS_REG_OFFSET); + + /* Need to resume? */ + if (udc->usb_state == USB_STATE_SUSPENDED) + if (!(udc_readl(udc, PORTSCX_REG_OFFSET) + & PORTSCX_PORT_SUSPEND)) + bus_resume(udc); + + /* USB Interrupt */ + if (irq_src & USB_STS_INT) { + VDBG("Packet int"); + /* Setup package, we only support ep0 as control ep */ + if (udc_readl(udc, EP_SETUP_STATUS_REG_OFFSET) & + EP_SETUP_STATUS_EP0) { + /* Setup packet received, we are connected to host + * and not to charger. Cancel any delayed work */ + __cancel_delayed_work(&udc->work); + tripwire_handler(udc, 0, + (u8 *) (&udc->local_setup_buff)); + setup_received_irq(udc, &udc->local_setup_buff); + status = IRQ_HANDLED; + } + + /* completion of dtd */ + if (udc_readl(udc, EP_COMPLETE_REG_OFFSET)) { + dtd_complete_irq(udc); + status = IRQ_HANDLED; + } + } + + /* SOF (for ISO transfer) */ + if (irq_src & USB_STS_SOF) + status = IRQ_HANDLED; + + /* Port Change */ + if (irq_src & USB_STS_PORT_CHANGE) { + port_change_irq(udc); + status = IRQ_HANDLED; + } + + /* Reset Received */ + if (irq_src & USB_STS_RESET) { + reset_irq(udc); + status = IRQ_HANDLED; + } + + /* Sleep Enable (Suspend) */ + if (irq_src & USB_STS_SUSPEND) { + suspend_irq(udc); + status = IRQ_HANDLED; + } + + if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) + VDBG("Error IRQ %x", irq_src); + + spin_unlock_irqrestore(&udc->lock, flags); + return status; +} + +/** + * Hook to gadget drivers + * Called by initialization code of gadget drivers + */ +static int tegra_udc_start(struct usb_gadget_driver *driver, + int (*bind)(struct usb_gadget *)) +{ + struct tegra_udc *udc = the_udc; + int retval = -ENODEV; + unsigned long flags = 0; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (!udc) + return -ENODEV; + + + + if (!driver || (driver->speed != USB_SPEED_FULL + && driver->speed != USB_SPEED_HIGH) + || !bind || !driver->disconnect + || !driver->setup) + return -EINVAL; + + if (udc->driver) + return -EBUSY; + + /* lock is needed but whether should use this lock or another */ + spin_lock_irqsave(&udc->lock, flags); + + driver->driver.bus = NULL; + /* hook up the driver */ + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + spin_unlock_irqrestore(&udc->lock, flags); + + /* bind udc driver to gadget driver */ + retval = bind(&udc->gadget); + if (retval) { + VDBG("bind to %s --> %d", driver->driver.name, retval); + udc->gadget.dev.driver = NULL; + udc->driver = NULL; + goto out; + } + + + /* Enable DR IRQ reg and Set usbcmd reg Run bit */ + if (!udc->transceiver) { + dr_controller_run(udc); + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + udc->vbus_active = vbus_enabled(udc); + } + + printk(KERN_INFO "%s: bind to driver %s\n", + udc->gadget.name, driver->driver.name); + +out: + if (retval) + printk(KERN_WARNING "gadget driver register failed %d\n", + retval); + + DBG("%s(%d) END\n", __func__, __LINE__); + return retval; +} + +/* Disconnect from gadget driver */ +static int tegra_udc_stop(struct usb_gadget_driver *driver) +{ + struct tegra_udc *udc = the_udc; + struct tegra_ep *loop_ep; + unsigned long flags; + + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + if (!udc) + return -ENODEV; + + if (!driver || driver != udc->driver || !driver->unbind) + return -EINVAL; + + /* stop DR, disable intr */ + dr_controller_stop(udc); + + /* in fact, no needed */ + udc->usb_state = USB_STATE_ATTACHED; + udc->ep0_state = WAIT_FOR_SETUP; + udc->ep0_dir = 0; + + /* stand operation */ + spin_lock_irqsave(&udc->lock, flags); + udc->gadget.speed = USB_SPEED_UNKNOWN; + nuke(&udc->eps[0], -ESHUTDOWN); + list_for_each_entry(loop_ep, &udc->gadget.ep_list, + ep.ep_list) + nuke(loop_ep, -ESHUTDOWN); + spin_unlock_irqrestore(&udc->lock, flags); + + /* report disconnect; the controller is already quiesced */ + driver->disconnect(&udc->gadget); + + /* unbind gadget and unhook driver. */ + driver->unbind(&udc->gadget); + udc->gadget.dev.driver = NULL; + udc->driver = NULL; + + printk(KERN_WARNING "unregistered gadget driver '%s'\n", + driver->driver.name); + + DBG("%s(%d) END\n", __func__, __LINE__); + + return 0; +} + + +/* Internal structure setup functions */ +static int tegra_udc_setup_qh(struct tegra_udc *udc) +{ + u32 dccparams; + size_t size; + + /* Read Device Controller Capability Parameters register */ + dccparams = udc_readl(udc, DCCPARAMS_REG_OFFSET); + if (!(dccparams & DCCPARAMS_DC)) { + ERR("This SOC doesn't support device role\n"); + return -ENODEV; + } + + /* Get max device endpoints */ + /* DEN is bidirectional ep number, max_ep doubles the number */ + udc->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2; + + udc->eps = kzalloc(sizeof(struct tegra_ep) * udc->max_ep, GFP_KERNEL); + if (!udc->eps) { + ERR("malloc tegra_ep failed\n"); + return -1; + } + + /* Setup hardware queue heads */ + size = udc->max_ep * sizeof(struct ep_queue_head); + udc->ep_qh = (struct ep_queue_head *)((u8 *)(udc->regs) + QH_OFFSET); + udc->ep_qh_dma = platform_get_resource(udc->pdev, IORESOURCE_MEM + , 0)->start + QH_OFFSET; + udc->ep_qh_size = size; + + /* Initialize ep0 status request structure */ + /* FIXME: tegra_alloc_request() ignores ep argument */ + udc->status_req = container_of(tegra_alloc_request(NULL, GFP_KERNEL), + struct tegra_req, req); + /* allocate a small amount of memory to get valid address */ + udc->status_req->req.buf = dma_alloc_coherent(&udc->pdev->dev, + STATUS_BUFFER_SIZE, &udc->status_req->req.dma, + GFP_KERNEL); + if (!udc->status_req->req.buf) { + ERR("alloc status_req buffer failed\n"); + kfree(udc->eps); + return -ENOMEM; + } + + udc->resume_state = USB_STATE_NOTATTACHED; + udc->usb_state = USB_STATE_POWERED; + udc->ep0_dir = 0; + udc->remote_wakeup = 0; /* default to 0 on reset */ + + return 0; +} + +/** + * Setup the tegra_ep struct for eps + * Link tegra_ep->ep to gadget->ep_list + * ep0out is not used so do nothing here + * ep0in should be taken care + */ +static int __init struct_ep_setup(struct tegra_udc *udc, unsigned char index, + char *name, int link) +{ + struct tegra_ep *ep = &udc->eps[index]; + + ep->udc = udc; + strcpy(ep->name, name); + ep->ep.name = ep->name; + + ep->ep.ops = &tegra_ep_ops; + ep->stopped = 0; + + /* for ep0: maxP defined in desc + * for other eps, maxP is set by epautoconfig() called by gadget layer + */ + ep->ep.maxpacket = (unsigned short) ~0; + + /* the queue lists any req for this ep */ + INIT_LIST_HEAD(&ep->queue); + + /* gagdet.ep_list used for ep_autoconfig so no ep0 */ + if (link) + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + ep->gadget = &udc->gadget; + ep->qh = &udc->ep_qh[index]; + + return 0; +} + +static int tegra_udc_ep_setup(struct tegra_udc *udc) +{ + /* initialize EP0 descriptor */ + static const struct usb_endpoint_descriptor tegra_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD, + }; + int i; + + /* setup QH and epctrl for ep0 */ + ep0_setup(udc); + + /* setup udc->eps[] for ep0 */ + struct_ep_setup(udc, 0, "ep0", 0); + /* for ep0: the desc defined here; + * for other eps, gadget layer called ep_enable with defined desc + */ + udc->eps[0].desc = &tegra_ep0_desc; + udc->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD; + + /* setup the udc->eps[] for non-control endpoints and link + * to gadget.ep_list */ + for (i = 1; i < (int)(udc->max_ep / 2); i++) { + char name[14]; + + sprintf(name, "ep%dout", i); + struct_ep_setup(udc, i * 2, name, 1); + sprintf(name, "ep%din", i); + struct_ep_setup(udc, i * 2 + 1, name, 1); + } + + return 0; +} + + +/* Driver probe function + * all intialization operations implemented here except enabling usb_intr reg + * board setup should have been done in the platform code + */ +static int __init tegra_udc_probe(struct platform_device *pdev) +{ + struct tegra_udc *udc; + struct resource *res; + int err = -ENODEV; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + if (strcmp(pdev->name, driver_name)) { + VDBG("Wrong device"); + return -ENODEV; + } + + the_udc = udc = kzalloc(sizeof(struct tegra_udc), GFP_KERNEL); + if (udc == NULL) { + ERR("malloc udc failed\n"); + return -ENOMEM; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENXIO; + ERR("failed to get platform resources\n"); + goto err_kfree; + } + + if (!request_mem_region(res->start, res->end - res->start + 1, + driver_name)) { + ERR("request mem region failed\n"); + err = -EBUSY; + goto err_kfree; + } + + udc->regs = ioremap(res->start, resource_size(res)); + if (!udc->regs) { + err = -ENOMEM; + ERR("failed to map mem region\n"); + goto err_rel_mem_region; + } + + udc->irq = platform_get_irq(pdev, 0); + if (!udc->irq) { + err = -ENODEV; + ERR("failed to get platform irq resources\n"); + goto err_iounmap; + } + + err = request_irq(udc->irq, tegra_udc_irq, IRQF_SHARED, + driver_name, udc); + if (err) { + ERR("cannot request irq %d err %d\n", udc->irq, err); + goto err_iounmap; + } + +#ifdef IS_NEW_PHY_DRIVER + udc->phy = tegra_usb_phy_open(pdev); + if (IS_ERR(udc->phy)) { + dev_err(&pdev->dev, "failed to open USB phy\n"); + err = -ENXIO; + goto err_irq; + } + + err = tegra_usb_phy_power_on(udc->phy); + if (err) { + dev_err(&pdev->dev, "failed to power on the phy\n"); + goto err_phy; + } + + err = tegra_usb_phy_init(udc->phy); + if (err) { + dev_err(&pdev->dev, "failed to init the phy\n"); + goto err_phy; + } +#else + /* Initialize USB clocks */ + fsl_udc_clk_init(pdev); +#endif + spin_lock_init(&udc->lock); + udc->stopped = 1; + udc->pdev = pdev; +#ifdef IS_NEW_PHY_DRIVER + udc->has_hostpc = tegra_usb_phy_has_hostpc(udc->phy) ? 1 : 0; +#else + #ifdef CONFIG_ARCH_TEGRA_2x_SOC + udc->has_hostpc = 0; + #else + udc->has_hostpc = 1; + #endif +#endif + + platform_set_drvdata(pdev, udc); + + /* Initialize the udc structure including QH members */ + err = tegra_udc_setup_qh(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup udc QH\n"); + goto err_phy; + } + + /* initialize usb hw reg except for regs for EP, + * leave usbintr reg untouched */ + err = dr_controller_setup(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup udc controller\n"); + goto err_phy; + } + + err = tegra_udc_setup_gadget_dev(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup udc gadget device\n"); + goto err_phy; + } + + err = tegra_udc_ep_setup(udc); + if (err) { + dev_err(&pdev->dev, "failed to setup end points\n"); + goto err_unregister; + } + + /* use dma_pool for TD management */ + udc->td_pool = dma_pool_create("udc_td", &pdev->dev, + sizeof(struct ep_td_struct), + DTD_ALIGNMENT, UDC_DMA_BOUNDARY); + if (!udc->td_pool) { + err = -ENOMEM; + goto err_unregister; + } + + err = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (err) + goto err_del_udc; +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + boost_cpufreq_work_flag = 1; + ep_queue_request_count = 0; + INIT_WORK(&udc->boost_cpufreq_work, + tegra_udc_boost_cpu_frequency_work); + pm_qos_add_request(&boost_cpu_freq_req, PM_QOS_CPU_FREQ_MIN, + PM_QOS_DEFAULT_VALUE); +#endif + + /* create a work for controlling clocks to the phy if otg is disabled */ + INIT_WORK(&udc->irq_work, tegra_udc_irq_work); + /* create a delayed work for detecting the USB charger */ + INIT_DELAYED_WORK(&udc->work, tegra_udc_charger_detect_work); + INIT_WORK(&udc->charger_work, tegra_udc_set_current_limit_work); + + /* Get the regulator for drawing the vbus current in udc driver */ + udc->vbus_reg = regulator_get(NULL, "usb_bat_chg"); + if (IS_ERR(udc->vbus_reg)) { + dev_info(&pdev->dev, + "usb_bat_chg regulator not registered:" + " USB charging will not be enabled\n"); + udc->vbus_reg = NULL; + } + +#ifdef CONFIG_USB_OTG_UTILS + +#ifdef IS_NEW_PHY_DRIVER + if (tegra_usb_phy_otg_supported(udc->phy)) + udc->transceiver = otg_get_transceiver(); +#else + udc->transceiver = otg_get_transceiver(); +#endif + + if (udc->transceiver) { + dr_controller_stop(udc); + dr_controller_reset(udc); +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_off(udc->phy); +#else + fsl_udc_clk_suspend(false); +#endif + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + otg_set_peripheral(udc->transceiver, &udc->gadget); + } +#else + /* Power down the phy if cable is not connected */ + if (!vbus_enabled()) { +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_off(udc->phy); +#else + fsl_udc_clk_suspend(false); +#endif + } +#endif + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; + +err_del_udc: + dma_pool_destroy(udc->td_pool); + +err_unregister: + device_unregister(&udc->gadget.dev); + +#ifdef IS_NEW_PHY_DRIVER +err_phy: + tegra_usb_phy_close(udc->phy); + +err_irq: + free_irq(udc->irq, udc); +#else +err_phy: +#endif + +err_iounmap: + iounmap(udc->regs); + +err_rel_mem_region: + release_mem_region(res->start, res->end - res->start + 1); + +err_kfree: + kfree(udc); + + return err; +} + +/* Driver removal function + * Free resources and finish pending transactions + */ +static int __exit tegra_udc_remove(struct platform_device *pdev) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + DECLARE_COMPLETION(done); + + if (!udc) + return -ENODEV; + + usb_del_gadget_udc(&udc->gadget); + udc->done = &done; + + cancel_delayed_work(&udc->work); +#ifdef CONFIG_TEGRA_GADGET_BOOST_CPU_FREQ + cancel_work_sync(&udc->boost_cpufreq_work); +#endif + + if (udc->vbus_reg) + regulator_put(udc->vbus_reg); + + if (udc->transceiver) + otg_set_peripheral(udc->transceiver, NULL); + +#ifndef IS_NEW_PHY_DRIVER + fsl_udc_clk_release(); +#endif + + /* Free allocated memory */ + dma_free_coherent(&pdev->dev, STATUS_BUFFER_SIZE, + udc->status_req->req.buf, + udc->status_req->req.dma); + kfree(udc->status_req); + kfree(udc->eps); + + dma_pool_destroy(udc->td_pool); + free_irq(udc->irq, udc); + iounmap(udc->regs); + release_mem_region(res->start, res->end - res->start + 1); + + device_unregister(&udc->gadget.dev); + /* free udc --wait for the release() finished */ + wait_for_completion(&done); + + return 0; +} + +static int tegra_udc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + + /* if it controller is in otg mode, return */ + if (udc->transceiver) + return 0; + + if (udc->vbus_active) { + spin_lock(&udc->lock); + /* Reset all internal Queues and inform client driver */ + reset_queues(udc); + udc->vbus_active = 0; + udc->usb_state = USB_STATE_DEFAULT; + spin_unlock(&udc->lock); + } + /* stop the controller and turn off the clocks */ + dr_controller_stop(udc); + if (udc->transceiver) + udc->transceiver->state = OTG_STATE_UNDEFINED; + +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_off(udc->phy); +#else + fsl_udc_clk_suspend(true); +#endif + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + +static int tegra_udc_resume(struct platform_device *pdev) +{ + struct tegra_udc *udc = platform_get_drvdata(pdev); + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + +#ifndef IS_NEW_PHY_DRIVER +#ifdef CONFIG_ARCH_TEGRA_2x_SOC + /* Work around to get UTMIP_OTGPD, UTMIP_BIASPD values correctly */ + fsl_udc_clk_resume(true); + fsl_udc_clk_suspend(true); +#endif +#endif + + if (udc->transceiver) + return 0; + +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_on(udc->phy); +#else + fsl_udc_clk_resume(true); + fsl_udc_clk_resume(true); +#endif + tegra_udc_restart(udc); + + /* Power down the phy if cable is not connected */ + if (!vbus_enabled(udc)) { + udc->vbus_active = 0; +#ifdef IS_NEW_PHY_DRIVER + tegra_usb_phy_power_off(udc->phy); +#else + fsl_udc_clk_suspend(true); +#endif + } + + + DBG("%s(%d) END\n", __func__, __LINE__); + return 0; +} + + +static struct platform_driver tegra_udc_driver = { + .remove = __exit_p(tegra_udc_remove), + .suspend = tegra_udc_suspend, + .resume = tegra_udc_resume, + .driver = { + .name = (char *)driver_name, + .owner = THIS_MODULE, + }, +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION); + return platform_driver_probe(&tegra_udc_driver, tegra_udc_probe); +} +module_init(udc_init); +static void __exit udc_exit(void) +{ + platform_driver_unregister(&tegra_udc_driver); + printk(KERN_WARNING "%s unregistered\n", driver_desc); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:tegra-udc"); diff --git a/drivers/usb/gadget/tegra_udc.h b/drivers/usb/gadget/tegra_udc.h new file mode 100644 index 000000000000..e94543fd98e3 --- /dev/null +++ b/drivers/usb/gadget/tegra_udc.h @@ -0,0 +1,440 @@ +/* + * Copyright (C) 2011 NVIDIA Corporation + * + * Description: + * High-speed USB device controller driver. + * USB device/endpoint management registers. + * The driver is previously named as fsl_udc_core. Based on Freescale driver + * code from Li Yang and Jiang Bo. + * + * 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. + */ +#ifndef __TEGRA_UDC_H +#define __TEGRA_UDC_H + +#ifdef VERBOSE +#define VDBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \ + __func__, ## args) +#else +#define VDBG(fmt, args...) do {} while (0) +#endif + + +#ifdef DEBUG +#define DBG(stuff...) pr_info("tegra_udc: " stuff) +#else +#define DBG(stuff...) do {} while (0) +#endif + +#define ERR(stuff...) pr_err("tegra_udc: " stuff) +#define WARNING(stuff...) pr_warning("tegra_udc: " stuff) + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) +#define STATUS_BUFFER_SIZE 8 + +#define USB_MAX_CTRL_PAYLOAD 64 + + /* Charger current limit=1800mA, as per the USB charger spec */ +#define USB_CHARGING_CURRENT_LIMIT_MA 1800 + /* 1 sec wait time for charger detection after vbus is detected */ +#define USB_CHARGER_DETECTION_WAIT_TIME_MS 1000 +#define BOOST_TRIGGER_SIZE 4096 + +#define UDC_RESET_TIMEOUT_MS 1000 +#define UDC_RUN_TIMEOUT_MS 1000 +#define UDC_FLUSH_TIMEOUT_MS 1000 + +/* ep0 transfer state */ +#define WAIT_FOR_SETUP 0 +#define DATA_STATE_XMIT 1 +#define DATA_STATE_NEED_ZLP 2 +#define WAIT_FOR_OUT_STATUS 3 +#define DATA_STATE_RECV 4 + +/* + * ### pipe direction macro from device view + */ +#define USB_RECV 0 /* OUT EP */ +#define USB_SEND 1 /* IN EP */ + +/* Device Controller Capability Parameter register */ +#define DCCPARAMS_REG_OFFSET 0x124 +#define DCCPARAMS_DC 0x00000080 +#define DCCPARAMS_DEN_MASK 0x0000001f + +/* USB CMD Register Bit Masks */ +#define USB_CMD_REG_OFFSET ((udc->has_hostpc) ? 0x130 : 0x140) +#define USB_CMD_RUN_STOP 0x00000001 +#define USB_CMD_CTRL_RESET 0x00000002 +#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010 +#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020 +#define USB_CMD_INT_AA_DOORBELL 0x00000040 +#define USB_CMD_ASP 0x00000300 +#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800 +#define USB_CMD_SUTW 0x00002000 +#define USB_CMD_ATDTW 0x00004000 +#define USB_CMD_ITC 0x00FF0000 +/* bit 15,3,2 are frame list size */ +#define USB_CMD_FRAME_SIZE_1024 0x00000000 +#define USB_CMD_FRAME_SIZE_512 0x00000004 +#define USB_CMD_FRAME_SIZE_256 0x00000008 +#define USB_CMD_FRAME_SIZE_128 0x0000000C +#define USB_CMD_FRAME_SIZE_64 0x00008000 +#define USB_CMD_FRAME_SIZE_32 0x00008004 +#define USB_CMD_FRAME_SIZE_16 0x00008008 +#define USB_CMD_FRAME_SIZE_8 0x0000800C +/* bit 9-8 are async schedule park mode count */ +#define USB_CMD_ASP_00 0x00000000 +#define USB_CMD_ASP_01 0x00000100 +#define USB_CMD_ASP_10 0x00000200 +#define USB_CMD_ASP_11 0x00000300 +#define USB_CMD_ASP_BIT_POS 8 +/* bit 23-16 are interrupt threshold control */ +#define USB_CMD_ITC_NO_THRESHOLD 0x00000000 +#define USB_CMD_ITC_1_MICRO_FRM 0x00010000 +#define USB_CMD_ITC_2_MICRO_FRM 0x00020000 +#define USB_CMD_ITC_4_MICRO_FRM 0x00040000 +#define USB_CMD_ITC_8_MICRO_FRM 0x00080000 +#define USB_CMD_ITC_16_MICRO_FRM 0x00100000 +#define USB_CMD_ITC_32_MICRO_FRM 0x00200000 +#define USB_CMD_ITC_64_MICRO_FRM 0x00400000 +#define USB_CMD_ITC_BIT_POS 16 + +/* USB STS Register Bit Masks */ +#define USB_STS_REG_OFFSET ((udc->has_hostpc) ? 0x134 : 0x144) +#define USB_STS_INT 0x00000001 +#define USB_STS_ERR 0x00000002 +#define USB_STS_PORT_CHANGE 0x00000004 +#define USB_STS_FRM_LST_ROLL 0x00000008 +#define USB_STS_SYS_ERR 0x00000010 +#define USB_STS_IAA 0x00000020 +#define USB_STS_RESET 0x00000040 +#define USB_STS_SOF 0x00000080 +#define USB_STS_SUSPEND 0x00000100 +#define USB_STS_HC_HALTED 0x00001000 +#define USB_STS_RCL 0x00002000 +#define USB_STS_PERIODIC_SCHEDULE 0x00004000 +#define USB_STS_ASYNC_SCHEDULE 0x00008000 + +/* USB INTR Register Bit Masks */ +#define USB_INTR_REG_OFFSET ((udc->has_hostpc) ? 0x138 : 0x148) +#define USB_INTR_INT_EN 0x00000001 +#define USB_INTR_ERR_INT_EN 0x00000002 +#define USB_INTR_PTC_DETECT_EN 0x00000004 +#define USB_INTR_FRM_LST_ROLL_EN 0x00000008 +#define USB_INTR_SYS_ERR_EN 0x00000010 +#define USB_INTR_ASYN_ADV_EN 0x00000020 +#define USB_INTR_RESET_EN 0x00000040 +#define USB_INTR_SOF_EN 0x00000080 +#define USB_INTR_DEVICE_SUSPEND 0x00000100 + +/* Frame Index Register Bit Masks */ +#define USB_FRINDEX_REG_OFFSET ((udc->has_hostpc) ? 0x13c : 0x14c) +#define USB_FRINDEX_MASKS 0x3fff + +/* Device Address bit masks */ +#define USB_DEVICE_ADDR_REG_OFFSET ((udc->has_hostpc) ? 0x144 : 0x154) +#define USB_DEVICE_ADDRESS_MASK 0xFE000000 +#define USB_DEVICE_ADDRESS_BIT_POS 25 + +/* endpoint list address bit masks */ +#define USB_EP_LIST_ADDRESS_REG_OFFSET ((udc->has_hostpc) ? 0x148 : 0x158) +#define USB_EP_LIST_ADDRESS_MASK 0xfffff800 + +/* PORTSCX Register Bit Masks */ +#define PORTSCX_REG_OFFSET ((udc->has_hostpc) ? 0x174 : 0x184) +#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001 +#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002 +#define PORTSCX_PORT_ENABLE 0x00000004 +#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008 +#define PORTSCX_OVER_CURRENT_ACT 0x00000010 +#define PORTSCX_OVER_CURRENT_CHG 0x00000020 +#define PORTSCX_PORT_FORCE_RESUME 0x00000040 +#define PORTSCX_PORT_SUSPEND 0x00000080 +#define PORTSCX_PORT_RESET 0x00000100 +#define PORTSCX_LINE_STATUS_BITS 0x00000C00 +#define PORTSCX_PORT_POWER 0x00001000 +#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000 +#define PORTSCX_PORT_TEST_CTRL 0x000F0000 +#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000 +#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000 +#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000 +#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000 + +/* In tegra3 the following fields have moved to new HOSTPC1_DEVLC reg and + * their offsets have changed. + * Keeping the name of bit masks same as before (PORTSCX_*) to have + * minimum changes to code */ +#define USB_HOSTPCX_DEVLC_REG_OFFSET 0x1b4 + +#define PORTSCX_PORT_FORCE_FULL_SPEED ((udc->has_hostpc) ? 0x00800000 \ + : 0x01000000) +#define PORTSCX_PORT_SPEED_MASK ((udc->has_hostpc) ? 0x06000000 : 0x0C000000) +#define PORTSCX_PORT_WIDTH ((udc->has_hostpc) ? 0x08000000 : 0x10000000) +#define PORTSCX_PHY_TYPE_SEL ((udc->has_hostpc) ? 0xE0000000 : 0xC0000000) + +/* bits for port speed */ +#define PORTSCX_PORT_SPEED_FULL ((udc->has_hostpc) ? 0x00000000 : 0x00000000) +#define PORTSCX_PORT_SPEED_LOW ((udc->has_hostpc) ? 0x02000000 : 0x04000000) +#define PORTSCX_PORT_SPEED_HIGH ((udc->has_hostpc) ? 0x04000000 : 0x08000000) +#define PORTSCX_PORT_SPEED_UNDEF ((udc->has_hostpc) ? 0x06000000 : 0x0C000000) +#define PORTSCX_SPEED_BIT_POS ((udc->has_hostpc) ? 25 : 26) + +/* bits for parallel transceiver width for UTMI interface */ +#define PORTSCX_PTW ((udc->has_hostpc) ? 0x08000000 : 0x10000000) +#define PORTSCX_PTW_8BIT ((udc->has_hostpc) ? 0x00000000 : 0x00000000) +#define PORTSCX_PTW_16BIT ((udc->has_hostpc) ? 0x08000000 : 0x10000000) + +/* bits for port transceiver select */ +#define PORTSCX_PTS_UTMI ((udc->has_hostpc) ? 0x00000000 : 0x00000000) +#define PORTSCX_PTS_ULPI ((udc->has_hostpc) ? 0x40000000 : 0x80000000) +#define PORTSCX_PTS_FSLS ((udc->has_hostpc) ? 0x60000000 : 0xC0000000) +#define PORTSCX_PTS_BIT_POS ((udc->has_hostpc) ? 29 : 30) + +/* bit 11-10 are line status */ +#define PORTSCX_LINE_STATUS_SE0 0x00000000 +#define PORTSCX_LINE_STATUS_JSTATE 0x00000400 +#define PORTSCX_LINE_STATUS_KSTATE 0x00000800 +#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00 +#define PORTSCX_LINE_STATUS_BIT_POS 10 + +/* bit 15-14 are port indicator control */ +#define PORTSCX_PIC_OFF 0x00000000 +#define PORTSCX_PIC_AMBER 0x00004000 +#define PORTSCX_PIC_GREEN 0x00008000 +#define PORTSCX_PIC_UNDEF 0x0000C000 +#define PORTSCX_PIC_BIT_POS 14 + +/* bit 19-16 are port test control */ +#define PORTSCX_PTC_DISABLE 0x00000000 +#define PORTSCX_PTC_JSTATE 0x00010000 +#define PORTSCX_PTC_KSTATE 0x00020000 +#define PORTSCX_PTC_SEQNAK 0x00030000 +#define PORTSCX_PTC_PACKET 0x00040000 +#define PORTSCX_PTC_FORCE_EN 0x00050000 +#define PORTSCX_PTC_BIT_POS 16 + + +/* USB MODE Register Bit Masks */ +#define USB_MODE_REG_OFFSET ((udc->has_hostpc) ? 0x1f8 : 0x1a8) +#define USB_MODE_CTRL_MODE_IDLE 0x00000000 +#define USB_MODE_CTRL_MODE_DEVICE 0x00000002 +#define USB_MODE_CTRL_MODE_HOST 0x00000003 +#define USB_MODE_CTRL_MODE_RSV 0x00000001 +#define USB_MODE_SETUP_LOCK_OFF 0x00000008 +#define USB_MODE_STREAM_DISABLE 0x00000010 + +/* Endpoint Setup Status bit masks */ +#define EP_SETUP_STATUS_REG_OFFSET ((udc->has_hostpc) ? 0x208 : 0x1ac) +#define EP_SETUP_STATUS_MASK 0x0000003F +#define EP_SETUP_STATUS_EP0 0x00000001 + +/* Endpoint Prime Register */ +#define EP_PRIME_REG_OFFSET ((udc->has_hostpc) ? 0x20c : 0x1b0) + +/* Endpoint Flush Register */ +#define EPFLUSH_REG_OFFSET ((udc->has_hostpc) ? 0x210 : 0x1b4) +#define EPFLUSH_TX_OFFSET 0x00010000 +#define EPFLUSH_RX_OFFSET 0x00000000 + +/* Endpoint Status Register */ +#define EP_STATUS_REG_OFFSET ((udc->has_hostpc) ? 0x214 : 0x1b8) + +/* Endpoint Complete Register */ +#define EP_COMPLETE_REG_OFFSET ((udc->has_hostpc) ? 0x218 : 0x1bc) + +/* Endpoint Control Registers */ +#define EP_CONTROL_REG_OFFSET ((udc->has_hostpc) ? 0x21c : 0x1c0) + +/* ENDPOINTCTRLx Register Bit Masks */ +#define EPCTRL_TX_ENABLE 0x00800000 +#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */ +#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */ +#define EPCTRL_TX_TYPE 0x000C0000 +#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */ +#define EPCTRL_TX_EP_STALL 0x00010000 +#define EPCTRL_RX_ENABLE 0x00000080 +#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */ +#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */ +#define EPCTRL_RX_TYPE 0x0000000C +#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */ +#define EPCTRL_RX_EP_STALL 0x00000001 + +/* bit 19-18 and 3-2 are endpoint type */ +#define EPCTRL_EP_TYPE_CONTROL 0 +#define EPCTRL_EP_TYPE_ISO 1 +#define EPCTRL_EP_TYPE_BULK 2 +#define EPCTRL_EP_TYPE_INTERRUPT 3 +#define EPCTRL_TX_EP_TYPE_SHIFT 18 +#define EPCTRL_RX_EP_TYPE_SHIFT 2 + +#define VBUS_SENSOR_REG_OFFSET 0x404 +#define VBUS_WAKEUP_REG_OFFSET 0x408 + +#define USB_SYS_VBUS_ASESSION_INT_EN 0x10000 +#define USB_SYS_VBUS_ASESSION_CHANGED 0x20000 +#define USB_SYS_VBUS_ASESSION 0x40000 +#define USB_SYS_VBUS_WAKEUP_ENABLE 0x40000000 +#define USB_SYS_VBUS_WAKEUP_INT_ENABLE 0x100 +#define USB_SYS_VBUS_WAKEUP_INT_STATUS 0x200 +#define USB_SYS_VBUS_STATUS 0x400 +#define USB_SYS_ID_PIN_STATUS 0x4 + + +/* Endpoint Queue Head Bit Masks */ +#define EP_QUEUE_HEAD_MULT_POS 30 +#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000 +#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16 +#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff) +#define EP_QUEUE_HEAD_IOS 0x00008000 +#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001 +#define EP_QUEUE_HEAD_IOC 0x00008000 +#define EP_QUEUE_HEAD_MULTO 0x00000C00 +#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040 +#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080 +#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF +#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0 +#define EP_QUEUE_FRINDEX_MASK 0x000007FF +#define EP_MAX_LENGTH_TRANSFER 0x4000 + + + +/* Endpoint Transfer Descriptor bit Masks */ +#define DTD_NEXT_TERMINATE 0x00000001 +#define DTD_IOC 0x00008000 +#define DTD_STATUS_ACTIVE 0x00000080 +#define DTD_STATUS_HALTED 0x00000040 +#define DTD_STATUS_DATA_BUFF_ERR 0x00000020 +#define DTD_STATUS_TRANSACTION_ERR 0x00000008 +#define DTD_RESERVED_FIELDS 0x80007300 +#define DTD_ADDR_MASK 0xFFFFFFE0 +#define DTD_PACKET_SIZE 0x7FFF0000 +#define DTD_LENGTH_BIT_POS 16 +#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \ + DTD_STATUS_DATA_BUFF_ERR | \ + DTD_STATUS_TRANSACTION_ERR) +/* Alignment requirements; must be a power of two */ +#define DTD_ALIGNMENT 0x80 +#define QH_ALIGNMENT 2048 +#define QH_OFFSET 0x1000 + +/* Controller dma boundary */ +#define UDC_DMA_BOUNDARY 0x1000 + +#define REQ_UNCOMPLETE 1 + +#define EP_DIR_IN 1 +#define EP_DIR_OUT 0 + +/* + * Endpoint Queue Head data struct + * Rem: all the variables of qh are LittleEndian Mode + * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr + */ +struct ep_queue_head { + u32 max_pkt_length; /* Mult(31-30), Zlt(29), Max Pkt len and IOS(15) */ + u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */ + u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */ + u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */ + u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */ + u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */ + u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */ + u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */ + u32 res1; + u8 setup_buffer[8]; /* Setup data 8 bytes */ + u32 res2[4]; +}; + +/* Endpoint Transfer Descriptor data struct */ +/* Rem: all the variables of td are LittleEndian Mode */ +struct ep_td_struct { + u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set + indicate invalid */ + u32 size_ioc_sts; /* Total bytes (30-16), IOC (15), + MultO(11-10), STS (7-0) */ + u32 buff_ptr0; /* Buffer pointer Page 0 */ + u32 buff_ptr1; /* Buffer pointer Page 1 */ + u32 buff_ptr2; /* Buffer pointer Page 2 */ + u32 buff_ptr3; /* Buffer pointer Page 3 */ + u32 buff_ptr4; /* Buffer pointer Page 4 */ + u32 res; + /* 32 bytes */ + dma_addr_t td_dma; /* dma address for this td */ + /* virtual address of next td specified in next_td_ptr */ + struct ep_td_struct *next_td_virt; +}; + + +struct tegra_req { + struct usb_request req; + struct list_head queue; + /* ep_queue() func will add + a request->queue into a udc_ep->queue 'd tail */ + struct tegra_ep *ep; + unsigned mapped:1; + + struct ep_td_struct *head, *tail; /* For dTD List + cpu endian Virtual addr */ + unsigned int dtd_count; +}; + +struct tegra_ep { + struct usb_ep ep; + struct list_head queue; + struct tegra_udc *udc; + struct ep_queue_head *qh; + const struct usb_endpoint_descriptor *desc; + struct usb_gadget *gadget; + + char name[14]; + unsigned stopped:1; +}; + +struct tegra_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct completion *done; /* to make sure release() is done */ + struct tegra_ep *eps; + struct platform_device *pdev; + struct tegra_usb_phy *phy; + struct usb_ctrlrequest local_setup_buff; + struct otg_transceiver *transceiver; + struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */ + struct tegra_req *status_req; /* ep0 status request */ + struct dma_pool *td_pool; /* dma pool for DTD */ + struct delayed_work work; /* delayed work for charger detection */ + struct regulator *vbus_reg; /* regulator for drawing VBUS */ + /* work for setting regulator current limit */ + struct work_struct charger_work; + /* work for boosting cpu frequency */ + struct work_struct boost_cpufreq_work; + /* irq work for controlling the usb power */ + struct work_struct irq_work; + void __iomem *regs; + size_t ep_qh_size; /* size after alignment adjustment*/ + dma_addr_t ep_qh_dma; /* dma address of QH */ + unsigned int max_ep; + unsigned int irq; + u32 max_pipes; /* Device max pipes */ + u32 resume_state; /* USB state to resume */ + u32 usb_state; /* USB current state */ + u32 ep0_state; /* Endpoint zero state */ + u32 ep0_dir; /* Endpoint zero direction: USB_DIR_IN/USB_DIR_OUT */ + u8 device_address; /* Device USB address */ + u32 current_limit; + spinlock_t lock; + unsigned softconnect:1; + unsigned vbus_active:1; + unsigned stopped:1; + unsigned remote_wakeup:1; + unsigned selfpowered:1; + bool has_hostpc; +}; + + +#endif /* __TEGRA_UDC_H */ -- cgit v1.2.3 From 1acc8a5c8ad269eca22517854582a9be0115fed8 Mon Sep 17 00:00:00 2001 From: Rakesh Bodla Date: Fri, 4 May 2012 15:06:18 +0530 Subject: usb: otg: tegra: Change suspend resume logic Changed the suspend resume logic as per new UDC driver. Also, added few debug prints. Bug 887361 Change-Id: I36ec1f160e8b4db54b5bd2153bdbf1c4fae1cc2a Signed-off-by: Rakesh Bodla Reviewed-on: http://git-master/r/99450 Reviewed-by: Venkat Moganty Reviewed-by: Automatic_Commit_Validation_User --- drivers/usb/otg/tegra-otg.c | 95 ++++++++++++++++++++++++++------------------- 1 file changed, 54 insertions(+), 41 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c index 68d402afb25e..d95d238fd9d6 100644 --- a/drivers/usb/otg/tegra-otg.c +++ b/drivers/usb/otg/tegra-otg.c @@ -42,6 +42,8 @@ #define USB_VBUS_INT_STATUS (1 << 9) #define USB_VBUS_STATUS (1 << 10) #define USB_INTS (USB_VBUS_INT_STATUS | USB_ID_INT_STATUS) +#define USB_INT_EN (USB_VBUS_INT_EN | USB_ID_INT_EN | \ + USB_VBUS_WAKEUP_EN | USB_ID_PIN_WAKEUP_EN) typedef void (*callback_t)(enum usb_otg_state to, enum usb_otg_state from, void *args); @@ -118,13 +120,10 @@ static unsigned long enable_interrupt(struct tegra_otg_data *tegra, bool en) clk_enable(tegra->clk); val = otg_readl(tegra, USB_PHY_WAKEUP); - if (en) { - val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); - val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); - } else { - val &= ~(USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN); - val &= ~(USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN); - } + if (en) + val |= USB_INT_EN; + else + val &= ~USB_INT_EN; otg_writel(tegra, val, USB_PHY_WAKEUP); /* Add delay to make sure register is updated */ udelay(1); @@ -184,19 +183,27 @@ static void tegra_usb_otg_host_unregister(struct platform_device *pdev) void tegra_start_host(struct tegra_otg_data *tegra) { + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + struct tegra_otg_platform_data *pdata = tegra->otg.dev->platform_data; if (!tegra->pdev) { tegra->pdev = tegra_usb_otg_host_register(pdata->ehci_device, pdata->ehci_pdata); } + + DBG("%s(%d) END\n", __func__, __LINE__); } void tegra_stop_host(struct tegra_otg_data *tegra) { + DBG("%s(%d) BEGIN\n", __func__, __LINE__); + if (tegra->pdev) { tegra_usb_otg_host_unregister(tegra->pdev); tegra->pdev = NULL; } + + DBG("%s(%d) END\n", __func__, __LINE__); } int register_otg_callback(callback_t cb, void *args) @@ -297,6 +304,7 @@ static irqreturn_t tegra_otg_irq(int irq, void *data) val = otg_readl(tegra, USB_PHY_WAKEUP); if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) { + DBG("%s(%d) PHY_WAKEUP = 0x%x\n", __func__, __LINE__, val); otg_writel(tegra, val, USB_PHY_WAKEUP); if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) { tegra->int_status = val; @@ -320,25 +328,26 @@ static int tegra_otg_set_peripheral(struct otg_transceiver *otg, { struct tegra_otg_data *tegra; unsigned long val; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); tegra = container_of(otg, struct tegra_otg_data, otg); otg->gadget = gadget; val = enable_interrupt(tegra, true); - if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) { + if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) val |= USB_VBUS_INT_STATUS; - } else if (!(val & USB_ID_STATUS)) { + else if (!(val & USB_ID_STATUS)) val |= USB_ID_INT_STATUS; - } else { + else val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS); - } if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) { tegra->int_status = val; schedule_work (&tegra->work); } + DBG("%s(%d) END\n", __func__, __LINE__); return 0; } @@ -347,6 +356,7 @@ static int tegra_otg_set_host(struct otg_transceiver *otg, { struct tegra_otg_data *tegra; unsigned long val; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); tegra = container_of(otg, struct tegra_otg_data, otg); otg->host = host; @@ -359,6 +369,7 @@ static int tegra_otg_set_host(struct otg_transceiver *otg, otg_writel(tegra, val, USB_PHY_WAKEUP); clk_disable(tegra->clk); + DBG("%s(%d) END\n", __func__, __LINE__); return 0; } @@ -534,20 +545,21 @@ static int tegra_otg_suspend(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); struct otg_transceiver *otg = &tegra_otg->otg; - enum usb_otg_state from = otg->state; - unsigned int val; + int val; + DBG("%s(%d) BEGIN state : %s\n", __func__, __LINE__, + tegra_state_name(otg->state)); - /* store the interupt enable for cable ID and VBUS */ clk_enable(tegra_otg->clk); - tegra_otg->intr_reg_data = readl(tegra_otg->regs + USB_PHY_WAKEUP); - val = tegra_otg->intr_reg_data & ~(USB_ID_INT_EN | USB_VBUS_INT_EN); - writel(val, (tegra_otg->regs + USB_PHY_WAKEUP)); + val = readl(tegra_otg->regs + USB_PHY_WAKEUP); + val &= ~USB_INT_EN; + writel(val, tegra_otg->regs + USB_PHY_WAKEUP); clk_disable(tegra_otg->clk); - if (from == OTG_STATE_B_PERIPHERAL && otg->gadget) { - usb_gadget_vbus_disconnect(otg->gadget); - otg->state = OTG_STATE_A_SUSPEND; - } + /* suspend peripheral mode, host mode is taken care by host driver */ + if (otg->state == OTG_STATE_B_PERIPHERAL) + tegra_change_otg_state(tegra_otg, OTG_STATE_A_SUSPEND); + + DBG("%s(%d) END\n", __func__, __LINE__); tegra_otg_disable_clk(); return 0; } @@ -556,34 +568,35 @@ static void tegra_otg_resume(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev); + struct otg_transceiver *otg = &tegra_otg->otg; + int val; unsigned long flags; + DBG("%s(%d) BEGIN\n", __func__, __LINE__); - tegra_otg_enable_clk(); - - /* Following delay is intentional. - * It is placed here after observing system hang. - * Root cause is not confirmed. - */ - msleep(1); - /* restore the interupt enable for cable ID and VBUS */ + /* Clear pending interrupts */ clk_enable(tegra_otg->clk); - writel(tegra_otg->intr_reg_data, (tegra_otg->regs + USB_PHY_WAKEUP)); val = readl(tegra_otg->regs + USB_PHY_WAKEUP); + writel(val, tegra_otg->regs + USB_PHY_WAKEUP); + DBG("%s(%d) PHY WAKEUP register : 0x%x\n", __func__, __LINE__, val); clk_disable(tegra_otg->clk); - /* A device might be connected while CPU is in sleep mode. In this case no interrupt - * will be triggered - * force irq_work to recheck connected devices - */ - if (!(val & USB_ID_STATUS)) { - spin_lock_irqsave(&tegra_otg->lock, flags); - tegra_otg->int_status = (val | USB_ID_INT_STATUS ); - schedule_work(&tegra_otg->work); - spin_unlock_irqrestore(&tegra_otg->lock, flags); - } + /* Handle if host cable is replaced with device during suspend state */ + if (otg->state == OTG_STATE_A_HOST && (val & USB_ID_STATUS)) + tegra_change_otg_state(tegra_otg, OTG_STATE_A_SUSPEND); + + /* Enable interrupt and call work to set to appropriate state */ + spin_lock_irqsave(&tegra_otg->lock, flags); + tegra_otg->int_status = (val | USB_INT_EN); + spin_unlock_irqrestore(&tegra_otg->lock, flags); + irq_work(&tegra_otg->work); - return; + clk_enable(tegra_otg->clk); + val = readl(tegra_otg->regs + USB_PHY_WAKEUP); + val |= USB_INT_EN; + writel(val, tegra_otg->regs + USB_PHY_WAKEUP); + clk_disable(tegra_otg->clk); + DBG("%s(%d) END\n", __func__, __LINE__); } static const struct dev_pm_ops tegra_otg_pm_ops = { -- cgit v1.2.3 From a3be04f0f9deefa1aef4bb89e7f5991816bb929a Mon Sep 17 00:00:00 2001 From: Krishna Reddy Date: Thu, 3 May 2012 15:36:48 -0700 Subject: video: tegra: nvmap: config option to enable page pools. Add config option to enable/disable nvmap page pools. Change-Id: I873e81a675fecd768534d4ce03c2f8fdd3c6a063 Signed-off-by: Krishna Reddy Reviewed-on: http://git-master/r/100424 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Yu-Huan Hsu --- drivers/video/tegra/Kconfig | 16 ++++++++++++++++ drivers/video/tegra/nvmap/nvmap.h | 5 ++++- drivers/video/tegra/nvmap/nvmap_dev.c | 4 ++++ drivers/video/tegra/nvmap/nvmap_handle.c | 31 ++++++++++++++++++++++++++----- 4 files changed, 50 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/Kconfig b/drivers/video/tegra/Kconfig index 468a5566b667..59e27788680d 100644 --- a/drivers/video/tegra/Kconfig +++ b/drivers/video/tegra/Kconfig @@ -85,6 +85,22 @@ config NVMAP_CARVEOUT_COMPACTOR heap and retries the failed allocation. Say Y here to let nvmap to keep carveout fragmentation under control. +config NVMAP_PAGE_POOLS + bool "Use page pools to reduce allocation overhead" + depends on TEGRA_NVMAP + default y + help + say Y here to reduce the alloction overhead, which is significant + for uncached, writecombine and inner cacheable memories as it + involves changing page attributes during every allocation per page + and flushing cache. Alloc time is reduced by allcoating the pages + ahead and keeping them aside. The reserved pages would be released + when system is low on memory and acquired back during release of + memory. + +config NVMAP_PAGE_POOL_SIZE + hex + default 0x0 config NVMAP_VPR bool "Enable VPR Heap." diff --git a/drivers/video/tegra/nvmap/nvmap.h b/drivers/video/tegra/nvmap/nvmap.h index 44a0d86b6039..87a5a74cf515 100644 --- a/drivers/video/tegra/nvmap/nvmap.h +++ b/drivers/video/tegra/nvmap/nvmap.h @@ -86,7 +86,7 @@ struct nvmap_handle { struct mutex lock; }; -#define NVMAP_DEFAULT_PAGE_POOL_SIZE 8192 +#ifdef CONFIG_NVMAP_PAGE_POOLS #define NVMAP_UC_POOL NVMAP_HANDLE_UNCACHEABLE #define NVMAP_WC_POOL NVMAP_HANDLE_WRITE_COMBINE #define NVMAP_IWB_POOL NVMAP_HANDLE_INNER_CACHEABLE @@ -103,11 +103,13 @@ struct nvmap_page_pool { }; int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags); +#endif struct nvmap_share { struct tegra_iovmm_client *iovmm; wait_queue_head_t pin_wait; struct mutex pin_lock; +#ifdef CONFIG_NVMAP_PAGE_POOLS union { struct nvmap_page_pool pools[NVMAP_NUM_POOLS]; struct { @@ -117,6 +119,7 @@ struct nvmap_share { struct nvmap_page_pool wb_pool; }; }; +#endif #ifdef CONFIG_NVMAP_RECLAIM_UNPINNED_VM struct mutex mru_lock; struct list_head *mru_lists; diff --git a/drivers/video/tegra/nvmap/nvmap_dev.c b/drivers/video/tegra/nvmap/nvmap_dev.c index f84f38c93aad..27c4f61e8956 100644 --- a/drivers/video/tegra/nvmap/nvmap_dev.c +++ b/drivers/video/tegra/nvmap/nvmap_dev.c @@ -1182,8 +1182,10 @@ static int nvmap_probe(struct platform_device *pdev) init_waitqueue_head(&dev->iovmm_master.pin_wait); mutex_init(&dev->iovmm_master.pin_lock); +#ifdef CONFIG_NVMAP_PAGE_POOLS for (i = 0; i < NVMAP_NUM_POOLS; i++) nvmap_page_pool_init(&dev->iovmm_master.pools[i], i); +#endif dev->iovmm_master.iovmm = tegra_iovmm_alloc_client(dev_name(&pdev->dev), NULL, @@ -1311,6 +1313,7 @@ static int nvmap_probe(struct platform_device *pdev) dev, &debug_iovmm_clients_fops); debugfs_create_file("allocations", 0664, iovmm_root, dev, &debug_iovmm_allocations_fops); +#ifdef CONFIG_NVMAP_PAGE_POOLS for (i = 0; i < NVMAP_NUM_POOLS; i++) { char name[40]; char *memtype_string[] = {"uc", "wc", @@ -1321,6 +1324,7 @@ static int nvmap_probe(struct platform_device *pdev) iovmm_root, &dev->iovmm_master.pools[i].npages); } +#endif } } diff --git a/drivers/video/tegra/nvmap/nvmap_handle.c b/drivers/video/tegra/nvmap/nvmap_handle.c index 539b7ce9801f..0708e7468dad 100644 --- a/drivers/video/tegra/nvmap/nvmap_handle.c +++ b/drivers/video/tegra/nvmap/nvmap_handle.c @@ -66,6 +66,9 @@ * preserve kmalloc space, if the array of pages exceeds PAGELIST_VMALLOC_MIN, * the array is allocated using vmalloc. */ #define PAGELIST_VMALLOC_MIN (PAGE_SIZE * 2) + +#ifdef CONFIG_NVMAP_PAGE_POOLS + #define NVMAP_TEST_PAGE_POOL_SHRINKER 1 static bool enable_pp = 1; static int pool_size[NVMAP_NUM_POOLS]; @@ -377,6 +380,7 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) int i; static int reg = 1; struct sysinfo info; + int highmem_pages = 0; typedef int (*set_pages_array) (struct page **pages, int addrinarray); set_pages_array s_cpa[] = { set_pages_array_uc, @@ -395,14 +399,16 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) return 0; si_meminfo(&info); - if (!pool_size[flags]) { + if (!pool_size[flags] && !CONFIG_NVMAP_PAGE_POOL_SIZE) /* Use 3/8th of total ram for page pools. * 1/8th for uc, 1/8th for wc and 1/8th for iwb. */ pool->max_pages = info.totalram >> 3; - } + else + pool->max_pages = CONFIG_NVMAP_PAGE_POOL_SIZE; + if (pool->max_pages <= 0 || pool->max_pages >= info.totalram) - pool->max_pages = NVMAP_DEFAULT_PAGE_POOL_SIZE; + goto fail; pool_size[flags] = pool->max_pages; pr_info("nvmap %s page pool size=%d pages", s_memtype_str[flags], pool->max_pages); @@ -425,7 +431,14 @@ int nvmap_page_pool_init(struct nvmap_page_pool *pool, int flags) __free_page(page); goto do_cpa; } + if (PageHighMem(page)) + highmem_pages++; } + si_meminfo(&info); + pr_info("nvmap pool = %s, highmem=%d, pool_size=%d," + "totalram=%lu, freeram=%lu, totalhigh=%lu, freehigh=%lu", + s_memtype_str[flags], highmem_pages, pool->max_pages, + info.totalram, info.freeram, info.totalhigh, info.freehigh); do_cpa: (*s_cpa[flags])(pool->page_array, pool->npages); nvmap_page_pool_unlock(pool); @@ -436,6 +449,7 @@ fail: vfree(pool->page_array); return -ENOMEM; } +#endif static inline void *altalloc(size_t len) { @@ -460,7 +474,9 @@ void _nvmap_handle_free(struct nvmap_handle *h) { struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); unsigned int i, nr_page, page_index = 0; +#ifdef CONFIG_NVMAP_PAGE_POOLS struct nvmap_page_pool *pool = NULL; +#endif if (nvmap_handle_remove(h->dev, h) != 0) return; @@ -481,6 +497,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) nvmap_mru_remove(share, h); +#ifdef CONFIG_NVMAP_PAGE_POOLS if (h->flags < NVMAP_NUM_POOLS) pool = &share->pools[h->flags]; @@ -490,6 +507,7 @@ void _nvmap_handle_free(struct nvmap_handle *h) break; page_index++; } +#endif if (page_index == nr_page) goto skip_attr_restore; @@ -538,12 +556,14 @@ static int handle_page_alloc(struct nvmap_client *client, struct nvmap_handle *h, bool contiguous) { size_t size = PAGE_ALIGN(h->size); - struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); unsigned int nr_page = size >> PAGE_SHIFT; pgprot_t prot; unsigned int i = 0, page_index = 0; struct page **pages; +#ifdef CONFIG_NVMAP_PAGE_POOLS struct nvmap_page_pool *pool = NULL; + struct nvmap_share *share = nvmap_get_share_from_dev(h->dev); +#endif pages = altalloc(nr_page * sizeof(*pages)); if (!pages) @@ -562,6 +582,7 @@ static int handle_page_alloc(struct nvmap_client *client, pages[i] = nth_page(page, i); } else { +#ifdef CONFIG_NVMAP_PAGE_POOLS if (h->flags < NVMAP_NUM_POOLS) pool = &share->pools[h->flags]; @@ -572,7 +593,7 @@ static int handle_page_alloc(struct nvmap_client *client, break; page_index++; } - +#endif for (; i < nr_page; i++) { pages[i] = nvmap_alloc_pages_exact(GFP_NVMAP, PAGE_SIZE); -- cgit v1.2.3 From bc4ec67309cf4c4bfbf83dec88d76ca641113135 Mon Sep 17 00:00:00 2001 From: Nagarjuna Kristam Date: Tue, 17 Apr 2012 12:14:35 +0530 Subject: bluetooth: tibluesleep: clean up tibluesleep driver Remove UART clock enable code, as UART clock gating is not needed in tibluesleep driver. Remove un-wanted tasklets, workqueues and wakelocks Remove extra lines and spaces Change-Id: I422e09ece2c736c4a98911a5bd84029ad654cb08 Signed-off-by: Nagarjuna Kristam Reviewed-on: http://git-master/r/96944 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Rakesh Goyal Reviewed-by: Bharat Nihalani --- drivers/bluetooth/ti_bluesleep.c | 120 ++------------------------------------- 1 file changed, 5 insertions(+), 115 deletions(-) (limited to 'drivers') diff --git a/drivers/bluetooth/ti_bluesleep.c b/drivers/bluetooth/ti_bluesleep.c index d86fd261e676..2ab532410c72 100644 --- a/drivers/bluetooth/ti_bluesleep.c +++ b/drivers/bluetooth/ti_bluesleep.c @@ -31,37 +31,17 @@ */ #include /* kernel module definitions */ -#include #include #include -#include -#include -#include -#include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include #include -#include -#include #include -#include /* event notifications */ -#include "hci_uart.h" /* * Defines */ - #define VERSION "1.1" #define POLARITY_LOW 0 @@ -70,9 +50,7 @@ struct bluesleep_info { unsigned host_wake_irq; struct uart_port *uport; - struct wake_lock wake_lock; int irq_polarity; - int has_ext_wake; }; @@ -81,16 +59,6 @@ struct bluesleep_info { #define BT_ACTIVE 0x02 #define BT_SUSPEND 0x04 - -/* work function */ -static void hostwake_sleep_work(struct work_struct *work); - -/* work queue */ -DECLARE_DELAYED_WORK(ti_sleep_workqueue, hostwake_sleep_work); - -/* Macros for handling sleep work */ -#define hostwake_workqueue() schedule_delayed_work(&ti_sleep_workqueue, 0) - static struct bluesleep_info *bsi; /* module usage */ @@ -102,66 +70,6 @@ static atomic_t open_count = ATOMIC_INIT(1); /** Global state flags */ static unsigned long flags; -/** Tasklet to respond to change in hostwake line */ -static struct tasklet_struct hostwake_task; - -/** Lock for state transitions */ -static spinlock_t rw_lock; - -/* - * Local functions - */ -static void hsuart_power(int on) -{ - pr_debug("%s", __func__); - - if (on) { - tegra_uart_request_clock_on(bsi->uport); - tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS); - } else { - tegra_uart_set_mctrl(bsi->uport, 0); - tegra_uart_request_clock_off(bsi->uport); - } -} - - - -/** - * @brief@ main sleep work handling function which update the flags - * and activate and deactivate UART . - */ - -static void hostwake_sleep_work(struct work_struct *work) -{ - pr_debug("%s", __func__); - free_irq(bsi->host_wake_irq, "tibluesleep"); - /*Activating UART */ - if (test_bit(BT_SUSPEND, &flags)) { - BT_DBG("Activate UART"); - hsuart_power(1); - - } - bsi->has_ext_wake = 0; - clear_bit(BT_SUSPEND, &flags); - set_bit(BT_ACTIVE, &flags); - -} - - -/** - * A tasklet function that runs in tasklet context - * @param data Not used. - */ -static void bluesleep_hostwake_task(unsigned long data) -{ - pr_debug("%s", __func__); - disable_irq(bsi->host_wake_irq); - spin_lock(&rw_lock); - hostwake_workqueue(); - spin_unlock(&rw_lock); -} - - /** * Schedules a tasklet to run when receiving an interrupt on the * HOST_WAKE GPIO pin. @@ -170,11 +78,8 @@ static void bluesleep_hostwake_task(unsigned long data) */ static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) { - pr_debug("%s", __func__); - /* schedule a tasklet to handle the change in the host wake line */ - bsi->has_ext_wake = 1; - tasklet_schedule(&hostwake_task); + disable_irq_nosync(bsi->host_wake_irq); return IRQ_HANDLED; } @@ -183,8 +88,7 @@ static irqreturn_t bluesleep_hostwake_isr(int irq, void *dev_id) * @return On success, 0. On error, -1, and errno is set * appropriately. */ - - int bluesleep_start(struct uart_port *uport) +int bluesleep_start(struct uart_port *uport) { int retval; bsi->uport = uport; @@ -224,9 +128,8 @@ fail: /** * Stops the Sleep-Mode Protocol on the Host. */ - void bluesleep_stop(void) +void bluesleep_stop(void) { - pr_debug("%s", __func__); if (disable_irq_wake(bsi->host_wake_irq)) @@ -264,7 +167,6 @@ static int bluesleep_probe(struct platform_device *pdev) else bsi->irq_polarity = POLARITY_HIGH;/*anything else*/ - wake_lock_init(&bsi->wake_lock, WAKE_LOCK_SUSPEND, "bluesleep"); clear_bit(BT_SUSPEND, &flags); set_bit(BT_ACTIVE, &flags); @@ -282,17 +184,11 @@ static int bluesleep_remove(struct platform_device *pdev) return 0; } - static int bluesleep_resume(struct platform_device *pdev) { - pr_debug("%s", __func__); if (test_bit(BT_SUSPEND, &flags)) { - - if ((bsi->uport != NULL) && (bsi->has_ext_wake)) { - tegra_uart_request_clock_on(bsi->uport); - tegra_uart_set_mctrl(bsi->uport, TIOCM_RTS); - } + free_irq(bsi->host_wake_irq, "tibluesleep"); clear_bit(BT_SUSPEND, &flags); set_bit(BT_ACTIVE, &flags); } @@ -317,6 +213,7 @@ static struct platform_driver bluesleep_driver = { .owner = THIS_MODULE, }, }; + /** * Initializes the module. * @return On success, 0. On error, -1, and errno is set @@ -337,12 +234,6 @@ static int __init bluesleep_init(void) flags = FLAG_RESET; /* clear all status bits */ - /* Initialize spinlock. */ - spin_lock_init(&rw_lock); - - /* initialize host wake tasklet */ - tasklet_init(&hostwake_task, bluesleep_hostwake_task, 0); - return 0; fail: return retval; @@ -351,7 +242,6 @@ fail: /** * Cleans up the module. */ - static void __exit bluesleep_exit(void) { if (bsi == NULL) -- cgit v1.2.3 From 4bcf4d1cb69470362fa2cbb7691d016513fa8e4d Mon Sep 17 00:00:00 2001 From: Jinyoung Park Date: Wed, 25 Apr 2012 15:21:42 +0900 Subject: mfd: max77663: Unmask EN0 rising interrupt Unmasked EN0 rising interrupt to generate fast PMU_INT by EN0(POWER_KEY). Bug 930883 Change-Id: I9a3d8c4f564e83deea86fbd3d05f14933a0b0f65 Signed-off-by: Jinyoung Park Reviewed-on: http://git-master/r/98665 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/mfd/max77663-core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/max77663-core.c b/drivers/mfd/max77663-core.c index ff47cb123d0d..dbd4fff37e75 100644 --- a/drivers/mfd/max77663-core.c +++ b/drivers/mfd/max77663-core.c @@ -118,6 +118,8 @@ #define ONOFF_SLP_LPM_MASK (1 << 5) +#define ONOFF_IRQ_EN0_RISING (1 << 3) + enum { CACHE_IRQ_LBT, CACHE_IRQ_SD, @@ -1132,6 +1134,10 @@ static int max77663_irq_init(struct max77663_chip *chip) max77663_write(chip->dev, MAX77663_REG_LBT_IRQ_MASK, &chip->cache_irq_mask[CACHE_IRQ_LBT], 1, 0); + chip->cache_irq_mask[CACHE_IRQ_ONOFF] &= ~ONOFF_IRQ_EN0_RISING; + max77663_write(chip->dev, MAX77663_REG_ONOFF_IRQ_MASK, + &chip->cache_irq_mask[CACHE_IRQ_ONOFF], 1, 0); + return 0; } -- cgit v1.2.3 From 62780102a97ec81f8db2b3e9ceacf99843fb6530 Mon Sep 17 00:00:00 2001 From: Jinyoung Park Date: Wed, 28 Mar 2012 12:32:41 +0900 Subject: regulator: max77663: fix fps update condition Fix fps update condition in max77663_regulator_set_fps(). Bug 930883 Change-Id: I2f57603320a91b2727932586fc3c66d9de347d64 signed-off-by: Jinyoung Park Reviewed-on: http://git-master/r/92707 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/regulator/max77663-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/max77663-regulator.c b/drivers/regulator/max77663-regulator.c index c9001c0a3e9f..55d2526b4490 100644 --- a/drivers/regulator/max77663-regulator.c +++ b/drivers/regulator/max77663-regulator.c @@ -280,7 +280,7 @@ static int max77663_regulator_set_fps(struct max77663_regulator *reg) fps_mask |= FPS_PD_PERIOD_MASK; } - if (fps_val) + if (fps_val || fps_mask) ret = max77663_regulator_cache_write(reg, reg->regs[FPS_REG].addr, fps_mask, fps_val, ®->regs[FPS_REG].val); -- cgit v1.2.3 From 94e4c7d5f745f9fd4471796a5321f87508daba7c Mon Sep 17 00:00:00 2001 From: Ramalingam C Date: Fri, 4 May 2012 11:57:55 +0530 Subject: mmc: host: loglevel of a message to KERN_INFO Some boards don't have a vddio regulator for few rails hence not getting the regulator handle. And we assume that those rails are always powered. Hence rephrased the error message and lowered the loglevel to KERN_INFO. Bug 976177 Change-Id: I92b82f75934eaf7137584a625065e3389b6ae1b7 Signed-off-by: Ramalingam C Reviewed-on: http://git-master/r/100490 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/mmc/host/sdhci-tegra.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c index b8f5630058f1..d6ed03d4f22a 100644 --- a/drivers/mmc/host/sdhci-tegra.c +++ b/drivers/mmc/host/sdhci-tegra.c @@ -1035,8 +1035,9 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) } tegra_host->vdd_io_reg = regulator_get(mmc_dev(host->mmc), "vddio_sdmmc"); if (IS_ERR_OR_NULL(tegra_host->vdd_io_reg)) { - dev_err(mmc_dev(host->mmc), "%s regulator not found: %ld\n", - "vddio_sdmmc", PTR_ERR(tegra_host->vdd_io_reg)); + dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld." + "Assuming vddio_sdmmc is not required.\n", + "vddio_sdmmc", PTR_ERR(tegra_host->vdd_io_reg)); tegra_host->vdd_io_reg = NULL; } else { rc = regulator_set_voltage(tegra_host->vdd_io_reg, @@ -1050,8 +1051,9 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev) tegra_host->vdd_slot_reg = regulator_get(mmc_dev(host->mmc), "vddio_sd_slot"); if (IS_ERR_OR_NULL(tegra_host->vdd_slot_reg)) { - dev_err(mmc_dev(host->mmc), "%s regulator not found: %ld\n", - "vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg)); + dev_info(mmc_dev(host->mmc), "%s regulator not found: %ld." + " Assuming vddio_sd_slot is not required.\n", + "vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg)); tegra_host->vdd_slot_reg = NULL; } -- cgit v1.2.3 From e0ce2e5ff81e3825b89babadd784baba96166949 Mon Sep 17 00:00:00 2001 From: Jinyoung Park Date: Mon, 23 Apr 2012 18:49:33 +0900 Subject: mfd: max77663: Add gpio irq masking in irq_sync_unlock Add gpio irq masking in irq_sync_unlock. Change-Id: I008caf58ae82d9ed888f4720f54675e9106f027d Signed-off-by: Jinyoung Park Reviewed-on: http://git-master/r/98664 Reviewed-by: Simone Willett Tested-by: Simone Willett --- drivers/mfd/max77663-core.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mfd/max77663-core.c b/drivers/mfd/max77663-core.c index dbd4fff37e75..46728c331f83 100644 --- a/drivers/mfd/max77663-core.c +++ b/drivers/mfd/max77663-core.c @@ -905,6 +905,8 @@ static void max77663_irq_sync_unlock(struct irq_data *data) irq_mask = irq_data->trigger_type; else irq_mask = GPIO_REFE_IRQ_EDGE_FALLING << shift; + } else { + irq_mask = GPIO_REFE_IRQ_NONE << shift; } ret = max77663_cache_write(chip->dev, GPIO_REG_ADDR(offset), -- cgit v1.2.3 From dfd813141283891e397d73958aabad38eddfe350 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 30 Apr 2012 17:36:24 +0530 Subject: video: tegra: dc: Set default videomode Set default videomode during the dc probe. This patch enables HDMI during the probe only and fixes following issues: 1. Until Xinit there was no display on HDMI. 2. Framebuffer console on HDMI needs it to be enabled well before Xinit. To avoide un-necessary powering on HDMI,Check HPD and enable HDMI only if it's present. Bug: 930136 Bug: 977705 Change-Id: Ifb71328e5df0ccbb5751669db71fd24719fe3738 Signed-off-by: Shashank Sharma Reviewed-on: http://git-master/r/100656 Reviewed-by: Rohan Somvanshi Tested-by: Rohan Somvanshi --- drivers/video/tegra/dc/dc.c | 65 ++++++++++++++++++++++++++++++++++++++----- drivers/video/tegra/dc/hdmi.c | 10 +------ 2 files changed, 59 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c index 11e62815f45a..c42df4c1957e 100644 --- a/drivers/video/tegra/dc/dc.c +++ b/drivers/video/tegra/dc/dc.c @@ -33,6 +33,7 @@ #include #include #include +#include #include