/* Driver for Realtek PCI-Express card reader * * Copyright(c) 2009-2013 Realtek Semiconductor Corp. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see . * * Author: * Wei WANG */ #include #include #include #include #include #include #include #include #include #include #include #include #include "rtsx_pcr.h" static bool msi_en = true; module_param(msi_en, bool, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(msi_en, "Enable MSI"); static DEFINE_IDR(rtsx_pci_idr); static DEFINE_SPINLOCK(rtsx_pci_lock); static struct mfd_cell rtsx_pcr_cells[] = { [RTSX_SD_CARD] = { .name = DRV_NAME_RTSX_PCI_SDMMC, }, [RTSX_MS_CARD] = { .name = DRV_NAME_RTSX_PCI_MS, }, }; static const struct pci_device_id rtsx_pci_ids[] = { { PCI_DEVICE(0x10EC, 0x5209), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5229), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5289), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5227), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x522A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5249), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5287), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x5286), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x524A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { PCI_DEVICE(0x10EC, 0x525A), PCI_CLASS_OTHERS << 16, 0xFF0000 }, { 0, } }; MODULE_DEVICE_TABLE(pci, rtsx_pci_ids); static inline void rtsx_pci_enable_aspm(struct rtsx_pcr *pcr) { rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, 0xFC, pcr->aspm_en); } static inline void rtsx_pci_disable_aspm(struct rtsx_pcr *pcr) { rtsx_pci_update_cfg_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL, 0xFC, 0); } void rtsx_pci_start_run(struct rtsx_pcr *pcr) { /* If pci device removed, don't queue idle work any more */ if (pcr->remove_pci) return; if (pcr->state != PDEV_STAT_RUN) { pcr->state = PDEV_STAT_RUN; if (pcr->ops->enable_auto_blink) pcr->ops->enable_auto_blink(pcr); if (pcr->aspm_en) rtsx_pci_disable_aspm(pcr); } mod_delayed_work(system_wq, &pcr->idle_work, msecs_to_jiffies(200)); } EXPORT_SYMBOL_GPL(rtsx_pci_start_run); int rtsx_pci_write_register(struct rtsx_pcr *pcr, u16 addr, u8 mask, u8 data) { int i; u32 val = HAIMR_WRITE_START; val |= (u32)(addr & 0x3FFF) << 16; val |= (u32)mask << 8; val |= (u32)data; rtsx_pci_writel(pcr, RTSX_HAIMR, val); for (i = 0; i < MAX_RW_REG_CNT; i++) { val = rtsx_pci_readl(pcr, RTSX_HAIMR); if ((val & HAIMR_TRANS_END) == 0) { if (data != (u8)val) return -EIO; return 0; } } return -ETIMEDOUT; } EXPORT_SYMBOL_GPL(rtsx_pci_write_register); int rtsx_pci_read_register(struct rtsx_pcr *pcr, u16 addr, u8 *data) { u32 val = HAIMR_READ_START; int i; val |= (u32)(addr & 0x3FFF) << 16; rtsx_pci_writel(pcr, RTSX_HAIMR, val); for (i = 0; i < MAX_RW_REG_CNT; i++) { val = rtsx_pci_readl(pcr, RTSX_HAIMR); if ((val & HAIMR_TRANS_END) == 0) break; } if (i >= MAX_RW_REG_CNT) return -ETIMEDOUT; if (data) *data = (u8)(val & 0xFF); return 0; } EXPORT_SYMBOL_GPL(rtsx_pci_read_register); int __rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) { int err, i, finished = 0; u8 tmp; rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA0, 0xFF, (u8)val); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYDATA1, 0xFF, (u8)(val >> 8)); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x81); err = rtsx_pci_send_cmd(pcr, 100); if (err < 0) return err; for (i = 0; i < 100000; i++) { err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); if (err < 0) return err; if (!(tmp & 0x80)) { finished = 1; break; } } if (!finished) return -ETIMEDOUT; return 0; } int rtsx_pci_write_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 val) { if (pcr->ops->write_phy) return pcr->ops->write_phy(pcr, addr, val); return __rtsx_pci_write_phy_register(pcr, addr, val); } EXPORT_SYMBOL_GPL(rtsx_pci_write_phy_register); int __rtsx_pci_read_phy_register(struct rtsx_pcr *pcr, u8 addr, u16 *val) { int err, i, finished = 0; u16 data; u8 *ptr, tmp; rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYADDR, 0xFF, addr); rtsx_pci_add_cmd(pcr, WRITE_REG_CMD, PHYRWCTL, 0xFF, 0x80); err = rtsx_pci_send_cmd(pcr, 100); if (err < 0) return err; for (i = 0; i < 100000; i++) { err = rtsx_pci_read_register(pcr, PHYRWCTL, &tmp); if (err < 0) return err; if (!(tmp & 0x80)) { finished = 1; break; } } if (!finished) return -ETIMEDOUT; rtsx_pci_init_cmd(pcr); rtsx_pci_add_cmd(pcr, READ_REG_CMD, PHYDATA0, 0, 0); rtsx_pci_add_cmd(pcr, RE
# Enable the creation of Neutron networks for isolated Overcloud
# traffic and configure each role to assign ports (related
# to that role) on these networks.
# Many networks are disabled by default because they are not used
# in a typical configuration. Override via parameter_defaults.
resource_registry:
  OS::TripleO::Network::External: ../network/external.yaml
  OS::TripleO::Network::InternalApi: ../network/internal_api.yaml
  OS::TripleO::Network::StorageMgmt: ../network/storage_mgmt.yaml
  OS::TripleO::Network::Storage: ../network/storage.yaml
  OS::TripleO::Network::Tenant: ../network/tenant.yaml
  # Management network is optional and disabled by default
  OS::TripleO::Network::Management: OS::Heat::None

  # Port assignments for the VIPs
  OS::TripleO::Network::Ports::ExternalVipPort: ../network/ports/external.yaml
  OS::TripleO::Network::Ports::InternalApiVipPort: ../network/ports/internal_api.yaml
  OS::TripleO::Network::Ports::StorageVipPort: ../network/ports/storage.yaml
  OS::TripleO::Network::Ports::StorageMgmtVipPort: ../network/ports/storage_mgmt.yaml
  OS::TripleO::Network::Ports::RedisVipPort: ../network/ports/vip.yaml

  # Port assignments for the controller role
  OS::TripleO::Controller::Ports::ExternalPort: ../network/ports/external.yaml
  OS::TripleO::Controller::Ports::InternalApiPort: ../network/ports/internal_api.yaml
  OS::TripleO::Controller::Ports::StoragePort: ../network/ports/storage.yaml
  OS::TripleO::Controller::Ports::StorageMgmtPort: ../network/ports/storage_mgmt.yaml
  OS::TripleO::Controller::Ports::TenantPort: ../network/ports/tenant.yaml
  OS::TripleO::Controller::Ports::ManagementPort: ../network/ports/noop.yaml

  # Port assignments for the compute role
  OS::TripleO::Compute::Ports::ExternalPort: ../network/ports/noop.yaml
  OS::TripleO::Compute::Ports::InternalApiPort: ../network/ports/internal_api.yaml
  OS::TripleO::Compute::Ports::StoragePort: ../network/ports/storage.yaml
  OS::TripleO::Compute::Ports::StorageMgmtPort: ../network/ports/noop.yaml
  OS::TripleO::Compute::Ports::TenantPort: ../network/ports/tenant.yaml
  OS::TripleO::Compute::Ports::ManagementPort: ../network/ports/noop.yaml

  # Port assignments for the ceph storage role
  OS::TripleO::CephStorage::Ports::ExternalPort: ../network/ports/noop.yaml
  OS::TripleO::CephStorage::Ports::InternalApiPort: ../network/ports/noop.yaml
  OS::TripleO::CephStorage::Ports::StoragePort: ../network/ports/storage.yaml
  OS::TripleO::CephStorage::Ports::StorageMgmtPort: ../network/ports/storage_mgmt.yaml
  OS::TripleO::CephStorage::Ports::TenantPort: ../network/ports/noop.yaml
  OS::TripleO::CephStorage::Ports::ManagementPort: ../network/ports/noop.yaml

  # Port assignments for the swift storage role
  OS::TripleO::SwiftStorage::Ports::ExternalPort: ../network/ports/noop.yaml
  OS::TripleO::SwiftStorage::Ports::InternalApiPort: ../network/ports/internal_api.yaml
  OS::TripleO::SwiftStorage::Ports::StoragePort: ../network/ports/storage.yaml
  OS::TripleO::SwiftStorage::Ports::StorageMgmtPort: ../network/ports/storage_mgmt.yaml
  OS::TripleO::SwiftStorage::Ports::TenantPort: ../network/ports/noop.yaml
  OS::TripleO::SwiftStorage::Ports::ManagementPort: ../network/ports/noop.yaml

  # Port assignments for the block storage role
  OS::TripleO::BlockStorage::Ports::ExternalPort: ../network/ports/noop.yaml
  OS::TripleO::BlockStorage::Ports::InternalApiPort: ../network/ports/internal_api.yaml
  OS::TripleO::BlockStorage::Ports::StoragePort: ../network/ports/storage.yaml
  OS::TripleO::BlockStorage::Ports::StorageMgmtPort: ../network/ports/storage_mgmt.yaml
  OS::TripleO::BlockStorage::Ports::TenantPort: ../network/ports/noop.yaml
  OS::TripleO::BlockStorage::Ports::ManagementPort: ../network/ports/noop.yaml

  # Port assignments for service virtual IPs for the controller role
  OS::TripleO::Controller::Ports::RedisVipPort: ../network/ports/vip.yaml
rr = rtsx_pci_send_cmd(pcr, 100); if (err < 0) return err; /* Enable clk_request_n to enable clock power management */ rtsx_pci_write_config_byte(pcr, pcr->pcie_cap + PCI_EXP_LNKCTL + 1, 1); /* Enter L1 when host tx idle */ rtsx_pci_write_config_byte(pcr, 0x70F, 0x5B); if (pcr->ops->extra_init_hw) { err = pcr->ops->extra_init_hw(pcr); if (err < 0) return err; } /* No CD interrupt if probing driver with card inserted. * So we need to initialize pcr->card_exist here. */ if (pcr->ops->cd_deglitch) pcr->card_exist = pcr->ops->cd_deglitch(pcr); else pcr->card_exist = rtsx_pci_readl(pcr, RTSX_BIPR) & CARD_EXIST; return 0; } static int rtsx_pci_init_chip(struct rtsx_pcr *pcr) { int err; spin_lock_init(&pcr->lock); mutex_init(&pcr->pcr_mutex); switch (PCI_PID(pcr)) { default: case 0x5209: rts5209_init_params(pcr); break; case 0x5229: rts5229_init_params(pcr); break; case 0x5289: rtl8411_init_params(pcr); break; case 0x5227: rts5227_init_params(pcr); break; case 0x522A: rts522a_init_params(pcr); break; case 0x5249: rts5249_init_params(pcr); break; case 0x524A: rts524a_init_params(pcr); break; case 0x525A: rts525a_init_params(pcr); break; case 0x5287: rtl8411b_init_params(pcr); break; case 0x5286: rtl8402_init_params(pcr); break; } pcr_dbg(pcr, "PID: 0x%04x, IC version: 0x%02x\n", PCI_PID(pcr), pcr->ic_version); pcr->slots = kcalloc(pcr->num_slots, sizeof(struct rtsx_slot), GFP_KERNEL); if (!pcr->slots) return -ENOMEM; if (pcr->ops->fetch_vendor_settings) pcr->ops->fetch_vendor_settings(pcr); pcr_dbg(pcr, "pcr->aspm_en = 0x%x\n", pcr->aspm_en); pcr_dbg(pcr, "pcr->sd30_drive_sel_1v8 = 0x%x\n", pcr->sd30_drive_sel_1v8); pcr_dbg(pcr, "pcr->sd30_drive_sel_3v3 = 0x%x\n", pcr->sd30_drive_sel_3v3); pcr_dbg(pcr, "pcr->card_drive_sel = 0x%x\n", pcr->card_drive_sel); pcr_dbg(pcr, "pcr->flags = 0x%x\n", pcr->flags); pcr->state = PDEV_STAT_IDLE; err = rtsx_pci_init_hw(pcr); if (err < 0) { kfree(pcr->slots); return err; } return 0; } static int rtsx_pci_probe(struct pci_dev *pcidev, const struct pci_device_id *id) { struct rtsx_pcr *pcr; struct pcr_handle *handle; u32 base, len; int ret, i, bar = 0; dev_dbg(&(pcidev->dev), ": Realtek PCI-E Card Reader found at %s [%04x:%04x] (rev %x)\n", pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device, (int)pcidev->revision); ret = pci_set_dma_mask(pcidev, DMA_BIT_MASK(32)); if (ret < 0) return ret; ret = pci_enable_device(pcidev); if (ret) return ret; ret = pci_request_regions(pcidev, DRV_NAME_RTSX_PCI); if (ret) goto disable; pcr = kzalloc(sizeof(*pcr), GFP_KERNEL); if (!pcr) { ret = -ENOMEM; goto release_pci; } handle = kzalloc(sizeof(*handle), GFP_KERNEL); if (!handle) { ret = -ENOMEM; goto free_pcr; } handle->pcr = pcr; idr_preload(GFP_KERNEL); spin_lock(&rtsx_pci_lock); ret = idr_alloc(&rtsx_pci_idr, pcr, 0, 0, GFP_NOWAIT); if (ret >= 0) pcr->id = ret; spin_unlock(&rtsx_pci_lock); idr_preload_end(); if (ret < 0) goto free_handle; pcr->pci = pcidev; dev_set_drvdata(&pcidev->dev, handle); if (CHK_PCI_PID(pcr, 0x525A)) bar = 1; len = pci_resource_len(pcidev, bar); base = pci_resource_start(pcidev, bar); pcr->remap_addr = ioremap_nocache(base, len); if (!pcr->remap_addr) { ret = -ENOMEM; goto free_handle; } pcr->rtsx_resv_buf = dma_alloc_coherent(&(pcidev->dev), RTSX_RESV_BUF_LEN, &(pcr->rtsx_resv_buf_addr), GFP_KERNEL); if (pcr->rtsx_resv_buf == NULL) { ret = -ENXIO; goto unmap; } pcr->host_cmds_ptr = pcr->rtsx_resv_buf; pcr->host_cmds_addr = pcr->rtsx_resv_buf_addr; pcr->host_sg_tbl_ptr = pcr->rtsx_resv_buf + HOST_CMDS_BUF_LEN; pcr->host_sg_tbl_addr = pcr->rtsx_resv_buf_addr + HOST_CMDS_BUF_LEN; pcr->card_inserted = 0; pcr->card_removed = 0; INIT_DELAYED_WORK(&pcr->carddet_work, rtsx_pci_card_detect); INIT_DELAYED_WORK(&pcr->idle_work, rtsx_pci_idle_work); pcr->msi_en = msi_en; if (pcr->msi_en) { ret = pci_enable_msi(pcidev); if (ret) pcr->msi_en = false; } ret = rtsx_pci_acquire_irq(pcr); if (ret < 0) goto disable_msi; pci_set_master(pcidev); synchronize_irq(pcr->irq); ret = rtsx_pci_init_chip(pcr); if (ret < 0) goto disable_irq; for (i = 0; i < ARRAY_SIZE(rtsx_pcr_cells); i++) { rtsx_pcr_cells[i].platform_data = handle; rtsx_pcr_cells[i].pdata_size = sizeof(*handle); } ret = mfd_add_devices(&pcidev->dev, pcr->id, rtsx_pcr_cells, ARRAY_SIZE(rtsx_pcr_cells), NULL, 0, NULL); if (ret < 0) goto disable_irq; schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); return 0; disable_irq: free_irq(pcr->irq, (void *)pcr); disable_msi: if (pcr->msi_en) pci_disable_msi(pcr->pci); dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); unmap: iounmap(pcr->remap_addr); free_handle: kfree(handle); free_pcr: kfree(pcr); release_pci: pci_release_regions(pcidev); disable: pci_disable_device(pcidev); return ret; } static void rtsx_pci_remove(struct pci_dev *pcidev) { struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; pcr->remove_pci = true; /* Disable interrupts at the pcr level */ spin_lock_irq(&pcr->lock); rtsx_pci_writel(pcr, RTSX_BIER, 0); pcr->bier = 0; spin_unlock_irq(&pcr->lock); cancel_delayed_work_sync(&pcr->carddet_work); cancel_delayed_work_sync(&pcr->idle_work); mfd_remove_devices(&pcidev->dev); dma_free_coherent(&(pcr->pci->dev), RTSX_RESV_BUF_LEN, pcr->rtsx_resv_buf, pcr->rtsx_resv_buf_addr); free_irq(pcr->irq, (void *)pcr); if (pcr->msi_en) pci_disable_msi(pcr->pci); iounmap(pcr->remap_addr); pci_release_regions(pcidev); pci_disable_device(pcidev); spin_lock(&rtsx_pci_lock); idr_remove(&rtsx_pci_idr, pcr->id); spin_unlock(&rtsx_pci_lock); kfree(pcr->slots); kfree(pcr); kfree(handle); dev_dbg(&(pcidev->dev), ": Realtek PCI-E Card Reader at %s [%04x:%04x] has been removed\n", pci_name(pcidev), (int)pcidev->vendor, (int)pcidev->device); } #ifdef CONFIG_PM static int rtsx_pci_suspend(struct pci_dev *pcidev, pm_message_t state) { struct pcr_handle *handle; struct rtsx_pcr *pcr; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); handle = pci_get_drvdata(pcidev); pcr = handle->pcr; cancel_delayed_work(&pcr->carddet_work); cancel_delayed_work(&pcr->idle_work); mutex_lock(&pcr->pcr_mutex); rtsx_pci_power_off(pcr, HOST_ENTER_S3); pci_save_state(pcidev); pci_enable_wake(pcidev, pci_choose_state(pcidev, state), 0); pci_disable_device(pcidev); pci_set_power_state(pcidev, pci_choose_state(pcidev, state)); mutex_unlock(&pcr->pcr_mutex); return 0; } static int rtsx_pci_resume(struct pci_dev *pcidev) { struct pcr_handle *handle; struct rtsx_pcr *pcr; int ret = 0; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); handle = pci_get_drvdata(pcidev); pcr = handle->pcr; mutex_lock(&pcr->pcr_mutex); pci_set_power_state(pcidev, PCI_D0); pci_restore_state(pcidev); ret = pci_enable_device(pcidev); if (ret) goto out; pci_set_master(pcidev); ret = rtsx_pci_write_register(pcr, HOST_SLEEP_STATE, 0x03, 0x00); if (ret) goto out; ret = rtsx_pci_init_hw(pcr); if (ret) goto out; schedule_delayed_work(&pcr->idle_work, msecs_to_jiffies(200)); out: mutex_unlock(&pcr->pcr_mutex); return ret; } static void rtsx_pci_shutdown(struct pci_dev *pcidev) { struct pcr_handle *handle; struct rtsx_pcr *pcr; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); handle = pci_get_drvdata(pcidev); pcr = handle->pcr; rtsx_pci_power_off(pcr, HOST_ENTER_S1); pci_disable_device(pcidev); } #else /* CONFIG_PM */ #define rtsx_pci_suspend NULL #define rtsx_pci_resume NULL #define rtsx_pci_shutdown NULL #endif /* CONFIG_PM */ static struct pci_driver rtsx_pci_driver = { .name = DRV_NAME_RTSX_PCI, .id_table = rtsx_pci_ids, .probe = rtsx_pci_probe, .remove = rtsx_pci_remove, .suspend = rtsx_pci_suspend, .resume = rtsx_pci_resume, .shutdown = rtsx_pci_shutdown, }; module_pci_driver(rtsx_pci_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Wei WANG "); MODULE_DESCRIPTION("Realtek PCI-E Card Reader Driver");