xref: /openbmc/linux/drivers/i2c/busses/i2c-thunderx-pcidrv.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
122d40209SJan Glauber /*
222d40209SJan Glauber  * Cavium ThunderX i2c driver.
322d40209SJan Glauber  *
422d40209SJan Glauber  * Copyright (C) 2015,2016 Cavium Inc.
522d40209SJan Glauber  * Authors: Fred Martin <fmartin@caviumnetworks.com>
622d40209SJan Glauber  *	    Jan Glauber <jglauber@cavium.com>
722d40209SJan Glauber  *
822d40209SJan Glauber  * This file is licensed under the terms of the GNU General Public
922d40209SJan Glauber  * License version 2. This program is licensed "as is" without any
1022d40209SJan Glauber  * warranty of any kind, whether express or implied.
1122d40209SJan Glauber  */
1222d40209SJan Glauber 
1322d40209SJan Glauber #include <linux/acpi.h>
1422d40209SJan Glauber #include <linux/clk.h>
1522d40209SJan Glauber #include <linux/delay.h>
1622d40209SJan Glauber #include <linux/i2c.h>
171e586671SJan Glauber #include <linux/i2c-smbus.h>
1822d40209SJan Glauber #include <linux/interrupt.h>
1922d40209SJan Glauber #include <linux/kernel.h>
2022d40209SJan Glauber #include <linux/module.h>
211e586671SJan Glauber #include <linux/of_irq.h>
2222d40209SJan Glauber #include <linux/pci.h>
2322d40209SJan Glauber 
2422d40209SJan Glauber #include "i2c-octeon-core.h"
2522d40209SJan Glauber 
2622d40209SJan Glauber #define DRV_NAME "i2c-thunderx"
2722d40209SJan Glauber 
2822d40209SJan Glauber #define PCI_DEVICE_ID_THUNDER_TWSI	0xa012
2922d40209SJan Glauber 
3022d40209SJan Glauber #define SYS_FREQ_DEFAULT		700000000
3122d40209SJan Glauber 
3222d40209SJan Glauber #define TWSI_INT_ENA_W1C		0x1028
3322d40209SJan Glauber #define TWSI_INT_ENA_W1S		0x1030
3422d40209SJan Glauber 
3522d40209SJan Glauber /*
3622d40209SJan Glauber  * Enable the CORE interrupt.
3722d40209SJan Glauber  * The interrupt will be asserted when there is non-STAT_IDLE state in the
3822d40209SJan Glauber  * SW_TWSI_EOP_TWSI_STAT register.
3922d40209SJan Glauber  */
thunder_i2c_int_enable(struct octeon_i2c * i2c)4022d40209SJan Glauber static void thunder_i2c_int_enable(struct octeon_i2c *i2c)
4122d40209SJan Glauber {
4222d40209SJan Glauber 	octeon_i2c_writeq_flush(TWSI_INT_CORE_INT,
4322d40209SJan Glauber 				i2c->twsi_base + TWSI_INT_ENA_W1S);
4422d40209SJan Glauber }
4522d40209SJan Glauber 
4622d40209SJan Glauber /*
4722d40209SJan Glauber  * Disable the CORE interrupt.
4822d40209SJan Glauber  */
thunder_i2c_int_disable(struct octeon_i2c * i2c)4922d40209SJan Glauber static void thunder_i2c_int_disable(struct octeon_i2c *i2c)
5022d40209SJan Glauber {
5122d40209SJan Glauber 	octeon_i2c_writeq_flush(TWSI_INT_CORE_INT,
5222d40209SJan Glauber 				i2c->twsi_base + TWSI_INT_ENA_W1C);
5322d40209SJan Glauber }
5422d40209SJan Glauber 
thunder_i2c_hlc_int_enable(struct octeon_i2c * i2c)5522d40209SJan Glauber static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c)
5622d40209SJan Glauber {
5722d40209SJan Glauber 	octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT,
5822d40209SJan Glauber 				i2c->twsi_base + TWSI_INT_ENA_W1S);
5922d40209SJan Glauber }
6022d40209SJan Glauber 
thunder_i2c_hlc_int_disable(struct octeon_i2c * i2c)6122d40209SJan Glauber static void thunder_i2c_hlc_int_disable(struct octeon_i2c *i2c)
6222d40209SJan Glauber {
6322d40209SJan Glauber 	octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT,
6422d40209SJan Glauber 				i2c->twsi_base + TWSI_INT_ENA_W1C);
6522d40209SJan Glauber }
6622d40209SJan Glauber 
thunderx_i2c_functionality(struct i2c_adapter * adap)6722d40209SJan Glauber static u32 thunderx_i2c_functionality(struct i2c_adapter *adap)
6822d40209SJan Glauber {
6922d40209SJan Glauber 	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |
7022d40209SJan Glauber 	       I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL;
7122d40209SJan Glauber }
7222d40209SJan Glauber 
7322d40209SJan Glauber static const struct i2c_algorithm thunderx_i2c_algo = {
7422d40209SJan Glauber 	.master_xfer = octeon_i2c_xfer,
7522d40209SJan Glauber 	.functionality = thunderx_i2c_functionality,
7622d40209SJan Glauber };
7722d40209SJan Glauber 
78329430ccSBhumika Goyal static const struct i2c_adapter thunderx_i2c_ops = {
7922d40209SJan Glauber 	.owner	= THIS_MODULE,
8022d40209SJan Glauber 	.name	= "ThunderX adapter",
8122d40209SJan Glauber 	.algo	= &thunderx_i2c_algo,
8222d40209SJan Glauber };
8322d40209SJan Glauber 
thunder_i2c_clock_enable(struct device * dev,struct octeon_i2c * i2c)8422d40209SJan Glauber static void thunder_i2c_clock_enable(struct device *dev, struct octeon_i2c *i2c)
8522d40209SJan Glauber {
8622d40209SJan Glauber 	int ret;
8722d40209SJan Glauber 
88346e400cSJan Glauber 	if (acpi_disabled) {
89346e400cSJan Glauber 		/* DT */
9022d40209SJan Glauber 		i2c->clk = clk_get(dev, NULL);
9122d40209SJan Glauber 		if (IS_ERR(i2c->clk)) {
9222d40209SJan Glauber 			i2c->clk = NULL;
9322d40209SJan Glauber 			goto skip;
9422d40209SJan Glauber 		}
9522d40209SJan Glauber 
9622d40209SJan Glauber 		ret = clk_prepare_enable(i2c->clk);
9722d40209SJan Glauber 		if (ret)
9822d40209SJan Glauber 			goto skip;
9922d40209SJan Glauber 		i2c->sys_freq = clk_get_rate(i2c->clk);
100346e400cSJan Glauber 	} else {
101346e400cSJan Glauber 		/* ACPI */
102346e400cSJan Glauber 		device_property_read_u32(dev, "sclk", &i2c->sys_freq);
103346e400cSJan Glauber 	}
10422d40209SJan Glauber 
10522d40209SJan Glauber skip:
10622d40209SJan Glauber 	if (!i2c->sys_freq)
10722d40209SJan Glauber 		i2c->sys_freq = SYS_FREQ_DEFAULT;
10822d40209SJan Glauber }
10922d40209SJan Glauber 
thunder_i2c_clock_disable(struct device * dev,struct clk * clk)11022d40209SJan Glauber static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)
11122d40209SJan Glauber {
11222d40209SJan Glauber 	if (!clk)
11322d40209SJan Glauber 		return;
11422d40209SJan Glauber 	clk_disable_unprepare(clk);
11522d40209SJan Glauber 	clk_put(clk);
11622d40209SJan Glauber }
11722d40209SJan Glauber 
thunder_i2c_smbus_setup_of(struct octeon_i2c * i2c,struct device_node * node)1181e586671SJan Glauber static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
1191e586671SJan Glauber 				      struct device_node *node)
1201e586671SJan Glauber {
121ed680522SWolfram Sang 	struct i2c_client *ara;
122ed680522SWolfram Sang 
1231e586671SJan Glauber 	if (!node)
1241e586671SJan Glauber 		return -EINVAL;
1251e586671SJan Glauber 
1261e586671SJan Glauber 	i2c->alert_data.irq = irq_of_parse_and_map(node, 0);
1271e586671SJan Glauber 	if (!i2c->alert_data.irq)
1281e586671SJan Glauber 		return -EINVAL;
1291e586671SJan Glauber 
130ed680522SWolfram Sang 	ara = i2c_new_smbus_alert_device(&i2c->adap, &i2c->alert_data);
131ed680522SWolfram Sang 	if (IS_ERR(ara))
132ed680522SWolfram Sang 		return PTR_ERR(ara);
133ed680522SWolfram Sang 
134ed680522SWolfram Sang 	i2c->ara = ara;
135ed680522SWolfram Sang 
1361e586671SJan Glauber 	return 0;
1371e586671SJan Glauber }
1381e586671SJan Glauber 
thunder_i2c_smbus_setup(struct octeon_i2c * i2c,struct device_node * node)1391e586671SJan Glauber static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c,
1401e586671SJan Glauber 				   struct device_node *node)
1411e586671SJan Glauber {
1421e586671SJan Glauber 	/* TODO: ACPI support */
1431e586671SJan Glauber 	if (!acpi_disabled)
1441e586671SJan Glauber 		return -EOPNOTSUPP;
1451e586671SJan Glauber 
1461e586671SJan Glauber 	return thunder_i2c_smbus_setup_of(i2c, node);
1471e586671SJan Glauber }
1481e586671SJan Glauber 
thunder_i2c_smbus_remove(struct octeon_i2c * i2c)1491e586671SJan Glauber static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c)
1501e586671SJan Glauber {
1511e586671SJan Glauber 	i2c_unregister_device(i2c->ara);
1521e586671SJan Glauber }
1531e586671SJan Glauber 
thunder_i2c_probe_pci(struct pci_dev * pdev,const struct pci_device_id * ent)15422d40209SJan Glauber static int thunder_i2c_probe_pci(struct pci_dev *pdev,
15522d40209SJan Glauber 				 const struct pci_device_id *ent)
15622d40209SJan Glauber {
15722d40209SJan Glauber 	struct device *dev = &pdev->dev;
15822d40209SJan Glauber 	struct octeon_i2c *i2c;
15922d40209SJan Glauber 	int ret;
16022d40209SJan Glauber 
16122d40209SJan Glauber 	i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
16222d40209SJan Glauber 	if (!i2c)
16322d40209SJan Glauber 		return -ENOMEM;
16422d40209SJan Glauber 
16597d97004SJan Glauber 	i2c->roff.sw_twsi = 0x1000;
16697d97004SJan Glauber 	i2c->roff.twsi_int = 0x1010;
16797d97004SJan Glauber 	i2c->roff.sw_twsi_ext = 0x1018;
16897d97004SJan Glauber 
16922d40209SJan Glauber 	i2c->dev = dev;
17022d40209SJan Glauber 	pci_set_drvdata(pdev, i2c);
17122d40209SJan Glauber 	ret = pcim_enable_device(pdev);
17222d40209SJan Glauber 	if (ret)
17322d40209SJan Glauber 		return ret;
17422d40209SJan Glauber 
17522d40209SJan Glauber 	ret = pci_request_regions(pdev, DRV_NAME);
17622d40209SJan Glauber 	if (ret)
17722d40209SJan Glauber 		return ret;
17822d40209SJan Glauber 
17922d40209SJan Glauber 	i2c->twsi_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));
18022d40209SJan Glauber 	if (!i2c->twsi_base)
18122d40209SJan Glauber 		return -EINVAL;
18222d40209SJan Glauber 
18322d40209SJan Glauber 	thunder_i2c_clock_enable(dev, i2c);
18422d40209SJan Glauber 	ret = device_property_read_u32(dev, "clock-frequency", &i2c->twsi_freq);
18522d40209SJan Glauber 	if (ret)
18690224e64SAndy Shevchenko 		i2c->twsi_freq = I2C_MAX_STANDARD_MODE_FREQ;
18722d40209SJan Glauber 
18822d40209SJan Glauber 	init_waitqueue_head(&i2c->queue);
18922d40209SJan Glauber 
19022d40209SJan Glauber 	i2c->int_enable = thunder_i2c_int_enable;
19122d40209SJan Glauber 	i2c->int_disable = thunder_i2c_int_disable;
19222d40209SJan Glauber 	i2c->hlc_int_enable = thunder_i2c_hlc_int_enable;
19322d40209SJan Glauber 	i2c->hlc_int_disable = thunder_i2c_hlc_int_disable;
19422d40209SJan Glauber 
1954c21541dSJan Glauber 	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);
1964c21541dSJan Glauber 	if (ret < 0)
19722d40209SJan Glauber 		goto error;
19822d40209SJan Glauber 
1994c21541dSJan Glauber 	ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), octeon_i2c_isr, 0,
20022d40209SJan Glauber 			       DRV_NAME, i2c);
20122d40209SJan Glauber 	if (ret)
20222d40209SJan Glauber 		goto error;
20322d40209SJan Glauber 
20422d40209SJan Glauber 	ret = octeon_i2c_init_lowlevel(i2c);
20522d40209SJan Glauber 	if (ret)
20622d40209SJan Glauber 		goto error;
20722d40209SJan Glauber 
20822d40209SJan Glauber 	octeon_i2c_set_clock(i2c);
20922d40209SJan Glauber 
21022d40209SJan Glauber 	i2c->adap = thunderx_i2c_ops;
21122d40209SJan Glauber 	i2c->adap.retries = 5;
212889ef45cSJan Glauber 	i2c->adap.class = I2C_CLASS_HWMON;
21322d40209SJan Glauber 	i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info;
21422d40209SJan Glauber 	i2c->adap.dev.parent = dev;
21522d40209SJan Glauber 	i2c->adap.dev.of_node = pdev->dev.of_node;
216*03a35bc8SPiyush Malgujar 	i2c->adap.dev.fwnode = dev->fwnode;
21722d40209SJan Glauber 	snprintf(i2c->adap.name, sizeof(i2c->adap.name),
21822d40209SJan Glauber 		 "Cavium ThunderX i2c adapter at %s", dev_name(dev));
21922d40209SJan Glauber 	i2c_set_adapdata(&i2c->adap, i2c);
22022d40209SJan Glauber 
22122d40209SJan Glauber 	ret = i2c_add_adapter(&i2c->adap);
22222d40209SJan Glauber 	if (ret)
22322d40209SJan Glauber 		goto error;
22422d40209SJan Glauber 
22522d40209SJan Glauber 	dev_info(i2c->dev, "Probed. Set system clock to %u\n", i2c->sys_freq);
2261e586671SJan Glauber 
2271e586671SJan Glauber 	ret = thunder_i2c_smbus_setup(i2c, pdev->dev.of_node);
2281e586671SJan Glauber 	if (ret)
2291e586671SJan Glauber 		dev_info(dev, "SMBUS alert not active on this bus\n");
2301e586671SJan Glauber 
23122d40209SJan Glauber 	return 0;
23222d40209SJan Glauber 
23322d40209SJan Glauber error:
23422d40209SJan Glauber 	thunder_i2c_clock_disable(dev, i2c->clk);
23522d40209SJan Glauber 	return ret;
23622d40209SJan Glauber }
23722d40209SJan Glauber 
thunder_i2c_remove_pci(struct pci_dev * pdev)23822d40209SJan Glauber static void thunder_i2c_remove_pci(struct pci_dev *pdev)
23922d40209SJan Glauber {
24022d40209SJan Glauber 	struct octeon_i2c *i2c = pci_get_drvdata(pdev);
24122d40209SJan Glauber 
2421e586671SJan Glauber 	thunder_i2c_smbus_remove(i2c);
24322d40209SJan Glauber 	thunder_i2c_clock_disable(&pdev->dev, i2c->clk);
24422d40209SJan Glauber 	i2c_del_adapter(&i2c->adap);
24522d40209SJan Glauber }
24622d40209SJan Glauber 
24722d40209SJan Glauber static const struct pci_device_id thunder_i2c_pci_id_table[] = {
24822d40209SJan Glauber 	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_TWSI) },
24922d40209SJan Glauber 	{ 0, }
25022d40209SJan Glauber };
25122d40209SJan Glauber 
25222d40209SJan Glauber MODULE_DEVICE_TABLE(pci, thunder_i2c_pci_id_table);
25322d40209SJan Glauber 
25422d40209SJan Glauber static struct pci_driver thunder_i2c_pci_driver = {
25522d40209SJan Glauber 	.name		= DRV_NAME,
25622d40209SJan Glauber 	.id_table	= thunder_i2c_pci_id_table,
25722d40209SJan Glauber 	.probe		= thunder_i2c_probe_pci,
25822d40209SJan Glauber 	.remove		= thunder_i2c_remove_pci,
25922d40209SJan Glauber };
26022d40209SJan Glauber 
26122d40209SJan Glauber module_pci_driver(thunder_i2c_pci_driver);
26222d40209SJan Glauber 
26322d40209SJan Glauber MODULE_LICENSE("GPL");
26422d40209SJan Glauber MODULE_AUTHOR("Fred Martin <fmartin@caviumnetworks.com>");
26522d40209SJan Glauber MODULE_DESCRIPTION("I2C-Bus adapter for Cavium ThunderX SOC");
266