0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk.h>
0009 #include <linux/i2c.h>
0010 #include <linux/io.h>
0011 #include <linux/kernel.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/types.h>
0015
0016 #include "i2c-pasemi-core.h"
0017
0018 struct pasemi_platform_i2c_data {
0019 struct pasemi_smbus smbus;
0020 struct clk *clk_ref;
0021 };
0022
0023 static int
0024 pasemi_platform_i2c_calc_clk_div(struct pasemi_platform_i2c_data *data,
0025 u32 frequency)
0026 {
0027 unsigned long clk_rate = clk_get_rate(data->clk_ref);
0028
0029 if (!clk_rate)
0030 return -EINVAL;
0031
0032 data->smbus.clk_div = DIV_ROUND_UP(clk_rate, 16 * frequency);
0033 if (data->smbus.clk_div < 4)
0034 return dev_err_probe(data->smbus.dev, -EINVAL,
0035 "Bus frequency %d is too fast.\n",
0036 frequency);
0037 if (data->smbus.clk_div > 0xff)
0038 return dev_err_probe(data->smbus.dev, -EINVAL,
0039 "Bus frequency %d is too slow.\n",
0040 frequency);
0041
0042 return 0;
0043 }
0044
0045 static int pasemi_platform_i2c_probe(struct platform_device *pdev)
0046 {
0047 struct device *dev = &pdev->dev;
0048 struct pasemi_platform_i2c_data *data;
0049 struct pasemi_smbus *smbus;
0050 u32 frequency;
0051 int error;
0052
0053 data = devm_kzalloc(dev, sizeof(struct pasemi_platform_i2c_data),
0054 GFP_KERNEL);
0055 if (!data)
0056 return -ENOMEM;
0057
0058 smbus = &data->smbus;
0059 smbus->dev = dev;
0060
0061 smbus->ioaddr = devm_platform_ioremap_resource(pdev, 0);
0062 if (IS_ERR(smbus->ioaddr))
0063 return PTR_ERR(smbus->ioaddr);
0064
0065 if (of_property_read_u32(dev->of_node, "clock-frequency", &frequency))
0066 frequency = I2C_MAX_STANDARD_MODE_FREQ;
0067
0068 data->clk_ref = devm_clk_get(dev, NULL);
0069 if (IS_ERR(data->clk_ref))
0070 return PTR_ERR(data->clk_ref);
0071
0072 error = clk_prepare_enable(data->clk_ref);
0073 if (error)
0074 return error;
0075
0076 error = pasemi_platform_i2c_calc_clk_div(data, frequency);
0077 if (error)
0078 goto out_clk_disable;
0079
0080 smbus->adapter.dev.of_node = pdev->dev.of_node;
0081 error = pasemi_i2c_common_probe(smbus);
0082 if (error)
0083 goto out_clk_disable;
0084
0085 platform_set_drvdata(pdev, data);
0086
0087 return 0;
0088
0089 out_clk_disable:
0090 clk_disable_unprepare(data->clk_ref);
0091
0092 return error;
0093 }
0094
0095 static int pasemi_platform_i2c_remove(struct platform_device *pdev)
0096 {
0097 struct pasemi_platform_i2c_data *data = platform_get_drvdata(pdev);
0098
0099 clk_disable_unprepare(data->clk_ref);
0100 return 0;
0101 }
0102
0103 static const struct of_device_id pasemi_platform_i2c_of_match[] = {
0104 { .compatible = "apple,t8103-i2c" },
0105 { .compatible = "apple,i2c" },
0106 {},
0107 };
0108 MODULE_DEVICE_TABLE(of, pasemi_platform_i2c_of_match);
0109
0110 static struct platform_driver pasemi_platform_i2c_driver = {
0111 .driver = {
0112 .name = "i2c-apple",
0113 .of_match_table = pasemi_platform_i2c_of_match,
0114 },
0115 .probe = pasemi_platform_i2c_probe,
0116 .remove = pasemi_platform_i2c_remove,
0117 };
0118 module_platform_driver(pasemi_platform_i2c_driver);
0119
0120 MODULE_LICENSE("GPL");
0121 MODULE_AUTHOR("Sven Peter <sven@svenpeter.dev>");
0122 MODULE_DESCRIPTION("Apple/PASemi SMBus platform driver");