/* * P2WI (Push-Pull Two Wire Interface) bus driver. * * Author: Boris BREZILLON <boris.brezillon@free-electrons.com> * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of any * kind, whether express or implied. * * The P2WI controller looks like an SMBus controller which only supports byte * data transfers. But, it differs from standard SMBus protocol on several * aspects: * - it supports only one target device, and thus drop the address field * - it adds a parity bit every 8bits of data * - only one read access is required to read a byte (instead of a write * followed by a read access in standard SMBus protocol) * - there's no Ack bit after each byte transfer * * This means this bus cannot be used to interface with standard SMBus * devices (the only known device to support this interface is the AXP221 * PMIC). *
*/ #include <linux/clk.h> #include <linux/i2c.h> #include <linux/io.h> #include <linux/interrupt.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/reset.h>
of_property_read_u32(np, "clock-frequency", &clk_freq); if (clk_freq > P2WI_MAX_FREQ) {
dev_err(dev, "required clock-frequency (%u Hz) is too high (max = 6MHz)",
clk_freq); return -EINVAL;
}
if (clk_freq == 0) {
dev_err(dev, "clock-frequency is set to 0 in DT\n"); return -EINVAL;
}
if (of_get_child_count(np) > 1) {
dev_err(dev, "P2WI only supports one target device\n"); return -EINVAL;
}
p2wi = devm_kzalloc(dev, sizeof(struct p2wi), GFP_KERNEL); if (!p2wi) return -ENOMEM;
p2wi->target_addr = -1;
/* * Authorize a p2wi node without any children to be able to use an * i2c-dev from userpace. * In this case the target_addr is set to -1 and won't be checked when * launching a P2WI transfer.
*/
childnp = of_get_next_available_child(np, NULL); if (childnp) {
ret = of_property_read_u32(childnp, "reg", &target_addr); if (ret) {
dev_err(dev, "invalid target address on node %pOF\n",
childnp); return -EINVAL;
}
p2wi->target_addr = target_addr;
}
p2wi->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(p2wi->regs)) return PTR_ERR(p2wi->regs);
clk_div = parent_clk_freq / clk_freq; if (!clk_div) {
dev_warn(dev, "clock-frequency is too high, setting it to %lu Hz\n",
parent_clk_freq);
clk_div = 1;
} elseif (clk_div > P2WI_CCR_MAX_CLK_DIV) {
dev_warn(dev, "clock-frequency is too low, setting it to %lu Hz\n",
parent_clk_freq / P2WI_CCR_MAX_CLK_DIV);
clk_div = P2WI_CCR_MAX_CLK_DIV;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.