/* * Copyright (C) 2009 ST-Ericsson SA * Copyright (C) 2009 STMicroelectronics * * I2C master mode controller driver, used in Nomadik 8815 * and Ux500 platforms. * * Author: Srinidhi Kasagar * Author: Sachin Verma * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #define DRIVER_NAME "nmk-i2c" /* I2C Controller register offsets */ #define I2C_CR (0x000) #define I2C_SCR (0x004) #define I2C_HSMCR (0x008) #define I2C_MCR (0x00C) #define I2C_TFR (0x010) #define I2C_SR (0x014) #define I2C_RFR (0x018) #define I2C_TFTR (0x01C) #define I2C_RFTR (0x020) #define I2C_DMAR (0x024) #define I2C_BRCR (0x028) #define I2C_IMSCR (0x02C) #define I2C_RISR (0x030) #define I2C_MISR (0x034) #define I2C_ICR (0x038) /* Control registers */ #define I2C_CR_PE (0x1 << 0) /* Peripheral Enable */ #define I2C_CR_OM (0x3 << 1) /* Operating mode */ #define I2C_CR_SAM (0x1 << 3) /* Slave addressing mode */ #define I2C_CR_SM (0x3 << 4) /* Speed mode */ #define I2C_CR_SGCM (0x1 << 6) /* Slave general call mode */ #define I2C_CR_FTX (0x1 << 7) /* Flush Transmit */ #define I2C_CR_FRX (0x1 << 8) /* Flush Receive */ #define I2C_CR_DMA_TX_EN (0x1 << 9) /* DMA Tx enable */ #define I2C_CR_DMA_RX_EN (0x1 << 10) /* DMA Rx Enable */ #define I2C_CR_DMA_SLE (0x1 << 11) /* DMA sync. logic enable */ #define I2C_CR_LM (0x1 << 12) /* Loopback mode */ #define I2C_CR_FON (0x3 << 13) /* Filtering on */ #define I2C_CR_FS (0x3 << 15) /* Force stop enable */ /* Master controller (MCR) register */ #define I2C_MCR_OP (0x1 << 0) /* Operation */ #define I2C_MCR_A7 (0x7f << 1) /* 7-bit address */ #define I2C_MCR_EA10 (0x7 << 8) /* 10-bit Extended address */ #define I2C_MCR_SB (0x1 << 11) /* Extended address */ #define I2C_MCR_AM (0x3 << 12) /* Address type */ #define I2C_MCR_STOP (0x1 << 14) /* Stop condition */ #define I2C_MCR_LENGTH (0x7ff << 15) /* Transaction length */ /* Status register (SR) */ #define I2C_SR_OP (0x3 << 0) /* Operation */ #define I2C_SR_STATUS (0x3 << 2) /* controller status */ #define I2C_SR_CAUSE (0x7 << 4) /* Abort cause */ #define I2C_SR_TYPE (0x3 << 7) /* Receive type */ #define I2C_SR_LENGTH (0x7ff << 9) /* Transfer length */ /* Interrupt mask set/clear (IMSCR) bits */ #define I2C_IT_TXFE (0x1 << 0) #define I2C_IT_TXFNE (0x1 << 1) #define I2C_IT_TXFF (0x1 << 2) #define I2C_IT_TXFOVR (0x1 << 3) #define I2C_IT_RXFE (0x1 << 4) #define I2C_IT_RXFNF (0x1 << 5) #define I2C_IT_RXFF (0x1 << 6) #define I2C_IT_RFSR (0x1 << 16) #define I2C_IT_RFSE (0x1 << 17) #define I2C_IT_WTSR (0x1 << 18) #define I2C_IT_MTD (0x1 << 19) #define I2C_IT_STD (0x1 << 20) #define I2C_IT_MAL (0x1 << 24) #define I2C_IT_BERR (0x1 << 25) #define I2C_IT_MTDWS (0x1 << 28) #define GEN_MASK(val, mask, sb) (((val) << (sb)) & (mask)) /* some bits in ICR are reserved */ #define I2C_CLEAR_ALL_INTS 0x131f007f /* first three msb bits are reserved */ #define IRQ_MASK(mask) (mask & 0x1fffffff) /* maximum threshold value */ #define MAX_I2C_FIFO_THRESHOLD 15 enum i2c_freq_mode { I2C_FREQ_MODE_STANDARD, /* up to 100 Kb/s */ I2C_FREQ_MODE_FAST, /* up to 400 Kb/s */ I2C_FREQ_MODE_HIGH_SPEED, /* up to 3.4 Mb/s */ I2C_FREQ_MODE_FAST_PLUS, /* up to 1 Mb/s */ }; /** * struct i2c_vendor_data - per-vendor variations * @has_mtdws: variant has the MTDWS bit * @fifodepth: variant FIFO depth */ struct i2c_vendor_data { bool has_mtdws; u32 fifodepth; }; enum i2c_status { I2C_NOP, I2C_ON_GOING, I2C_OK, I2C_ABORT }; /* operation */ enum i2c_operation { I2C_NO_OPERATION = 0xff, I2C_WRITE = 0x00, I2C_READ = 0x01 }; /** * struct i2c_nmk_client - client specific data * @slave_adr: 7-bit slave address * @count: no. bytes to be transferred * @buffer: client data buffer * @xfer_bytes: bytes transferred till now * @operation: current I2C operation */ struct i2c_nmk_client { unsigned short slave_adr; unsigned long count; unsigned char *buffer; unsigned long xfer_bytes; enum i2c_operation operation; }; /** * struct nmk_i2c_dev - private data structure of the controller. * @vendor: vendor data for this variant. * @adev: parent amba device. * @adap: corresponding I2C adapter. * @irq: interrupt line for the controller. * @virtbase: virtual io memory area. * @clk: hardware i2c block clock. * @cli: holder of client specific data. * @clk_freq: clock frequency for the operation mode * @tft: Tx FIFO Threshold in bytes * @rft: Rx FIFO Threshold in bytes * @timeout Slave response timeout (ms) * @sm: speed mode * @stop: stop condition. * @xfer_complete: acknowledge completion for a I2C message. * @result: controller propogated result. */ struct nmk_i2c_dev { struct i2c_vendor_data *vendor; struct amba_device *adev; struct i2c_adapter adap; int irq; void __iomem *virtbase; struct clk *clk; struct i2c_nmk_client cli; u32 clk_freq; unsigned char tft; unsigned char rft; int timeout; enum i2c
resource_registry:
  OS::TripleO::Controller::Net::SoftwareConfig: ../common/net-config-multinode.yaml
  OS::TripleO::Compute::Net::SoftwareConfig: ../common/net-config-multinode.yaml
  OS::TripleO::Services::SaharaApi: ../../puppet/services/sahara-api.yaml
  OS::TripleO::Services::SaharaEngine: ../../puppet/services/sahara-engine.yaml
  OS::TripleO::Services::MistralApi: ../../puppet/services/mistral-api.yaml
  OS::TripleO::Services::MistralEngine: ../../puppet/services/mistral-engine.yaml
  OS::TripleO::Services::MistralExecutor: ../../puppet/services/mistral-executor.yaml

parameter_defaults:
  ControllerServices:
    - OS::TripleO::Services::Kernel
    - OS::TripleO::Services::Keystone
    - OS::TripleO::Services::GlanceApi
    - OS::TripleO::Services::GlanceRegistry
    - OS::TripleO::Services::HeatApi
    - OS::TripleO::Services::HeatApiCfn
    - OS::TripleO::Services::HeatApiCloudwatch
    - OS::TripleO::Services::HeatEngine
    - OS::TripleO::Services::MySQL
    - OS::TripleO::Services::NeutronDhcpAgent
    - OS::TripleO::Services::NeutronL3Agent
    - OS::TripleO::Services::NeutronMetadataAgent
    - OS::TripleO::Services::NeutronServer
    - OS::TripleO::Services::NeutronCorePlugin
    - OS::TripleO::Services::NeutronOvsAgent
    - OS::TripleO::Services::RabbitMQ
    - OS::TripleO::Services::HAproxy
    - OS::TripleO::Services::Keepalived
    - OS::TripleO::Services::Memcached
    - OS::TripleO::Services::Pacemaker
    - OS::TripleO::Services::NovaConductor
    - OS::TripleO::Services::NovaApi
    - OS::TripleO::Services::NovaMetadata
    - OS::TripleO::Services::NovaScheduler
    - OS::TripleO::Services::Ntp
    - OS::TripleO::Services::Snmp
    - OS::TripleO::Services::Timezone
    - OS::TripleO::Services::NovaCompute
    - OS::TripleO::Services::NovaLibvirt
    - OS::TripleO::Services::SaharaApi
    - OS::TripleO::Services::SaharaEngine
    - OS::TripleO::Services::MistralApi
    - OS::TripleO::Services::MistralEngine
    - OS::TripleO::Services::MistralExecutor
  ControllerExtraConfig:
    nova::compute::libvirt::services::libvirt_virt_type: qemu
    nova::compute::libvirt::libvirt_virt_type: qemu
  Debug: true
  # we don't deploy Swift so we switch to file backend.
  GlanceBackend: 'file'
  KeystoneTokenProvider: 'fernet'
t = -EIO; (void) init_hw(dev); dev_err(&dev->adev->dev, "Tx Fifo Over run\n"); complete(&dev->xfer_complete); break; /* unhandled interrupts by this driver - TODO*/ case I2C_IT_TXFE: case I2C_IT_TXFF: case I2C_IT_RXFE: case I2C_IT_RFSR: case I2C_IT_RFSE: case I2C_IT_WTSR: case I2C_IT_STD: dev_err(&dev->adev->dev, "unhandled Interrupt\n"); break; default: dev_err(&dev->adev->dev, "spurious Interrupt..\n"); break; } return IRQ_HANDLED; } #ifdef CONFIG_PM_SLEEP static int nmk_i2c_suspend_late(struct device *dev) { int ret; ret = pm_runtime_force_suspend(dev); if (ret) return ret; pinctrl_pm_select_sleep_state(dev); return 0; } static int nmk_i2c_resume_early(struct device *dev) { return pm_runtime_force_resume(dev); } #endif #ifdef CONFIG_PM static int nmk_i2c_runtime_suspend(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev); clk_disable_unprepare(nmk_i2c->clk); pinctrl_pm_select_idle_state(dev); return 0; } static int nmk_i2c_runtime_resume(struct device *dev) { struct amba_device *adev = to_amba_device(dev); struct nmk_i2c_dev *nmk_i2c = amba_get_drvdata(adev); int ret; ret = clk_prepare_enable(nmk_i2c->clk); if (ret) { dev_err(dev, "can't prepare_enable clock\n"); return ret; } pinctrl_pm_select_default_state(dev); ret = init_hw(nmk_i2c); if (ret) { clk_disable_unprepare(nmk_i2c->clk); pinctrl_pm_select_idle_state(dev); } return ret; } #endif static const struct dev_pm_ops nmk_i2c_pm = { SET_LATE_SYSTEM_SLEEP_PM_OPS(nmk_i2c_suspend_late, nmk_i2c_resume_early) SET_RUNTIME_PM_OPS(nmk_i2c_runtime_suspend, nmk_i2c_runtime_resume, NULL) }; static unsigned int nmk_i2c_functionality(struct i2c_adapter *adap) { return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR; } static const struct i2c_algorithm nmk_i2c_algo = { .master_xfer = nmk_i2c_xfer, .functionality = nmk_i2c_functionality }; static void nmk_i2c_of_probe(struct device_node *np, struct nmk_i2c_dev *nmk) { /* Default to 100 kHz if no frequency is given in the node */ if (of_property_read_u32(np, "clock-frequency", &nmk->clk_freq)) nmk->clk_freq = 100000; /* This driver only supports 'standard' and 'fast' modes of operation. */ if (nmk->clk_freq <= 100000) nmk->sm = I2C_FREQ_MODE_STANDARD; else nmk->sm = I2C_FREQ_MODE_FAST; nmk->tft = 1; /* Tx FIFO threshold */ nmk->rft = 8; /* Rx FIFO threshold */ nmk->timeout = 200; /* Slave response timeout(ms) */ } static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) { int ret = 0; struct device_node *np = adev->dev.of_node; struct nmk_i2c_dev *dev; struct i2c_adapter *adap; struct i2c_vendor_data *vendor = id->data; u32 max_fifo_threshold = (vendor->fifodepth / 2) - 1; dev = devm_kzalloc(&adev->dev, sizeof(struct nmk_i2c_dev), GFP_KERNEL); if (!dev) { dev_err(&adev->dev, "cannot allocate memory\n"); ret = -ENOMEM; goto err_no_mem; } dev->vendor = vendor; dev->adev = adev; nmk_i2c_of_probe(np, dev); if (dev->tft > max_fifo_threshold) { dev_warn(&adev->dev, "requested TX FIFO threshold %u, adjusted down to %u\n", dev->tft, max_fifo_threshold); dev->tft = max_fifo_threshold; } if (dev->rft > max_fifo_threshold) { dev_warn(&adev->dev, "requested RX FIFO threshold %u, adjusted down to %u\n", dev->rft, max_fifo_threshold); dev->rft = max_fifo_threshold; } amba_set_drvdata(adev, dev); dev->virtbase = devm_ioremap(&adev->dev, adev->res.start, resource_size(&adev->res)); if (!dev->virtbase) { ret = -ENOMEM; goto err_no_mem; } dev->irq = adev->irq[0]; ret = devm_request_irq(&adev->dev, dev->irq, i2c_irq_handler, 0, DRIVER_NAME, dev); if (ret) { dev_err(&adev->dev, "cannot claim the irq %d\n", dev->irq); goto err_no_mem; } pm_suspend_ignore_children(&adev->dev, true); dev->clk = devm_clk_get(&adev->dev, NULL); if (IS_ERR(dev->clk)) { dev_err(&adev->dev, "could not get i2c clock\n"); ret = PTR_ERR(dev->clk); goto err_no_mem; } ret = clk_prepare_enable(dev->clk); if (ret) { dev_err(&adev->dev, "can't prepare_enable clock\n"); goto err_no_mem; } init_hw(dev); adap = &dev->adap; adap->dev.of_node = np; adap->dev.parent = &adev->dev; adap->owner = THIS_MODULE; adap->class = I2C_CLASS_DEPRECATED; adap->algo = &nmk_i2c_algo; adap->timeout = msecs_to_jiffies(dev->timeout); snprintf(adap->name, sizeof(adap->name), "Nomadik I2C at %pR", &adev->res); i2c_set_adapdata(adap, dev); dev_info(&adev->dev, "initialize %s on virtual base %p\n", adap->name, dev->virtbase); ret = i2c_add_adapter(adap); if (ret) { dev_err(&adev->dev, "failed to add adapter\n"); goto err_no_adap; } pm_runtime_put(&adev->dev); return 0; err_no_adap: clk_disable_unprepare(dev->clk); err_no_mem: return ret; } static int nmk_i2c_remove(struct amba_device *adev) { struct resource *res = &adev->res; struct nmk_i2c_dev *dev = amba_get_drvdata(adev); i2c_del_adapter(&dev->adap); flush_i2c_fifo(dev); disable_all_interrupts(dev); clear_all_interrupts(dev); /* disable the controller */ i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); clk_disable_unprepare(dev->clk); if (res) release_mem_region(res->start, resource_size(res)); return 0; } static struct i2c_vendor_data vendor_stn8815 = { .has_mtdws = false, .fifodepth = 16, /* Guessed from TFTR/RFTR = 7 */ }; static struct i2c_vendor_data vendor_db8500 = { .has_mtdws = true, .fifodepth = 32, /* Guessed from TFTR/RFTR = 15 */ }; static struct amba_id nmk_i2c_ids[] = { { .id = 0x00180024, .mask = 0x00ffffff, .data = &vendor_stn8815, }, { .id = 0x00380024, .mask = 0x00ffffff, .data = &vendor_db8500, }, {}, }; MODULE_DEVICE_TABLE(amba, nmk_i2c_ids); static struct amba_driver nmk_i2c_driver = { .drv = { .owner = THIS_MODULE, .name = DRIVER_NAME, .pm = &nmk_i2c_pm, }, .id_table = nmk_i2c_ids, .probe = nmk_i2c_probe, .remove = nmk_i2c_remove, }; static int __init nmk_i2c_init(void) { return amba_driver_register(&nmk_i2c_driver); } static void __exit nmk_i2c_exit(void) { amba_driver_unregister(&nmk_i2c_driver); } subsys_initcall(nmk_i2c_init); module_exit(nmk_i2c_exit); MODULE_AUTHOR("Sachin Verma, Srinidhi KASAGAR"); MODULE_DESCRIPTION("Nomadik/Ux500 I2C driver"); MODULE_LICENSE("GPL");