brintos

brintos / linux-shallow public Read only

0
0
Text · 6.5 KiB · 143d012 Raw
275 lines · c
1/*2 * Cavium ThunderX i2c driver.3 *4 * Copyright (C) 2015,2016 Cavium Inc.5 * Authors: Fred Martin <fmartin@caviumnetworks.com>6 *	    Jan Glauber <jglauber@cavium.com>7 *8 * This file is licensed under the terms of the GNU General Public9 * License version 2. This program is licensed "as is" without any10 * warranty of any kind, whether express or implied.11 */12 13#include <linux/acpi.h>14#include <linux/clk.h>15#include <linux/delay.h>16#include <linux/i2c.h>17#include <linux/i2c-smbus.h>18#include <linux/interrupt.h>19#include <linux/kernel.h>20#include <linux/module.h>21#include <linux/of_irq.h>22#include <linux/pci.h>23 24#include "i2c-octeon-core.h"25 26#define DRV_NAME "i2c-thunderx"27 28#define PCI_DEVICE_ID_THUNDER_TWSI	0xa01229 30#define SYS_FREQ_DEFAULT		80000000031#define OTX2_REF_FREQ_DEFAULT		10000000032 33#define TWSI_INT_ENA_W1C		0x102834#define TWSI_INT_ENA_W1S		0x103035 36/*37 * Enable the CORE interrupt.38 * The interrupt will be asserted when there is non-STAT_IDLE state in the39 * SW_TWSI_EOP_TWSI_STAT register.40 */41static void thunder_i2c_int_enable(struct octeon_i2c *i2c)42{43	octeon_i2c_writeq_flush(TWSI_INT_CORE_INT,44				i2c->twsi_base + TWSI_INT_ENA_W1S);45}46 47/*48 * Disable the CORE interrupt.49 */50static void thunder_i2c_int_disable(struct octeon_i2c *i2c)51{52	octeon_i2c_writeq_flush(TWSI_INT_CORE_INT,53				i2c->twsi_base + TWSI_INT_ENA_W1C);54}55 56static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c)57{58	octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT,59				i2c->twsi_base + TWSI_INT_ENA_W1S);60}61 62static void thunder_i2c_hlc_int_disable(struct octeon_i2c *i2c)63{64	octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT,65				i2c->twsi_base + TWSI_INT_ENA_W1C);66}67 68static u32 thunderx_i2c_functionality(struct i2c_adapter *adap)69{70	return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) |71	       I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL;72}73 74static const struct i2c_algorithm thunderx_i2c_algo = {75	.xfer = octeon_i2c_xfer,76	.functionality = thunderx_i2c_functionality,77};78 79static const struct i2c_adapter thunderx_i2c_ops = {80	.owner	= THIS_MODULE,81	.name	= "ThunderX adapter",82	.algo	= &thunderx_i2c_algo,83};84 85static void thunder_i2c_clock_enable(struct device *dev, struct octeon_i2c *i2c)86{87	int ret;88 89	if (acpi_disabled) {90		/* DT */91		i2c->clk = clk_get(dev, NULL);92		if (IS_ERR(i2c->clk)) {93			i2c->clk = NULL;94			goto skip;95		}96 97		ret = clk_prepare_enable(i2c->clk);98		if (ret)99			goto skip;100		i2c->sys_freq = clk_get_rate(i2c->clk);101	} else {102		/* ACPI */103		if (device_property_read_u32(dev, "sclk", &i2c->sys_freq))104			device_property_read_u32(dev, "ioclk", &i2c->sys_freq);105	}106 107skip:108	if (!i2c->sys_freq)109		i2c->sys_freq = SYS_FREQ_DEFAULT;110}111 112static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)113{114	if (!clk)115		return;116	clk_disable_unprepare(clk);117	clk_put(clk);118}119 120static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,121				      struct device_node *node)122{123	struct i2c_client *ara;124 125	if (!node)126		return -EINVAL;127 128	i2c->alert_data.irq = irq_of_parse_and_map(node, 0);129	if (!i2c->alert_data.irq)130		return -EINVAL;131 132	ara = i2c_new_smbus_alert_device(&i2c->adap, &i2c->alert_data);133	if (IS_ERR(ara))134		return PTR_ERR(ara);135 136	i2c->ara = ara;137 138	return 0;139}140 141static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c,142				   struct device_node *node)143{144	/* TODO: ACPI support */145	if (!acpi_disabled)146		return -EOPNOTSUPP;147 148	return thunder_i2c_smbus_setup_of(i2c, node);149}150 151static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c)152{153	i2c_unregister_device(i2c->ara);154}155 156static int thunder_i2c_probe_pci(struct pci_dev *pdev,157				 const struct pci_device_id *ent)158{159	struct device *dev = &pdev->dev;160	struct octeon_i2c *i2c;161	int ret;162 163	i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);164	if (!i2c)165		return -ENOMEM;166 167	i2c->roff.sw_twsi = 0x1000;168	i2c->roff.twsi_int = 0x1010;169	i2c->roff.sw_twsi_ext = 0x1018;170	i2c->roff.mode = 0x1038;171 172	i2c->dev = dev;173	pci_set_drvdata(pdev, i2c);174	ret = pcim_enable_device(pdev);175	if (ret)176		return ret;177 178	ret = pci_request_regions(pdev, DRV_NAME);179	if (ret)180		return ret;181 182	i2c->twsi_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0));183	if (!i2c->twsi_base)184		return -EINVAL;185 186	thunder_i2c_clock_enable(dev, i2c);187	ret = device_property_read_u32(dev, "clock-frequency", &i2c->twsi_freq);188	if (ret)189		i2c->twsi_freq = I2C_MAX_STANDARD_MODE_FREQ;190 191	init_waitqueue_head(&i2c->queue);192 193	i2c->int_enable = thunder_i2c_int_enable;194	i2c->int_disable = thunder_i2c_int_disable;195	i2c->hlc_int_enable = thunder_i2c_hlc_int_enable;196	i2c->hlc_int_disable = thunder_i2c_hlc_int_disable;197 198	ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSIX);199	if (ret < 0)200		goto error;201 202	ret = devm_request_irq(dev, pci_irq_vector(pdev, 0), octeon_i2c_isr, 0,203			       DRV_NAME, i2c);204	if (ret)205		goto error;206 207	ret = octeon_i2c_init_lowlevel(i2c);208	if (ret)209		goto error;210 211	/*212	 * For OcteonTX2 chips, set reference frequency to 100MHz213	 * as refclk_src in TWSI_MODE register defaults to 100MHz.214	 */215	if (octeon_i2c_is_otx2(pdev) && IS_LS_FREQ(i2c->twsi_freq))216		i2c->sys_freq = OTX2_REF_FREQ_DEFAULT;217	octeon_i2c_set_clock(i2c);218 219	i2c->adap = thunderx_i2c_ops;220	i2c->adap.retries = 5;221	i2c->adap.class = I2C_CLASS_HWMON;222	i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info;223	i2c->adap.dev.parent = dev;224	i2c->adap.dev.of_node = pdev->dev.of_node;225	i2c->adap.dev.fwnode = dev->fwnode;226	snprintf(i2c->adap.name, sizeof(i2c->adap.name),227		 "Cavium ThunderX i2c adapter at %s", dev_name(dev));228	i2c_set_adapdata(&i2c->adap, i2c);229 230	ret = i2c_add_adapter(&i2c->adap);231	if (ret)232		goto error;233 234	dev_info(i2c->dev, "Probed. Set system clock to %u\n", i2c->sys_freq);235 236	ret = thunder_i2c_smbus_setup(i2c, pdev->dev.of_node);237	if (ret)238		dev_info(dev, "SMBUS alert not active on this bus\n");239 240	return 0;241 242error:243	thunder_i2c_clock_disable(dev, i2c->clk);244	return ret;245}246 247static void thunder_i2c_remove_pci(struct pci_dev *pdev)248{249	struct octeon_i2c *i2c = pci_get_drvdata(pdev);250 251	thunder_i2c_smbus_remove(i2c);252	thunder_i2c_clock_disable(&pdev->dev, i2c->clk);253	i2c_del_adapter(&i2c->adap);254}255 256static const struct pci_device_id thunder_i2c_pci_id_table[] = {257	{ PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_TWSI) },258	{ 0, }259};260 261MODULE_DEVICE_TABLE(pci, thunder_i2c_pci_id_table);262 263static struct pci_driver thunder_i2c_pci_driver = {264	.name		= DRV_NAME,265	.id_table	= thunder_i2c_pci_id_table,266	.probe		= thunder_i2c_probe_pci,267	.remove		= thunder_i2c_remove_pci,268};269 270module_pci_driver(thunder_i2c_pci_driver);271 272MODULE_LICENSE("GPL");273MODULE_AUTHOR("Fred Martin <fmartin@caviumnetworks.com>");274MODULE_DESCRIPTION("I2C-Bus adapter for Cavium ThunderX SOC");275