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