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