18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * i2c_pca_platform.c 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Platform driver for the PCA9564 I2C controller. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2008 Pengutronix 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/module.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/delay.h> 158c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 168c2ecf20Sopenharmony_ci#include <linux/errno.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c.h> 188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 198c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 208c2ecf20Sopenharmony_ci#include <linux/i2c-algo-pca.h> 218c2ecf20Sopenharmony_ci#include <linux/platform_data/i2c-pca-platform.h> 228c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 238c2ecf20Sopenharmony_ci#include <linux/io.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/of_device.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <asm/irq.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistruct i2c_pca_pf_data { 308c2ecf20Sopenharmony_ci void __iomem *reg_base; 318c2ecf20Sopenharmony_ci int irq; /* if 0, use polling */ 328c2ecf20Sopenharmony_ci struct gpio_desc *gpio; 338c2ecf20Sopenharmony_ci wait_queue_head_t wait; 348c2ecf20Sopenharmony_ci struct i2c_adapter adap; 358c2ecf20Sopenharmony_ci struct i2c_algo_pca_data algo_data; 368c2ecf20Sopenharmony_ci unsigned long io_base; 378c2ecf20Sopenharmony_ci unsigned long io_size; 388c2ecf20Sopenharmony_ci}; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci/* Read/Write functions for different register alignments */ 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int i2c_pca_pf_readbyte8(void *pd, int reg) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 458c2ecf20Sopenharmony_ci return ioread8(i2c->reg_base + reg); 468c2ecf20Sopenharmony_ci} 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_cistatic int i2c_pca_pf_readbyte16(void *pd, int reg) 498c2ecf20Sopenharmony_ci{ 508c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 518c2ecf20Sopenharmony_ci return ioread8(i2c->reg_base + reg * 2); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int i2c_pca_pf_readbyte32(void *pd, int reg) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 578c2ecf20Sopenharmony_ci return ioread8(i2c->reg_base + reg * 4); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void i2c_pca_pf_writebyte8(void *pd, int reg, int val) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 638c2ecf20Sopenharmony_ci iowrite8(val, i2c->reg_base + reg); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void i2c_pca_pf_writebyte16(void *pd, int reg, int val) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 698c2ecf20Sopenharmony_ci iowrite8(val, i2c->reg_base + reg * 2); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic void i2c_pca_pf_writebyte32(void *pd, int reg, int val) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 758c2ecf20Sopenharmony_ci iowrite8(val, i2c->reg_base + reg * 4); 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_cistatic int i2c_pca_pf_waitforcompletion(void *pd) 808c2ecf20Sopenharmony_ci{ 818c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 828c2ecf20Sopenharmony_ci unsigned long timeout; 838c2ecf20Sopenharmony_ci long ret; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (i2c->irq) { 868c2ecf20Sopenharmony_ci ret = wait_event_timeout(i2c->wait, 878c2ecf20Sopenharmony_ci i2c->algo_data.read_byte(i2c, I2C_PCA_CON) 888c2ecf20Sopenharmony_ci & I2C_PCA_CON_SI, i2c->adap.timeout); 898c2ecf20Sopenharmony_ci } else { 908c2ecf20Sopenharmony_ci /* Do polling */ 918c2ecf20Sopenharmony_ci timeout = jiffies + i2c->adap.timeout; 928c2ecf20Sopenharmony_ci do { 938c2ecf20Sopenharmony_ci ret = time_before(jiffies, timeout); 948c2ecf20Sopenharmony_ci if (i2c->algo_data.read_byte(i2c, I2C_PCA_CON) 958c2ecf20Sopenharmony_ci & I2C_PCA_CON_SI) 968c2ecf20Sopenharmony_ci break; 978c2ecf20Sopenharmony_ci udelay(100); 988c2ecf20Sopenharmony_ci } while (ret); 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return ret > 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic void i2c_pca_pf_dummyreset(void *pd) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci dev_warn(&i2c->adap.dev, "No reset-pin found. Chip may get stuck!\n"); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_cistatic void i2c_pca_pf_resetchip(void *pd) 1128c2ecf20Sopenharmony_ci{ 1138c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = pd; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci gpiod_set_value(i2c->gpio, 1); 1168c2ecf20Sopenharmony_ci ndelay(100); 1178c2ecf20Sopenharmony_ci gpiod_set_value(i2c->gpio, 0); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_cistatic irqreturn_t i2c_pca_pf_handler(int this_irq, void *dev_id) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = dev_id; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci if ((i2c->algo_data.read_byte(i2c, I2C_PCA_CON) & I2C_PCA_CON_SI) == 0) 1258c2ecf20Sopenharmony_ci return IRQ_NONE; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci wake_up(&i2c->wait); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int i2c_pca_pf_probe(struct platform_device *pdev) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c; 1368c2ecf20Sopenharmony_ci struct resource *res; 1378c2ecf20Sopenharmony_ci struct i2c_pca9564_pf_platform_data *platform_data = 1388c2ecf20Sopenharmony_ci dev_get_platdata(&pdev->dev); 1398c2ecf20Sopenharmony_ci struct device_node *np = pdev->dev.of_node; 1408c2ecf20Sopenharmony_ci int ret = 0; 1418c2ecf20Sopenharmony_ci int irq; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci irq = platform_get_irq_optional(pdev, 0); 1448c2ecf20Sopenharmony_ci /* If irq is 0, we do polling. */ 1458c2ecf20Sopenharmony_ci if (irq < 0) 1468c2ecf20Sopenharmony_ci irq = 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); 1498c2ecf20Sopenharmony_ci if (!i2c) 1508c2ecf20Sopenharmony_ci return -ENOMEM; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci i2c->reg_base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 1538c2ecf20Sopenharmony_ci if (IS_ERR(i2c->reg_base)) 1548c2ecf20Sopenharmony_ci return PTR_ERR(i2c->reg_base); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci init_waitqueue_head(&i2c->wait); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci i2c->io_base = res->start; 1608c2ecf20Sopenharmony_ci i2c->io_size = resource_size(res); 1618c2ecf20Sopenharmony_ci i2c->irq = irq; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci i2c->adap.nr = pdev->id; 1648c2ecf20Sopenharmony_ci i2c->adap.owner = THIS_MODULE; 1658c2ecf20Sopenharmony_ci snprintf(i2c->adap.name, sizeof(i2c->adap.name), 1668c2ecf20Sopenharmony_ci "PCA9564/PCA9665 at 0x%08lx", 1678c2ecf20Sopenharmony_ci (unsigned long) res->start); 1688c2ecf20Sopenharmony_ci i2c->adap.algo_data = &i2c->algo_data; 1698c2ecf20Sopenharmony_ci i2c->adap.dev.parent = &pdev->dev; 1708c2ecf20Sopenharmony_ci i2c->adap.dev.of_node = np; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci i2c->gpio = devm_gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); 1738c2ecf20Sopenharmony_ci if (IS_ERR(i2c->gpio)) 1748c2ecf20Sopenharmony_ci return PTR_ERR(i2c->gpio); 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci i2c->adap.timeout = HZ; 1778c2ecf20Sopenharmony_ci ret = device_property_read_u32(&pdev->dev, "clock-frequency", 1788c2ecf20Sopenharmony_ci &i2c->algo_data.i2c_clock); 1798c2ecf20Sopenharmony_ci if (ret) 1808c2ecf20Sopenharmony_ci i2c->algo_data.i2c_clock = 59000; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci if (platform_data) { 1838c2ecf20Sopenharmony_ci i2c->adap.timeout = platform_data->timeout; 1848c2ecf20Sopenharmony_ci i2c->algo_data.i2c_clock = platform_data->i2c_clock_speed; 1858c2ecf20Sopenharmony_ci } 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci i2c->algo_data.data = i2c; 1888c2ecf20Sopenharmony_ci i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion; 1898c2ecf20Sopenharmony_ci if (i2c->gpio) 1908c2ecf20Sopenharmony_ci i2c->algo_data.reset_chip = i2c_pca_pf_resetchip; 1918c2ecf20Sopenharmony_ci else 1928c2ecf20Sopenharmony_ci i2c->algo_data.reset_chip = i2c_pca_pf_dummyreset; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci switch (res->flags & IORESOURCE_MEM_TYPE_MASK) { 1958c2ecf20Sopenharmony_ci case IORESOURCE_MEM_32BIT: 1968c2ecf20Sopenharmony_ci i2c->algo_data.write_byte = i2c_pca_pf_writebyte32; 1978c2ecf20Sopenharmony_ci i2c->algo_data.read_byte = i2c_pca_pf_readbyte32; 1988c2ecf20Sopenharmony_ci break; 1998c2ecf20Sopenharmony_ci case IORESOURCE_MEM_16BIT: 2008c2ecf20Sopenharmony_ci i2c->algo_data.write_byte = i2c_pca_pf_writebyte16; 2018c2ecf20Sopenharmony_ci i2c->algo_data.read_byte = i2c_pca_pf_readbyte16; 2028c2ecf20Sopenharmony_ci break; 2038c2ecf20Sopenharmony_ci case IORESOURCE_MEM_8BIT: 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci i2c->algo_data.write_byte = i2c_pca_pf_writebyte8; 2068c2ecf20Sopenharmony_ci i2c->algo_data.read_byte = i2c_pca_pf_readbyte8; 2078c2ecf20Sopenharmony_ci break; 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci if (irq) { 2118c2ecf20Sopenharmony_ci ret = devm_request_irq(&pdev->dev, irq, i2c_pca_pf_handler, 2128c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING, pdev->name, i2c); 2138c2ecf20Sopenharmony_ci if (ret) 2148c2ecf20Sopenharmony_ci return ret; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci ret = i2c_pca_add_numbered_bus(&i2c->adap); 2188c2ecf20Sopenharmony_ci if (ret) 2198c2ecf20Sopenharmony_ci return ret; 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, i2c); 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "registered.\n"); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_cistatic int i2c_pca_pf_remove(struct platform_device *pdev) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct i2c_pca_pf_data *i2c = platform_get_drvdata(pdev); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci i2c_del_adapter(&i2c->adap); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci return 0; 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci#ifdef CONFIG_OF 2388c2ecf20Sopenharmony_cistatic const struct of_device_id i2c_pca_of_match_table[] = { 2398c2ecf20Sopenharmony_ci { .compatible = "nxp,pca9564" }, 2408c2ecf20Sopenharmony_ci { .compatible = "nxp,pca9665" }, 2418c2ecf20Sopenharmony_ci {}, 2428c2ecf20Sopenharmony_ci}; 2438c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, i2c_pca_of_match_table); 2448c2ecf20Sopenharmony_ci#endif 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic struct platform_driver i2c_pca_pf_driver = { 2478c2ecf20Sopenharmony_ci .probe = i2c_pca_pf_probe, 2488c2ecf20Sopenharmony_ci .remove = i2c_pca_pf_remove, 2498c2ecf20Sopenharmony_ci .driver = { 2508c2ecf20Sopenharmony_ci .name = "i2c-pca-platform", 2518c2ecf20Sopenharmony_ci .of_match_table = of_match_ptr(i2c_pca_of_match_table), 2528c2ecf20Sopenharmony_ci }, 2538c2ecf20Sopenharmony_ci}; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_cimodule_platform_driver(i2c_pca_pf_driver); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ciMODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>"); 2588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("I2C-PCA9564/PCA9665 platform driver"); 2598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 260