--- zzzz-none-000/linux-3.10.107/drivers/i2c/busses/i2c-s3c2410.c 2017-06-27 09:49:32.000000000 +0000 +++ scorpion-7490-727/linux-3.10.107/drivers/i2c/busses/i2c-s3c2410.c 2021-02-04 17:41:59.000000000 +0000 @@ -14,10 +14,6 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include @@ -36,9 +32,11 @@ #include #include #include -#include +#include #include #include +#include +#include #include @@ -86,10 +84,14 @@ #define QUIRK_S3C2440 (1 << 0) #define QUIRK_HDMIPHY (1 << 1) #define QUIRK_NO_GPIO (1 << 2) +#define QUIRK_POLL (1 << 3) /* Max time to wait for bus to become idle after a xfer (in us) */ #define S3C2410_IDLE_TIMEOUT 5000 +/* Exynos5 Sysreg offset */ +#define EXYNOS5_SYS_I2C_CFG 0x0234 + /* i2c controller state */ enum s3c24xx_i2c_state { STATE_IDLE, @@ -101,7 +103,7 @@ struct s3c24xx_i2c { wait_queue_head_t wait; - unsigned int quirks; + kernel_ulong_t quirks; unsigned int suspended:1; struct i2c_msg *msg; @@ -123,12 +125,14 @@ struct s3c2410_platform_i2c *pdata; int gpios[2]; struct pinctrl *pctrl; -#ifdef CONFIG_CPU_FREQ +#if defined(CONFIG_ARM_S3C24XX_CPUFREQ) struct notifier_block freq_transition; #endif + struct regmap *sysreg; + unsigned int sys_i2c_cfg; }; -static struct platform_device_id s3c24xx_driver_ids[] = { +static const struct platform_device_id s3c24xx_driver_ids[] = { { .name = "s3c2410-i2c", .driver_data = 0, @@ -142,6 +146,8 @@ }; MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); +static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat); + #ifdef CONFIG_OF static const struct of_device_id s3c24xx_i2c_match[] = { { .compatible = "samsung,s3c2410-i2c", .data = (void *)0 }, @@ -150,6 +156,8 @@ .data = (void *)(QUIRK_S3C2440 | QUIRK_HDMIPHY | QUIRK_NO_GPIO) }, { .compatible = "samsung,exynos5440-i2c", .data = (void *)(QUIRK_S3C2440 | QUIRK_NO_GPIO) }, + { .compatible = "samsung,exynos5-sata-phy-i2c", + .data = (void *)(QUIRK_S3C2440 | QUIRK_POLL | QUIRK_NO_GPIO) }, {}, }; MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match); @@ -160,12 +168,12 @@ * Get controller type either from device tree or platform device variant. */ -static inline unsigned int s3c24xx_get_device_quirks(struct platform_device *pdev) +static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev) { if (pdev->dev.of_node) { const struct of_device_id *match; match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node); - return (unsigned int)match->data; + return (kernel_ulong_t)match->data; } return platform_get_device_id(pdev)->driver_data; @@ -188,7 +196,8 @@ if (ret) i2c->msg_idx = ret; - wake_up(&i2c->wait); + if (!(i2c->quirks & QUIRK_POLL)) + wake_up(&i2c->wait); } static inline void s3c24xx_i2c_disable_ack(struct s3c24xx_i2c *i2c) @@ -225,6 +234,22 @@ writel(tmp | S3C2410_IICCON_IRQEN, i2c->regs + S3C2410_IICCON); } +static bool is_ack(struct s3c24xx_i2c *i2c) +{ + int tries; + + for (tries = 50; tries; --tries) { + if (readl(i2c->regs + S3C2410_IICCON) + & S3C2410_IICCON_IRQPEND) { + if (!(readl(i2c->regs + S3C2410_IICSTAT) + & S3C2410_IICSTAT_LASTBIT)) + return true; + } + usleep_range(1000, 2000); + } + dev_err(i2c->dev, "ack was not received\n"); + return false; +} /* s3c24xx_i2c_message_start * @@ -269,6 +294,16 @@ stat |= S3C2410_IICSTAT_START; writel(stat, i2c->regs + S3C2410_IICSTAT); + + if (i2c->quirks & QUIRK_POLL) { + while ((i2c->msg_num != 0) && is_ack(i2c)) { + i2c_s3c_irq_nextbyte(i2c, stat); + stat = readl(i2c->regs + S3C2410_IICSTAT); + + if (stat & S3C2410_IICSTAT_ARBITR) + dev_err(i2c->dev, "deal with arbitration loss\n"); + } + } } static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret) @@ -569,6 +604,31 @@ return IRQ_HANDLED; } +/* + * Disable the bus so that we won't get any interrupts from now on, or try + * to drive any lines. This is the default state when we don't have + * anything to send/receive. + * + * If there is an event on the bus, or we have a pre-existing event at + * kernel boot time, we may not notice the event and the I2C controller + * will lock the bus with the I2C clock line low indefinitely. + */ +static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c) +{ + unsigned long tmp; + + /* Stop driving the I2C pins */ + tmp = readl(i2c->regs + S3C2410_IICSTAT); + tmp &= ~S3C2410_IICSTAT_TXRXEN; + writel(tmp, i2c->regs + S3C2410_IICSTAT); + + /* We don't expect any interrupts now, and don't want send acks */ + tmp = readl(i2c->regs + S3C2410_IICCON); + tmp &= ~(S3C2410_IICCON_IRQEN | S3C2410_IICCON_IRQPEND | + S3C2410_IICCON_ACKEN); + writel(tmp, i2c->regs + S3C2410_IICCON); +} + /* s3c24xx_i2c_set_master * @@ -676,6 +736,15 @@ s3c24xx_i2c_enable_irq(i2c); s3c24xx_i2c_message_start(i2c, msgs); + if (i2c->quirks & QUIRK_POLL) { + ret = i2c->msg_idx; + + if (ret != num) + dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret); + + goto out; + } + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); ret = i2c->msg_idx; @@ -694,7 +763,11 @@ s3c24xx_i2c_wait_idle(i2c); + s3c24xx_i2c_disable_bus(i2c); + out: + i2c->state = STATE_IDLE; + return ret; } @@ -712,14 +785,16 @@ int ret; pm_runtime_get_sync(&adap->dev); - clk_prepare_enable(i2c->clk); + ret = clk_enable(i2c->clk); + if (ret) + return ret; for (retry = 0; retry < adap->retries; retry++) { ret = s3c24xx_i2c_doxfer(i2c, msgs, num); if (ret != -EAGAIN) { - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); pm_runtime_put(&adap->dev); return ret; } @@ -729,7 +804,7 @@ udelay(100); } - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); pm_runtime_put(&adap->dev); return -EREMOTEIO; } @@ -821,6 +896,9 @@ if (div1 == 512) iiccon |= S3C2410_IICCON_TXDIV_512; + if (i2c->quirks & QUIRK_POLL) + iiccon |= S3C2410_IICCON_SCALE(2); + writel(iiccon, i2c->regs + S3C2410_IICCON); if (i2c->quirks & QUIRK_S3C2440) { @@ -843,7 +921,7 @@ return 0; } -#ifdef CONFIG_CPU_FREQ +#if defined(CONFIG_ARM_S3C24XX_CPUFREQ) #define freq_to_i2c(_n) container_of(_n, struct s3c24xx_i2c, freq_transition) @@ -960,7 +1038,6 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c) { - unsigned long iicon = S3C2410_IICCON_IRQEN | S3C2410_IICCON_ACKEN; struct s3c2410_platform_i2c *pdata; unsigned int freq; @@ -974,12 +1051,12 @@ dev_info(i2c->dev, "slave address 0x%02x\n", pdata->slave_addr); - writel(iicon, i2c->regs + S3C2410_IICCON); + writel(0, i2c->regs + S3C2410_IICCON); + writel(0, i2c->regs + S3C2410_IICSTAT); /* we need to work out the divisors for the clock... */ if (s3c24xx_i2c_clockrate(i2c, &freq) != 0) { - writel(0, i2c->regs + S3C2410_IICCON); dev_err(i2c->dev, "cannot meet bus frequency required\n"); return -EINVAL; } @@ -987,7 +1064,8 @@ /* todo - check that the i2c lines aren't being dragged anywhere */ dev_info(i2c->dev, "bus frequency set to %d KHz\n", freq); - dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02lx\n", iicon); + dev_dbg(i2c->dev, "S3C2410_IICCON=0x%02x\n", + readl(i2c->regs + S3C2410_IICCON)); return 0; } @@ -1002,6 +1080,7 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) { struct s3c2410_platform_i2c *pdata = i2c->pdata; + int id; if (!np) return; @@ -1011,6 +1090,21 @@ of_property_read_u32(np, "samsung,i2c-slave-addr", &pdata->slave_addr); of_property_read_u32(np, "samsung,i2c-max-bus-freq", (u32 *)&pdata->frequency); + /* + * Exynos5's legacy i2c controller and new high speed i2c + * controller have muxed interrupt sources. By default the + * interrupts for 4-channel HS-I2C controller are enabled. + * If nodes for first four channels of legacy i2c controller + * are available then re-configure the interrupts via the + * system register. + */ + id = of_alias_get_id(np, "i2c"); + i2c->sysreg = syscon_regmap_lookup_by_phandle(np, + "samsung,sysreg-phandle"); + if (IS_ERR(i2c->sysreg)) + return; + + regmap_update_bits(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, BIT(id), 0); } #else static void @@ -1033,7 +1127,7 @@ int ret; if (!pdev->dev.of_node) { - pdata = pdev->dev.platform_data; + pdata = dev_get_platdata(&pdev->dev); if (!pdata) { dev_err(&pdev->dev, "no platform data\n"); return -EINVAL; @@ -1041,29 +1135,26 @@ } i2c = devm_kzalloc(&pdev->dev, sizeof(struct s3c24xx_i2c), GFP_KERNEL); - if (!i2c) { - dev_err(&pdev->dev, "no memory for state\n"); + if (!i2c) return -ENOMEM; - } i2c->pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!i2c->pdata) { - dev_err(&pdev->dev, "no memory for platform data\n"); + if (!i2c->pdata) return -ENOMEM; - } i2c->quirks = s3c24xx_get_device_quirks(pdev); + i2c->sysreg = ERR_PTR(-ENOENT); if (pdata) memcpy(i2c->pdata, pdata, sizeof(*pdata)); else s3c24xx_i2c_parse_dt(pdev->dev.of_node, i2c); strlcpy(i2c->adap.name, "s3c2410-i2c", sizeof(i2c->adap.name)); - i2c->adap.owner = THIS_MODULE; - i2c->adap.algo = &s3c24xx_i2c_algorithm; + i2c->adap.owner = THIS_MODULE; + i2c->adap.algo = &s3c24xx_i2c_algorithm; i2c->adap.retries = 2; - i2c->adap.class = I2C_CLASS_HWMON | I2C_CLASS_SPD; - i2c->tx_setup = 50; + i2c->adap.class = I2C_CLASS_DEPRECATED; + i2c->tx_setup = 50; init_waitqueue_head(&i2c->wait); @@ -1109,7 +1200,7 @@ clk_prepare_enable(i2c->clk); ret = s3c24xx_i2c_init(i2c); - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); if (ret != 0) { dev_err(&pdev->dev, "I2C controller init failed\n"); return ret; @@ -1118,23 +1209,28 @@ * ensure no current IRQs pending */ - i2c->irq = ret = platform_get_irq(pdev, 0); - if (ret <= 0) { - dev_err(&pdev->dev, "cannot find IRQ\n"); - return ret; - } + if (!(i2c->quirks & QUIRK_POLL)) { + i2c->irq = ret = platform_get_irq(pdev, 0); + if (ret <= 0) { + dev_err(&pdev->dev, "cannot find IRQ\n"); + clk_unprepare(i2c->clk); + return ret; + } ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0, - dev_name(&pdev->dev), i2c); + dev_name(&pdev->dev), i2c); - if (ret != 0) { - dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); - return ret; + if (ret != 0) { + dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq); + clk_unprepare(i2c->clk); + return ret; + } } ret = s3c24xx_i2c_register_cpufreq(i2c); if (ret < 0) { dev_err(&pdev->dev, "failed to register cpufreq notifier\n"); + clk_unprepare(i2c->clk); return ret; } @@ -1147,17 +1243,19 @@ i2c->adap.nr = i2c->pdata->bus_num; i2c->adap.dev.of_node = pdev->dev.of_node; + platform_set_drvdata(pdev, i2c); + + pm_runtime_enable(&pdev->dev); + ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + pm_runtime_disable(&pdev->dev); s3c24xx_i2c_deregister_cpufreq(i2c); + clk_unprepare(i2c->clk); return ret; } - of_i2c_register_devices(&i2c->adap); - platform_set_drvdata(pdev, i2c); - - pm_runtime_enable(&pdev->dev); pm_runtime_enable(&i2c->adap.dev); dev_info(&pdev->dev, "%s: S3C I2C adapter\n", dev_name(&i2c->adap.dev)); @@ -1173,6 +1271,8 @@ { struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + clk_unprepare(i2c->clk); + pm_runtime_disable(&i2c->adap.dev); pm_runtime_disable(&pdev->dev); @@ -1180,8 +1280,6 @@ i2c_del_adapter(&i2c->adap); - clk_disable_unprepare(i2c->clk); - if (pdev->dev.of_node && IS_ERR(i2c->pctrl)) s3c24xx_i2c_dt_gpio_free(i2c); @@ -1196,17 +1294,26 @@ i2c->suspended = 1; + if (!IS_ERR(i2c->sysreg)) + regmap_read(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, &i2c->sys_i2c_cfg); + return 0; } -static int s3c24xx_i2c_resume(struct device *dev) +static int s3c24xx_i2c_resume_noirq(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + int ret; - clk_prepare_enable(i2c->clk); + if (!IS_ERR(i2c->sysreg)) + regmap_write(i2c->sysreg, EXYNOS5_SYS_I2C_CFG, i2c->sys_i2c_cfg); + + ret = clk_enable(i2c->clk); + if (ret) + return ret; s3c24xx_i2c_init(i2c); - clk_disable_unprepare(i2c->clk); + clk_disable(i2c->clk); i2c->suspended = 0; return 0; @@ -1217,7 +1324,11 @@ static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { #ifdef CONFIG_PM_SLEEP .suspend_noirq = s3c24xx_i2c_suspend_noirq, - .resume = s3c24xx_i2c_resume, + .resume_noirq = s3c24xx_i2c_resume_noirq, + .freeze_noirq = s3c24xx_i2c_suspend_noirq, + .thaw_noirq = s3c24xx_i2c_resume_noirq, + .poweroff_noirq = s3c24xx_i2c_suspend_noirq, + .restore_noirq = s3c24xx_i2c_resume_noirq, #endif }; @@ -1233,7 +1344,6 @@ .remove = s3c24xx_i2c_remove, .id_table = s3c24xx_driver_ids, .driver = { - .owner = THIS_MODULE, .name = "s3c-i2c", .pm = S3C24XX_DEV_PM_OPS, .of_match_table = of_match_ptr(s3c24xx_i2c_match),