/* * drivers/i2c/busses/i2c-rtcpu.c * * Copyright (C) 2017 NVIDIA Corporation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include "i2c-rtcpu-clk-config.h" #include "i2c-ivc-single.h" #include "i2c-rtcpu-common.h" /* * I2C bus driver internal data structure */ struct tegra_i2c_rtcpu { struct device *dev; struct i2c_adapter adapter; struct tegra_i2c_clk_config i2c_clk_config; u32 i2c_base; bool is_suspended; }; /* * I2C bus driver interface */ static int tegra_i2c_rtcpu_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) { struct tegra_i2c_rtcpu *i2c_dev = i2c_get_adapdata(adap); int ret; if (i2c_dev->is_suspended) return -EBUSY; pm_runtime_get_sync(&adap->dev); ret = tegra_i2c_ivc_single_xfer(i2c_dev->i2c_base, msgs, num); pm_runtime_put(&adap->dev); return ret; } static u32 tegra_i2c_rtcpu_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR | I2C_FUNC_NOSTART | I2C_FUNC_SMBUS_EMUL; } static const struct i2c_algorithm tegra_i2c_rtcpu_algo = { .master_xfer = tegra_i2c_rtcpu_xfer, .functionality = tegra_i2c_rtcpu_func, }; /* * Probe and initialization */ static int tegra_i2c_rtcpu_probe(struct platform_device *pdev) { struct tegra_i2c_rtcpu *i2c_dev; struct resource *res; struct clk *div_clk; struct clk *parent_clk; struct clk *slow_clk; u32 i2c_base; void __iomem *base; int ret = 0; struct tegra_i2c_clk_config *i2c_clk_config; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); i2c_base = tegra_i2c_get_reg_base(pdev->dev.of_node); if (i2c_base == 0) return -ENODEV; if (tegra_i2c_ivc_single_xfer(i2c_base, NULL, 0) != 0) return -EPROBE_DEFER; base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(base)) return PTR_ERR(base); div_clk = devm_clk_get(&pdev->dev, "div-clk"); if (IS_ERR(div_clk)) { dev_err(&pdev->dev, "missing controller clock"); return PTR_ERR(div_clk); } parent_clk = devm_clk_get(&pdev->dev, "parent"); if (IS_ERR(parent_clk)) { dev_err(&pdev->dev, "Unable to get parent_clk err:%ld\n", PTR_ERR(parent_clk)); } else { ret = clk_set_parent(div_clk, parent_clk); if (ret < 0) dev_warn(&pdev->dev, "Couldn't set parent clock : %d\n", ret); } slow_clk = devm_clk_get(&pdev->dev, "slow-clk"); if (IS_ERR(slow_clk)) { dev_err(&pdev->dev, "missing slow clock\n"); slow_clk = NULL; } /* Create a private data */ i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL); if (!i2c_dev) return -ENOMEM; platform_set_drvdata(pdev, i2c_dev); i2c_dev->dev = &pdev->dev; i2c_dev->i2c_base = i2c_base; i2c_dev->is_suspended = false; i2c_clk_config = &i2c_dev->i2c_clk_config; i2c_clk_config->base = base; i2c_clk_config->div_clk = div_clk; i2c_clk_config->slow_clk = slow_clk; i2c_clk_config->rst = devm_reset_control_get(&pdev->dev, "i2c"); if (IS_ERR(i2c_clk_config->rst)) { dev_err(i2c_dev->dev, "missing controller reset"); return PTR_ERR(i2c_clk_config->rst); } i2c_clk_config->bus_clk_rate = tegra_i2c_get_clk_freq( i2c_dev->dev->of_node); i2c_clk_config->is_clkon_always = of_property_read_bool(pdev->dev.of_node, "nvidia,clock-always-on"); i2c_clk_config->clk_divisor_non_hs_mode = 0x19; if (i2c_clk_config->bus_clk_rate == I2C_FAST_MODE_PLUS) i2c_clk_config->clk_divisor_non_hs_mode = 0x10; ret = clk_prepare(i2c_clk_config->div_clk); if (ret < 0) { dev_err(i2c_dev->dev, "Clock prepare failed %d\n", ret); goto fail; } if (i2c_clk_config->slow_clk) { ret = clk_prepare(i2c_clk_config->slow_clk); if (ret < 0) { dev_err(i2c_dev->dev, "slow clk prep failed %d\n", ret); goto unprepare_div_clk; } } ret = tegra_i2c_rtcpu_clock_enable(i2c_clk_config); if (ret < 0) { dev_err(i2c_dev->dev, "div_clk enable failed %d\n", ret); goto unprepare_slow_clk; } ret = tegra_i2c_rtcpu_clock_init(i2c_clk_config); if (ret) { dev_err(i2c_dev->dev, "Failed to initialize i2c controller"); goto disable_clk; } /* Probe the I2C hardware */ pm_runtime_enable(&pdev->dev); /* Register I2C bus */ i2c_set_adapdata(&i2c_dev->adapter, i2c_dev); i2c_dev->adapter.owner = THIS_MODULE; i2c_dev->adapter.class = I2C_CLASS_DEPRECATED; strlcpy(i2c_dev->adapter.name, "Tegra CAMRTC I2C adapter", sizeof(i2c_dev->adapter.name)); i2c_dev->adapter.algo = &tegra_i2c_rtcpu_algo; i2c_dev->adapter.dev.parent = &pdev->dev; i2c_dev->adapter.nr = pdev->id; i2c_dev->adapter.dev.of_node = pdev->dev.of_node; ret = i2c_add_numbered_adapter(&i2c_dev->adapter); if (ret) { dev_err(i2c_dev->dev, "Cannot add I2C adapter: %d\n", ret); goto pm_disable; } return 0; pm_disable: pm_runtime_disable(&pdev->dev); disable_clk: tegra_i2c_rtcpu_clock_disable(i2c_clk_config); unprepare_slow_clk: if (i2c_clk_config->slow_clk) clk_unprepare(i2c_clk_config->slow_clk); unprepare_div_clk: clk_unprepare(i2c_clk_config->div_clk); fail: return ret; } static int tegra_i2c_rtcpu_remove(struct platform_device *pdev) { struct tegra_i2c_rtcpu *i2c_dev = platform_get_drvdata(pdev); i2c_del_adapter(&i2c_dev->adapter); pm_runtime_disable(&pdev->dev); return 0; } static void tegra_i2c_rtcpu_shutdown(struct platform_device *pdev) { struct tegra_i2c_rtcpu *i2c_dev = platform_get_drvdata(pdev); dev_info(i2c_dev->dev, "Bus is shutdown down..\n"); i2c_shutdown_adapter(&i2c_dev->adapter); } /* * I2C bus driver PM interface */ #ifdef CONFIG_PM_SLEEP static int tegra_i2c_rtcpu_suspend(struct device *dev) { struct tegra_i2c_rtcpu *i2c_dev = dev_get_drvdata(dev); i2c_lock_adapter(&i2c_dev->adapter); i2c_dev->is_suspended = true; i2c_unlock_adapter(&i2c_dev->adapter); return 0; } static int tegra_i2c_rtcpu_resume(struct device *dev) { struct tegra_i2c_rtcpu *i2c_dev = dev_get_drvdata(dev); i2c_lock_adapter(&i2c_dev->adapter); i2c_dev->is_suspended = false; i2c_unlock_adapter(&i2c_dev->adapter); return 0; } static SIMPLE_DEV_PM_OPS(tegra_i2c_rtcpu_pm, tegra_i2c_rtcpu_suspend, tegra_i2c_rtcpu_resume); #define TEGRA_I2C_PM (&tegra_i2c_rtcpu_pm) #else #define TEGRA_I2C_PM NULL #endif /* * I2C bus driver interface */ static const struct of_device_id tegra_i2c_rtcpu_of_match[] = { { .compatible = "nvidia,tegra-i2c-rtcpu", .data = NULL, }, {}, }; MODULE_DEVICE_TABLE(of, tegra_i2c_rtcpu_of_match); static struct platform_driver tegra_i2c_rtcpu_driver = { .probe = tegra_i2c_rtcpu_probe, .remove = tegra_i2c_rtcpu_remove, .late_shutdown = tegra_i2c_rtcpu_shutdown, .driver = { .name = "tegra-i2c-rtcpu", .of_match_table = tegra_i2c_rtcpu_of_match, .pm = TEGRA_I2C_PM, }, }; static int __init tegra_i2c_rtcpu_init_driver(void) { return platform_driver_register(&tegra_i2c_rtcpu_driver); } subsys_initcall(tegra_i2c_rtcpu_init_driver);