18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005 Simtec Electronics 48c2ecf20Sopenharmony_ci * Ben Dooks <ben@simtec.co.uk> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Simtec Generic I2C Controller 78c2ecf20Sopenharmony_ci*/ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 138c2ecf20Sopenharmony_ci#include <linux/slab.h> 148c2ecf20Sopenharmony_ci#include <linux/io.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/i2c.h> 178c2ecf20Sopenharmony_ci#include <linux/i2c-algo-bit.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistruct simtec_i2c_data { 208c2ecf20Sopenharmony_ci struct resource *ioarea; 218c2ecf20Sopenharmony_ci void __iomem *reg; 228c2ecf20Sopenharmony_ci struct i2c_adapter adap; 238c2ecf20Sopenharmony_ci struct i2c_algo_bit_data bit; 248c2ecf20Sopenharmony_ci}; 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define CMD_SET_SDA (1<<2) 278c2ecf20Sopenharmony_ci#define CMD_SET_SCL (1<<3) 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define STATE_SDA (1<<0) 308c2ecf20Sopenharmony_ci#define STATE_SCL (1<<1) 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci/* i2c bit-bus functions */ 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic void simtec_i2c_setsda(void *pw, int state) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct simtec_i2c_data *pd = pw; 378c2ecf20Sopenharmony_ci writeb(CMD_SET_SDA | (state ? STATE_SDA : 0), pd->reg); 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic void simtec_i2c_setscl(void *pw, int state) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci struct simtec_i2c_data *pd = pw; 438c2ecf20Sopenharmony_ci writeb(CMD_SET_SCL | (state ? STATE_SCL : 0), pd->reg); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int simtec_i2c_getsda(void *pw) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct simtec_i2c_data *pd = pw; 498c2ecf20Sopenharmony_ci return readb(pd->reg) & STATE_SDA ? 1 : 0; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic int simtec_i2c_getscl(void *pw) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci struct simtec_i2c_data *pd = pw; 558c2ecf20Sopenharmony_ci return readb(pd->reg) & STATE_SCL ? 1 : 0; 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/* device registration */ 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int simtec_i2c_probe(struct platform_device *dev) 618c2ecf20Sopenharmony_ci{ 628c2ecf20Sopenharmony_ci struct simtec_i2c_data *pd; 638c2ecf20Sopenharmony_ci struct resource *res; 648c2ecf20Sopenharmony_ci int size; 658c2ecf20Sopenharmony_ci int ret; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci pd = kzalloc(sizeof(struct simtec_i2c_data), GFP_KERNEL); 688c2ecf20Sopenharmony_ci if (pd == NULL) 698c2ecf20Sopenharmony_ci return -ENOMEM; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci platform_set_drvdata(dev, pd); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci res = platform_get_resource(dev, IORESOURCE_MEM, 0); 748c2ecf20Sopenharmony_ci if (res == NULL) { 758c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot find IO resource\n"); 768c2ecf20Sopenharmony_ci ret = -ENOENT; 778c2ecf20Sopenharmony_ci goto err; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci size = resource_size(res); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci pd->ioarea = request_mem_region(res->start, size, dev->name); 838c2ecf20Sopenharmony_ci if (pd->ioarea == NULL) { 848c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot request IO\n"); 858c2ecf20Sopenharmony_ci ret = -ENXIO; 868c2ecf20Sopenharmony_ci goto err; 878c2ecf20Sopenharmony_ci } 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci pd->reg = ioremap(res->start, size); 908c2ecf20Sopenharmony_ci if (pd->reg == NULL) { 918c2ecf20Sopenharmony_ci dev_err(&dev->dev, "cannot map IO\n"); 928c2ecf20Sopenharmony_ci ret = -ENXIO; 938c2ecf20Sopenharmony_ci goto err_res; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci /* setup the private data */ 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci pd->adap.owner = THIS_MODULE; 998c2ecf20Sopenharmony_ci pd->adap.algo_data = &pd->bit; 1008c2ecf20Sopenharmony_ci pd->adap.dev.parent = &dev->dev; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci strlcpy(pd->adap.name, "Simtec I2C", sizeof(pd->adap.name)); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci pd->bit.data = pd; 1058c2ecf20Sopenharmony_ci pd->bit.setsda = simtec_i2c_setsda; 1068c2ecf20Sopenharmony_ci pd->bit.setscl = simtec_i2c_setscl; 1078c2ecf20Sopenharmony_ci pd->bit.getsda = simtec_i2c_getsda; 1088c2ecf20Sopenharmony_ci pd->bit.getscl = simtec_i2c_getscl; 1098c2ecf20Sopenharmony_ci pd->bit.timeout = HZ; 1108c2ecf20Sopenharmony_ci pd->bit.udelay = 20; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci ret = i2c_bit_add_bus(&pd->adap); 1138c2ecf20Sopenharmony_ci if (ret) 1148c2ecf20Sopenharmony_ci goto err_all; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci err_all: 1198c2ecf20Sopenharmony_ci iounmap(pd->reg); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci err_res: 1228c2ecf20Sopenharmony_ci release_mem_region(pd->ioarea->start, size); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci err: 1258c2ecf20Sopenharmony_ci kfree(pd); 1268c2ecf20Sopenharmony_ci return ret; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic int simtec_i2c_remove(struct platform_device *dev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct simtec_i2c_data *pd = platform_get_drvdata(dev); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci i2c_del_adapter(&pd->adap); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci iounmap(pd->reg); 1368c2ecf20Sopenharmony_ci release_mem_region(pd->ioarea->start, resource_size(pd->ioarea)); 1378c2ecf20Sopenharmony_ci kfree(pd); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* device driver */ 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic struct platform_driver simtec_i2c_driver = { 1458c2ecf20Sopenharmony_ci .driver = { 1468c2ecf20Sopenharmony_ci .name = "simtec-i2c", 1478c2ecf20Sopenharmony_ci }, 1488c2ecf20Sopenharmony_ci .probe = simtec_i2c_probe, 1498c2ecf20Sopenharmony_ci .remove = simtec_i2c_remove, 1508c2ecf20Sopenharmony_ci}; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_cimodule_platform_driver(simtec_i2c_driver); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Simtec Generic I2C Bus driver"); 1558c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); 1568c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1578c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:simtec-i2c"); 158