diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/fsl_pme2/Makefile | 2 | ||||
-rw-r--r-- | drivers/staging/fsl_pme2/pme2_ctrl.c | 123 | ||||
-rw-r--r-- | drivers/staging/fsl_pme2/pme2_private.h | 222 | ||||
-rw-r--r-- | drivers/staging/fsl_pme2/pme2_suspend.c | 1187 |
4 files changed, 1532 insertions, 2 deletions
diff --git a/drivers/staging/fsl_pme2/Makefile b/drivers/staging/fsl_pme2/Makefile index 694513b..e91c514 100644 --- a/drivers/staging/fsl_pme2/Makefile +++ b/drivers/staging/fsl_pme2/Makefile @@ -1,5 +1,5 @@ # PME -obj-$(CONFIG_FSL_PME2_CTRL) += pme2_ctrl.o pme2_sysfs.o +obj-$(CONFIG_FSL_PME2_CTRL) += pme2_ctrl.o pme2_sysfs.o pme2_suspend.o obj-$(CONFIG_FSL_PME2_PORTAL) += pme2.o pme2-y := pme2_low.o pme2_high.o obj-$(CONFIG_FSL_PME2_TEST_HIGH) += pme2_test_high.o diff --git a/drivers/staging/fsl_pme2/pme2_ctrl.c b/drivers/staging/fsl_pme2/pme2_ctrl.c index 30956c2..9901fe6 100644 --- a/drivers/staging/fsl_pme2/pme2_ctrl.c +++ b/drivers/staging/fsl_pme2/pme2_ctrl.c @@ -295,6 +295,7 @@ static int of_fsl_pme_probe(struct platform_device *ofdev) int srec_aim = 0, srec_esr = 0; u32 srecontextsize_code; u32 dec1; + struct pme2_private_data *priv_data; /* * TODO: This standby handling won't work properly after failover, it's @@ -415,6 +416,11 @@ static int of_fsl_pme_probe(struct platform_device *ofdev) (CONFIG_FSL_PME2_SRE_MAX_INSTRUCTION_LIMIT << 16) | CONFIG_FSL_PME2_SRE_MAX_BLOCK_NUMBER); +#ifdef CONFIG_PM + /* can't flush pme device easily. Disable caching for FC and RES */ + pme_out(global_pme, CDCR, 0x00000009); +#endif + /* Setup Accumulator */ if (pme_stat_interval) schedule_delayed_work(&accumulator_work, @@ -424,6 +430,19 @@ static int of_fsl_pme_probe(struct platform_device *ofdev) if (err) goto out_stop_accumulator; + priv_data = kzalloc(sizeof(*priv_data), GFP_KERNEL); + if (!priv_data) + goto out_stop_accumulator; + + priv_data->regs = (uint32_t __iomem *)regs; + priv_data->pme_rev1 = pme_in(global_pme, PM_IP_REV1); + dev_set_drvdata(dev, priv_data); + +#ifdef CONFIG_PM + /* setup resources required for power management */ + init_pme_suspend(priv_data); +#endif + /* Enable interrupts */ pme_out(global_pme, IER, PME_ALL_ERR); dev_info(dev, "ver: 0x%08x\n", pme_in(global_pme, PM_IP_REV1)); @@ -448,11 +467,79 @@ out: return err; } +#ifdef CONFIG_PM +void restore_all_ccsr(struct ccsr_backup_info *save_ccsr, + uint32_t __iomem *regs) +{ + int i; + int num_regs = sizeof(save_ccsr->regdb)/sizeof(uint32_t); + + uint32_t *pme_reg = &save_ccsr->regdb.pmfa.isr; + + for (i = 0; i < num_regs; i++) { + /* skip enable register */ + if ((pme_reg + i) != (&save_ccsr->regdb.pmfa.faconf)) + out_be32(regs + i, *(pme_reg + i)); + } +} + +void save_all_ccsr(struct ccsr_backup_info *save_ccsr, uint32_t __iomem *regs) +{ + int i; + int num_regs = sizeof(save_ccsr->regdb)/sizeof(uint32_t); + uint32_t *pme_reg; + + /* setup ddr space to save ccsr */ + pme_reg = &save_ccsr->regdb.pmfa.isr; + + for (i = 0; i < num_regs; i++) + *(pme_reg+i) = in_be32(regs + i); +} + +static int pme2_pm_suspend(struct device *dev) +{ + struct pme2_private_data *priv_data; + + priv_data = dev_get_drvdata(dev); + if (!priv_data) { + pr_err("No device data\n"); + return -ENOMEM; + } + dev_dbg(dev, "fsl-pme PM suspend\n"); + + return pme_suspend(priv_data); +} + +static int pme2_pm_resume(struct device *dev) +{ + struct pme2_private_data *priv_data; + + priv_data = dev_get_drvdata(dev); + if (!priv_data) { + pr_err("No device data\n"); + return -ENOMEM; + } + dev_dbg(dev, "fsl-pme PM resume\n"); + + return pme_resume(priv_data); + +} +#else +#define pme2_pm_suspend NULL +#define pme2_pm_resume NULL +#endif + +static const struct dev_pm_ops pme2_pm_ops = { + .suspend = pme2_pm_suspend, + .resume = pme2_pm_resume, +}; + static struct platform_driver of_fsl_pme_driver = { .driver = { .owner = THIS_MODULE, .name = DRV_NAME, .of_match_table = of_fsl_pme_ids, + .pm = &pme2_pm_ops, }, .probe = of_fsl_pme_probe, .remove = of_fsl_pme_remove, @@ -768,7 +855,25 @@ int pme_attr_set(enum pme_attr attr, u32 val) case pme_attr_pmtr: pme_out(global_pme, PMTR, val); break; - + case pme_attr_faconf_rst: + attr_val = pme_in(global_pme, FACONF); + if (val) + attr_val |= PME_FACONF_RESET; + else + attr_val &= ~(PME_FACONF_RESET); + pme_out(global_pme, FACONF, attr_val); + break; + case pme_attr_faconf_en: + attr_val = pme_in(global_pme, FACONF); + if (val) + attr_val |= PME_FACONF_ENABLE; + else + attr_val &= ~(PME_FACONF_ENABLE); + pme_out(global_pme, FACONF, attr_val); + break; + case pme_attr_efqc: + pme_out(global_pme, EFQC, val); + break; default: pr_err("pme: Unknown attr %u\n", attr); return -EINVAL; @@ -1246,6 +1351,22 @@ int pme_attr_get(enum pme_attr attr, u32 *val) attr_val = pme_in(global_pme, SRRWC); break; + case pme_attr_faconf_rst: + attr_val = pme_in(global_pme, FACONF); + attr_val &= PME_FACONF_RESET; + break; + + case pme_attr_faconf_en: + attr_val = pme_in(global_pme, FACONF); + attr_val &= PME_FACONF_ENABLE; + attr_val >>= 1; + break; + + case pme_attr_efqc: + attr_val = pme_in(global_pme, EFQC); + break; + + default: pr_err("pme: Unknown attr %u\n", attr); return -EINVAL; diff --git a/drivers/staging/fsl_pme2/pme2_private.h b/drivers/staging/fsl_pme2/pme2_private.h index 3968f02..e1ea3e8 100644 --- a/drivers/staging/fsl_pme2/pme2_private.h +++ b/drivers/staging/fsl_pme2/pme2_private.h @@ -29,6 +29,9 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef PME2_PRIVATE_H +#define PME2_PRIVATE_H + #include "pme2_sys.h" #include <linux/fsl_pme.h> @@ -50,6 +53,218 @@ void pme2_remove_sysfs_dev_files(struct platform_device *ofdev); void accumulator_update_interval(u32 interval); #endif +#ifdef CONFIG_PM + +struct pme_save_regs_pmfa { + uint32_t isr; + uint32_t ier; + uint32_t isdr; + uint32_t iir; + uint32_t ifr; + uint32_t rll; + uint32_t cdcr; + uint32_t reserved1[2]; + uint32_t trunci; + uint32_t rbc; + uint32_t esr; + uint32_t ecr0; + uint32_t ecr1; + uint32_t reserved2[6]; + uint32_t efqc; + uint32_t sram_addr; + uint32_t sram_rdat; + uint32_t sram_wdat; + uint32_t faconf; + uint32_t pmstat; + uint32_t famcr; + uint32_t pmtr; + uint32_t reserved3; + uint32_t pehd; + uint32_t reserved4[2]; + uint32_t bsc0; + uint32_t bsc1; + uint32_t bsc2; + uint32_t bsc3; + uint32_t bsc4; + uint32_t bsc5; + uint32_t bsc6; + uint32_t bsc7; + uint32_t reserved5[16]; + uint32_t qmbfd0; + uint32_t qmbfd1; + uint32_t qmbfd2; + uint32_t qmbfd3; + uint32_t qmbctxtah; + uint32_t qmbctxtal; + uint32_t qmbctxtb; + uint32_t qmbctl; + uint32_t ecc1bes; + uint32_t ecc2bes; + uint32_t reserved6[2]; + uint32_t eccaddr; + uint32_t reserved7[27]; + uint32_t tbt0ecc1th; + uint32_t tbt0ecc1ec; + uint32_t tbt1ecc1th; + uint32_t tbt1ecc1ec; + uint32_t vlt0ecc1th; + uint32_t vlt0ecc1ec; + uint32_t vlt1ecc1th; + uint32_t vlt1ecc1ec; + uint32_t cmecc1th; + uint32_t cmecc1ec; + uint32_t reserved8[2]; + uint32_t dxcmecc1th; + uint32_t dxcmecc1ec; + uint32_t reserved9[2]; + uint32_t dxemecc1th; + uint32_t dxemecc1ec; + uint32_t reserved10[14]; +}; + +struct pme_save_regs_kes { + uint32_t stnib; + uint32_t stnis; + uint32_t stnth1; + uint32_t stnth2; + uint32_t stnthv; + uint32_t stnths; + uint32_t stnch; + uint32_t swdb; + uint32_t kvlts; + uint32_t kec; + uint32_t reserved1[22]; +}; + +struct pme_save_regs_dxe { + uint32_t stnpm; + uint32_t stns1m; + uint32_t drcic; + uint32_t drcmc; + uint32_t stnpmr; + uint32_t reserved1[3]; + uint32_t pdsrbah; + uint32_t pdsrbal; + uint32_t dmcr; + uint32_t dec0; + uint32_t dec1; + uint32_t reserved2[3]; + uint32_t dlc; + uint32_t reserved3[15]; +}; + +struct pme_save_regs_sre { + uint32_t stndrs; + uint32_t stnesr; + uint32_t stns1r; + uint32_t stnob; + uint32_t scbarh; + uint32_t scbarl; + uint32_t smcr; + uint32_t reserved1; + uint32_t srec; + uint32_t reserved2; + uint32_t esrp; + uint32_t reserved3[3]; + uint32_t srrv0; + uint32_t srrv1; + uint32_t srrv2; + uint32_t srrv3; + uint32_t srrv4; + uint32_t srrv5; + uint32_t srrv6; + uint32_t srrv7; + uint32_t srrfi; + uint32_t reserved4; + uint32_t srri; + uint32_t srrr; + uint32_t srrwc; + uint32_t sfrcc; + uint32_t sec1; + uint32_t sec2; + uint32_t sec3; + uint32_t reserved5; +}; + +struct pme_save_regs_mia { + uint32_t mia_byc; + uint32_t mia_blc; + uint32_t mia_ce; + uint32_t reserved1; + uint32_t mia_cr; + uint32_t reserved2[284]; +}; + +struct pme_save_regs_gen { + uint32_t liodnbr; + uint32_t reserved1[126]; + uint32_t srcidr; + uint32_t reserved2[2]; + uint32_t liodnr; + uint32_t reserved3[122]; + uint32_t pm_ip_rev_1; + uint32_t pm_ip_rev_2; +}; + +struct pme_save_reg_all { + struct pme_save_regs_pmfa pmfa; + struct pme_save_regs_kes kes; + struct pme_save_regs_dxe dxe; + struct pme_save_regs_sre sre; + struct pme_save_regs_mia mia; + struct pme_save_regs_gen gen; +}; + +struct pme_pwrmgmt_ctx { + struct qman_fq tx_fq; + struct qman_fq rx_fq; + struct qm_fd result_fd; + struct completion done; +}; + +struct pmtcc_raw_db { + /* vmalloc's memory. Save PME's sram data */ + uint8_t *alldb; +}; + +struct ccsr_backup_info { + uint32_t save_faconf_en; + uint32_t save_cdcr; + struct pme_save_reg_all regdb; +}; + +struct portal_backup_info { + /* vmalloc's memory. Save PME's sram data */ + struct pmtcc_raw_db db; + struct pme_pwrmgmt_ctx *ctx; + struct platform_device *pdev; +}; + +#endif /* CONFIG_PM */ + +struct pme2_private_data { + uint32_t pme_rev1; + uint32_t __iomem *regs; +#ifdef CONFIG_PM + struct ccsr_backup_info save_ccsr; + struct portal_backup_info save_db; +#endif +}; + +#ifdef CONFIG_PM +/* Hooks from pme_ctrl to pme_suspend */ +int init_pme_suspend(struct pme2_private_data *priv_data); +void exit_pme_suspend(struct pme2_private_data *priv_data); +int pme_suspend(struct pme2_private_data *priv_data); +int pme_resume(struct pme2_private_data *priv_data); + +/* Hooks from pme_suspend into pme_ctrl */ +void restore_all_ccsr(struct ccsr_backup_info *save_ccsr, + uint32_t __iomem *regs); +void save_all_ccsr(struct ccsr_backup_info *save_ccsr, + uint32_t __iomem *regs); +#endif + static inline void set_fd_addr(struct qm_fd *fd, dma_addr_t addr) { qm_fd_addr_set64(fd, addr); @@ -215,3 +430,10 @@ static inline int is_version_2_1_4(u32 pme_rev1, u32 pme_rev2) (get_errata_rev(pme_rev2) == 4); } +static inline int is_version(u32 pme_rev1, int major, int minor) +{ + return (get_major_rev(pme_rev1) == major) && + (get_minor_rev(pme_rev1) == minor); +} + +#endif diff --git a/drivers/staging/fsl_pme2/pme2_suspend.c b/drivers/staging/fsl_pme2/pme2_suspend.c new file mode 100644 index 0000000..69951bf --- /dev/null +++ b/drivers/staging/fsl_pme2/pme2_suspend.c @@ -0,0 +1,1187 @@ +/* Copyright 2013 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * This software is provided by Freescale Semiconductor "as is" and any + * express or implied warranties, including, but not limited to, the implied + * warranties of merchantability and fitness for a particular purpose are + * disclaimed. In no event shall Freescale Semiconductor be liable for any + * direct, indirect, incidental, special, exemplary, or consequential damages + * (including, but not limited to, procurement of substitute goods or services; + * loss of use, data, or profits; or business interruption) however caused and + * on any theory of liability, whether in contract, strict liability, or tort + * (including negligence or otherwise) arising in any way out of the use of + * this software, even if advised of the possibility of such damage. + */ + +#ifdef CONFIG_PM + +#include "pme2_private.h" +#include "pme2_regs.h" +#include <linux/vmalloc.h> + +static dma_addr_t pme_suspend_map(struct platform_device *pdev, void *ptr) +{ + return dma_map_single(&pdev->dev, ptr, 1, DMA_BIDIRECTIONAL); +} + +#ifdef PME_SUSPEND_DEBUG + +static inline void __hexdump(unsigned long start, unsigned long end, + unsigned long p, size_t sz, const unsigned char *c) +{ + while (start < end) { + unsigned int pos = 0; + char buf[64]; + int nl = 0; + pos += sprintf(buf + pos, "%08lx: ", start); + do { + if ((start < p) || (start >= (p + sz))) + pos += sprintf(buf + pos, ".."); + else + pos += sprintf(buf + pos, "%02x", *(c++)); + if (!(++start & 15)) { + buf[pos++] = '\n'; + nl = 1; + } else { + nl = 0; + if (!(start & 1)) + buf[pos++] = ' '; + if (!(start & 3)) + buf[pos++] = ' '; + } + } while (start & 15); + if (!nl) + buf[pos++] = '\n'; + buf[pos] = '\0'; + pr_info("%s", buf); + } +} + +static inline void hexdump(const void *ptr, size_t sz) +{ + unsigned long p = (unsigned long)ptr; + unsigned long start = p & ~(unsigned long)15; + unsigned long end = (p + sz + 15) & ~(unsigned long)15; + const unsigned char *c = ptr; + __hexdump(start, end, p, sz, c); +} +#endif + +/* + * The following SRAM tables need to be saved + * 1-byte trigger table + * 2-byte trigger table + * variable length trigger table + * confidence table + * User-Defined Group Mapping tablle + * Equivalent Byte Mapping table + * Special Trigger table + */ +enum pme_pmtcc_table_id { + PME_ONE_BYTE_TRIGGER_TBL = 0x00, + PME_TWO_BYTE_TRIGGER_TBL = 0x01, + PME_VARIABLE_TRIGGER_TBL = 0x02, + PME_CONFIDENCE_TBL = 0x03, + PME_UDG_TBL = 0x05, + PME_EQUIVALENT_BYTE_TBL = 0x06, + PME_SPECIAL_TRIGGER_TBL = 0x08, + PME_LAST_TABLE = PME_SPECIAL_TRIGGER_TBL +}; + +static enum pme_pmtcc_table_id table_list[] = {PME_ONE_BYTE_TRIGGER_TBL, + PME_TWO_BYTE_TRIGGER_TBL, PME_VARIABLE_TRIGGER_TBL, PME_CONFIDENCE_TBL, + PME_UDG_TBL, PME_EQUIVALENT_BYTE_TBL, PME_SPECIAL_TRIGGER_TBL}; + +struct pme_pmtcc_header_t { + uint8_t protocol_version; + uint8_t msg_type; + uint16_t reserved; + /* total message length, including the header */ + uint32_t msg_length; + uint64_t msg_id; + uint8_t data[0]; +} __packed; + +/* + * The next few macros define the sizes (in bytes) of the entries in + * the different PM H/W tables. + */ +#define PME_ONE_BYTE_TRIGGER_ENTRY_SIZE 32 +#define PME_TWO_BYTE_TRIGGER_ENTRY_SIZE 8 +#define PME_VARIABLE_TRIGGER_ENTRY_SIZE 8 +#define PME_CONFIDENCE_ENTRY_SIZE 4 +#define PME_CONFIRMATION_ENTRY_SIZE 128 +#define PME_USER_DEFINED_GROUP_READ_ENTRY_SIZE 4 +#define PME_USER_DEFINED_GROUP_WRITE_ENTRY_SIZE 256 +#define PME_EQUIVALENCE_READ_ENTRY_SIZE 4 +#define PME_EQUIVALENCE_WRITE_ENTRY_SIZE 256 +#define PME_SESSION_CONTEXT_ENTRY_SIZE 32 +#define PME_SPECIAL_TRIGGER_ENTRY_SIZE 32 + +union pme_table_entry_t { + /* The next few types define the entries for the different PM tables. */ + uint8_t one_byte_trigger_entry[PME_ONE_BYTE_TRIGGER_ENTRY_SIZE]; + uint8_t two_byte_trigger_entry[PME_TWO_BYTE_TRIGGER_ENTRY_SIZE]; + uint8_t variable_trigger_entry[PME_VARIABLE_TRIGGER_ENTRY_SIZE]; + uint8_t confidence_entry[PME_CONFIDENCE_ENTRY_SIZE]; + uint8_t udg_read_entry[PME_USER_DEFINED_GROUP_READ_ENTRY_SIZE]; + uint8_t udg_write_entry[PME_USER_DEFINED_GROUP_WRITE_ENTRY_SIZE]; + uint8_t equivalence_read_entry[PME_EQUIVALENCE_READ_ENTRY_SIZE]; + uint8_t equivalence_write_entry[PME_EQUIVALENCE_WRITE_ENTRY_SIZE]; + uint8_t special_trigger_entry[PME_SPECIAL_TRIGGER_ENTRY_SIZE]; +} __packed; + +/* This type defines an indexed table entry. */ +struct pme_indexed_table_entry_t { + uint32_t index; + union pme_table_entry_t entry; +} __packed; + +/* table read request */ +struct pme_pmtcc_read_request_msg_t { + struct pme_pmtcc_header_t header; + uint32_t table_id; + uint32_t index; +} __packed; + +/* table read reply message. */ +struct pme_pmtcc_read_reply_msg_t { + struct pme_pmtcc_header_t header; + uint32_t table_id; + struct pme_indexed_table_entry_t indexed_entry; +} __packed; + +/* table write request message */ +struct pme_pmtcc_write_request_msg_t { + struct pme_pmtcc_header_t header; + uint32_t table_id; + struct pme_indexed_table_entry_t indexed_entry; +} __packed; + +/* + * The next few macros define the number of entries in the different PM + * H/W tables. + */ +#define PME_CONFIDENCE_ENTRY_NUM_PER_TRIGGER_ENTRY 4 + +#define PME_ONE_BYTE_TRIGGER_ENTRY_NUM 1 + +#define PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V1 512 +#define PME_VARIABLE_TRIGGER_ENTRY_NUM_V1 4096 + +#define PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_0 2048 +#define PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_0 16384 + +#define PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_1 1024 +#define PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_1 8192 + +#define PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_2 512 +#define PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_2 4096 + +#define PME_SPECIAL_CONFIDENCE_ENTRY_NUM 64 +#define PME_ONE_BYTE_CONFIDENCE_ENTRY_NUM 64 + +#define PME_CONFIDENCE_ENTRY_NUM_V1 \ + ((PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V1 + \ + PME_VARIABLE_TRIGGER_ENTRY_NUM_V1 + \ + PME_ONE_BYTE_CONFIDENCE_ENTRY_NUM + \ + PME_SPECIAL_CONFIDENCE_ENTRY_NUM) * \ + PME_CONFIDENCE_ENTRY_NUM_PER_TRIGGER_ENTRY) + +#define PME_CONFIDENCE_ENTRY_NUM_V2_0 \ + ((PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_0 + \ + PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_0 + \ + PME_ONE_BYTE_CONFIDENCE_ENTRY_NUM + \ + PME_SPECIAL_CONFIDENCE_ENTRY_NUM) * \ + PME_CONFIDENCE_ENTRY_NUM_PER_TRIGGER_ENTRY) + +#define PME_CONFIDENCE_ENTRY_NUM_V2_1 \ + ((PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_1 + \ + PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_1 + \ + PME_ONE_BYTE_CONFIDENCE_ENTRY_NUM + \ + PME_SPECIAL_CONFIDENCE_ENTRY_NUM) * \ + PME_CONFIDENCE_ENTRY_NUM_PER_TRIGGER_ENTRY) + +#define PME_CONFIDENCE_ENTRY_NUM_V2_2 \ + ((PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_2 + \ + PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_2 + \ + PME_ONE_BYTE_CONFIDENCE_ENTRY_NUM + \ + PME_SPECIAL_CONFIDENCE_ENTRY_NUM) * \ + PME_CONFIDENCE_ENTRY_NUM_PER_TRIGGER_ENTRY) + +#define PME_EQUIVALENCE_ENTRY_NUM 1 +#define PME_USER_DEFINED_GROUP_ENTRY_NUM 1 +#define PME_SPECIAL_TRIGGER_ENTRY_NUM 1 + +/* + * The next few macros below define the sizes of the different + * messages. Note the the macros related to the table read and write + * messages assume that there is only one entry in the read/write + * message. + */ +#define PME_TABLE_READ_REQUEST_MSG_SIZE \ + sizeof(struct pme_pmtcc_read_request_msg_t) + +#define PME_ONE_BYTE_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_ONE_BYTE_TRIGGER_ENTRY_SIZE) + +#define PME_TWO_BYTE_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_TWO_BYTE_TRIGGER_ENTRY_SIZE) + +#define PME_VARIABLE_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_VARIABLE_TRIGGER_ENTRY_SIZE) + +#define PME_CONFIDENCE_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_CONFIDENCE_ENTRY_SIZE) + +#define PME_UDG_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_USER_DEFINED_GROUP_READ_ENTRY_SIZE) + +#define PME_EQUIVALENCE_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_EQUIVALENCE_READ_ENTRY_SIZE) + +#define PME_SPECIAL_TABLE_READ_REPLY_MSG_SIZE \ + (sizeof(struct pme_pmtcc_read_reply_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_SPECIAL_TRIGGER_ENTRY_SIZE) + +#define PME_ONE_BYTE_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_ONE_BYTE_TRIGGER_ENTRY_SIZE) + +#define PME_TWO_BYTE_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_TWO_BYTE_TRIGGER_ENTRY_SIZE) + +#define PME_VARIABLE_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_VARIABLE_TRIGGER_ENTRY_SIZE) + +#define PME_CONFIDENCE_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_CONFIDENCE_ENTRY_SIZE) + +#define PME_UDG_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_USER_DEFINED_GROUP_WRITE_ENTRY_SIZE) + +#define PME_EQUIVALENCE_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_EQUIVALENCE_WRITE_ENTRY_SIZE) + +#define PME_SPECIAL_TABLE_WRITE_REQUEST_MSG_SIZE \ + (sizeof(struct pme_pmtcc_write_request_msg_t) - \ + sizeof(union pme_table_entry_t) + \ + PME_SPECIAL_TRIGGER_ENTRY_SIZE) + +/* + * Index 0..255, bools do indicated which errors are serious + * 0x40, 0x41, 0x48, 0x49, 0x4c, 0x4e, 0x4f, 0x50, 0x51, 0x59, 0x5a, 0x5b, + * 0x5c, 0x5d, 0x5f, 0x60, 0x80, 0xc0, 0xc1, 0xc2, 0xc4, 0xd2, + * 0xd4, 0xd5, 0xd7, 0xd9, 0xda, 0xe0, 0xe7 + */ +static u8 serious_error_vec[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x00, 0x01, 0x01, + 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static enum qman_cb_dqrr_result cb_dqrr(struct qman_portal *portal, + struct qman_fq *fq, const struct qm_dqrr_entry *dq) +{ + u8 status = (u8)pme_fd_res_status(&dq->fd); + u8 flags = pme_fd_res_flags(&dq->fd); + struct pme_pwrmgmt_ctx *ctx = (struct pme_pwrmgmt_ctx *)fq; + + if (unlikely(flags & PME_STATUS_UNRELIABLE)) + pr_err("pme error %d\n", __LINE__); + else if (unlikely((serious_error_vec[status]))) + pr_err("pme error %d\n", __LINE__); + else { + memcpy(&ctx->result_fd, &dq->fd, sizeof(*&dq->fd)); + complete(&ctx->done); + } + return qman_cb_dqrr_consume; +} + +static void cb_fqs(__always_unused struct qman_portal *portal, + __always_unused struct qman_fq *fq, + const struct qm_mr_entry *mr) +{ + u8 verb = mr->verb & QM_MR_VERB_TYPE_MASK; + if (verb == QM_MR_VERB_FQRNI) + return; + /* nothing else is supposed to occur */ + BUG(); +} + +static const struct qman_fq_cb pme_fq_base_out = { + .dqrr = cb_dqrr, + .fqs = cb_fqs +}; + +static const struct qman_fq_cb pme_fq_base_in = { + .fqs = cb_fqs, + .ern = NULL +}; + +static void pme_pwrmgmt_initfq(struct qm_mcc_initfq *initfq, u32 rfqid) +{ + struct pme_context_a *pme_a = + (struct pme_context_a *)&initfq->fqd.context_a; + struct pme_context_b *pme_b = + (struct pme_context_b *)&initfq->fqd.context_b; + + initfq->we_mask = QM_INITFQ_WE_DESTWQ | QM_INITFQ_WE_CONTEXTA | + QM_INITFQ_WE_CONTEXTB; + initfq->fqd.dest.channel = qm_channel_pme; + initfq->fqd.dest.wq = 4; + pme_a->mode = pme_mode_direct; + pme_context_a_set64(pme_a, 0); + pme_b->rfqid = rfqid; +} + +static int pme_pwrmgmt_ctx_reconfigure_tx(struct pme_pwrmgmt_ctx *ctx) +{ + struct qm_mcc_initfq initfq; + u32 flags = QMAN_INITFQ_FLAG_SCHED | QMAN_INITFQ_FLAG_LOCAL; + int ret; + + memset(&initfq, 0, sizeof(initfq)); + initfq.we_mask = QM_INITFQ_WE_DESTWQ | QM_INITFQ_WE_FQCTRL; + initfq.fqd.dest.wq = 4; + initfq.fqd.fq_ctrl = 0; /* disable stashing */ + ret = qman_init_fq(&ctx->tx_fq, flags, &initfq); + return ret; +} + +static int pme_pwrmgmt_ctx_reconfigure_rx(struct pme_pwrmgmt_ctx *ctx) +{ + struct qm_mcc_initfq initfq; + int ret; + + memset(&initfq, 0, sizeof(initfq)); + pme_pwrmgmt_initfq(&initfq, qman_fq_fqid(&ctx->tx_fq)); + ret = qman_init_fq(&ctx->rx_fq, 0, &initfq); + return ret; +} + +int pme_pwrmgmt_ctx_init(struct pme_pwrmgmt_ctx *ctx) +{ + int ret; + + ctx->tx_fq.cb = pme_fq_base_out; + ctx->rx_fq.cb = pme_fq_base_in; + + /* Create tx (from pme point of view) frame queue */ + ret = qman_create_fq(0, QMAN_FQ_FLAG_TO_DCPORTAL | + QMAN_FQ_FLAG_DYNAMIC_FQID | QMAN_FQ_FLAG_LOCKED, + &ctx->rx_fq); + if (ret) + return ret; + + ret = qman_create_fq(0, QMAN_FQ_FLAG_NO_ENQUEUE | + QMAN_FQ_FLAG_DYNAMIC_FQID | QMAN_FQ_FLAG_LOCKED, + &ctx->tx_fq); + if (ret) + goto create_rx_failed; + + ret = pme_pwrmgmt_ctx_reconfigure_rx(ctx); + if (ret) + goto config_rx_failed; + + ret = pme_pwrmgmt_ctx_reconfigure_tx(ctx); + if (ret) + goto config_tx_failed; + + return 0; +config_tx_failed: +config_rx_failed: + qman_destroy_fq(&ctx->rx_fq, 0); +create_rx_failed: + qman_destroy_fq(&ctx->tx_fq, 0); + return ret; +} + +static void pme_pwrmgmt_ctx_finish(struct pme_pwrmgmt_ctx *ctx) +{ + u32 flags; + int ret; + + ret = qman_retire_fq(&ctx->tx_fq, &flags); + BUG_ON(ret); + BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS); + ret = qman_retire_fq(&ctx->rx_fq, &flags); + BUG_ON(ret); + BUG_ON(flags & QMAN_FQ_STATE_BLOCKOOS); + ret = qman_oos_fq(&ctx->tx_fq); + BUG_ON(ret); + ret = qman_oos_fq(&ctx->rx_fq); + BUG_ON(ret); + qman_destroy_fq(&ctx->tx_fq, 0); + qman_destroy_fq(&ctx->rx_fq, 0); +} + +static int create_pwrmgmt_ctx(struct portal_backup_info *save_db) +{ + int ret; + + /* check to see if context already exists */ + if (save_db->ctx) + return 0; + + save_db->ctx = kzalloc(sizeof(*save_db->ctx), GFP_KERNEL); + if (!save_db->ctx) + return -ENOMEM; + + init_completion(&save_db->ctx->done); + ret = pme_pwrmgmt_ctx_init(save_db->ctx); + if (ret) { + pr_err("Error pme_pwrmgmt_ctx_init\n"); + goto error_free_mem; + } + return 0; + +error_free_mem: + kfree(save_db->ctx); + save_db->ctx = NULL; + return ret; +} + +static int delete_pwrmgmt_ctx(struct portal_backup_info *save_db) +{ + if (!save_db->ctx) + return 0; + + pme_pwrmgmt_ctx_finish(save_db->ctx); + kfree(save_db->ctx); + save_db->ctx = NULL; + + return 0; +} + +/* Send a pmtcc pme frame */ +static int pme_pwrmgmt_ctx_pmtcc(struct pme_pwrmgmt_ctx *ctx, u32 flags, + struct qm_fd *fd) +{ + int ret; + + struct pme_cmd_pmtcc *pmtcc = (struct pme_cmd_pmtcc *)&fd->cmd; + pmtcc->cmd = pme_cmd_pmtcc; + + ret = qman_enqueue(&ctx->rx_fq, fd, flags & + (QMAN_ENQUEUE_FLAG_WAIT | QMAN_ENQUEUE_FLAG_WAIT_INT)); + + return ret; +} + +static int get_table_attributes(enum pme_pmtcc_table_id tbl_id, + uint32_t pme_rev1, int *num_read_entries, int *num_write_entries, + int *read_size, int *read_reply_size, int *write_size, + int *read_entry_size, int *write_entry_size) +{ + *read_size = PME_TABLE_READ_REQUEST_MSG_SIZE; + + switch (tbl_id) { + case PME_ONE_BYTE_TRIGGER_TBL: + *num_read_entries = PME_ONE_BYTE_TRIGGER_ENTRY_NUM; + *num_write_entries = PME_ONE_BYTE_TRIGGER_ENTRY_NUM; + *read_reply_size = PME_ONE_BYTE_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_ONE_BYTE_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = PME_ONE_BYTE_TRIGGER_ENTRY_SIZE; + *write_entry_size = PME_ONE_BYTE_TRIGGER_ENTRY_SIZE; + break; + + case PME_TWO_BYTE_TRIGGER_TBL: + if (is_version(pme_rev1, 2, 0)) + *num_read_entries = PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_0; + else if (is_version(pme_rev1, 2, 1)) + *num_read_entries = PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_1; + else if (is_version(pme_rev1, 2, 2)) + *num_read_entries = PME_TWO_BYTE_TRIGGER_ENTRY_NUM_V2_2; + else { + pr_err("pme suspend: unsupported pme version %u\n", + pme_rev1); + return -EINVAL; + } + *num_write_entries = *num_read_entries; + *read_reply_size = PME_TWO_BYTE_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_TWO_BYTE_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = PME_TWO_BYTE_TRIGGER_ENTRY_SIZE; + *write_entry_size = PME_TWO_BYTE_TRIGGER_ENTRY_SIZE; + break; + + case PME_VARIABLE_TRIGGER_TBL: + if (is_version(pme_rev1, 2, 0)) + *num_read_entries = PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_0; + else if (is_version(pme_rev1, 2, 1)) + *num_read_entries = PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_1; + else if (is_version(pme_rev1, 2, 2)) + *num_read_entries = PME_VARIABLE_TRIGGER_ENTRY_NUM_V2_2; + else { + pr_err("pme suspend: unsupported pme version %u\n", + pme_rev1); + return -EINVAL; + } + *num_write_entries = *num_read_entries; + *read_reply_size = PME_VARIABLE_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_VARIABLE_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = PME_VARIABLE_TRIGGER_ENTRY_SIZE; + *write_entry_size = PME_VARIABLE_TRIGGER_ENTRY_SIZE; + break; + + case PME_CONFIDENCE_TBL: + if (is_version(pme_rev1, 2, 0)) + *num_read_entries = PME_CONFIDENCE_ENTRY_NUM_V2_0; + else if (is_version(pme_rev1, 2, 1)) + *num_read_entries = PME_CONFIDENCE_ENTRY_NUM_V2_1; + else if (is_version(pme_rev1, 2, 2)) + *num_read_entries = PME_CONFIDENCE_ENTRY_NUM_V2_2; + else { + pr_err("pme suspend: unsupported pme version %u\n", + pme_rev1); + return -EINVAL; + } + *num_write_entries = *num_read_entries; + *read_reply_size = PME_CONFIDENCE_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_CONFIDENCE_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = PME_CONFIDENCE_ENTRY_SIZE; + *write_entry_size = PME_CONFIDENCE_ENTRY_SIZE; + break; + case PME_UDG_TBL: + *num_read_entries = 256; + *num_write_entries = PME_USER_DEFINED_GROUP_ENTRY_NUM; + *read_reply_size = PME_UDG_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_UDG_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = (PME_UDG_TABLE_READ_REPLY_MSG_SIZE - + PME_TABLE_READ_REQUEST_MSG_SIZE); + *write_entry_size = PME_USER_DEFINED_GROUP_WRITE_ENTRY_SIZE; + break; + + case PME_EQUIVALENT_BYTE_TBL: + *num_read_entries = 256; + *num_write_entries = PME_EQUIVALENCE_ENTRY_NUM; + *read_reply_size = PME_EQUIVALENCE_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_EQUIVALENCE_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = (PME_EQUIVALENCE_TABLE_READ_REPLY_MSG_SIZE - + PME_TABLE_READ_REQUEST_MSG_SIZE); + *write_entry_size = PME_EQUIVALENCE_WRITE_ENTRY_SIZE; + break; + + case PME_SPECIAL_TRIGGER_TBL: + *num_read_entries = PME_SPECIAL_TRIGGER_ENTRY_NUM; + *num_write_entries = PME_SPECIAL_TRIGGER_ENTRY_NUM; + *read_reply_size = PME_SPECIAL_TABLE_READ_REPLY_MSG_SIZE; + *write_size = PME_SPECIAL_TABLE_WRITE_REQUEST_MSG_SIZE; + *read_entry_size = PME_SPECIAL_TRIGGER_ENTRY_SIZE; + *write_entry_size = PME_SPECIAL_TRIGGER_ENTRY_SIZE; + break; + } + return 0; +} + +#ifdef PME_SUSPEND_DEBUG +static int total_size_read_request(enum pme_pmtcc_table_id tbl_id, + uint32_t pme_rev1) +{ + int ret, num_read_entries, read_size, read_reply_size, write_size, + read_entry_size, write_entry_size, num_write_entries; + + ret = get_table_attributes(tbl_id, pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, &write_size, + &read_entry_size, &write_entry_size); + + if (ret) + return ret; + + return num_read_entries * read_size; +} + +static int total_size_read_response_request(enum pme_pmtcc_table_id tbl_id, + uint32_t pme_rev1) +{ + int ret, num_read_entries, read_size, read_reply_size, write_size, + read_entry_size, write_entry_size, num_write_entries; + + ret = get_table_attributes(tbl_id, pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, &write_size, + &read_entry_size, &write_entry_size); + + if (ret) + return ret; + + return num_read_entries * read_reply_size; +} + +static int total_size_write_request(enum pme_pmtcc_table_id tbl_id, + uint32_t pme_rev1) +{ + int ret, num_read_entries, read_size, read_reply_size, write_size, + read_entry_size, write_entry_size, num_write_entries; + + ret = get_table_attributes(tbl_id, pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, &write_size, + &read_entry_size, &write_entry_size); + + if (ret) + return ret; + + return num_write_entries * write_entry_size; +} +#endif + +static int sizeof_all_db_tables(uint32_t pme_rev1) +{ + enum pme_pmtcc_table_id tbl_id; + int i, ret, size = 0; + + for (i = 0; i < ARRAY_SIZE(table_list); i++) { + int num_read_entries, read_size, read_reply_size, write_size, + read_entry_size, write_entry_size, num_write_entries; + tbl_id = table_list[i]; + + ret = get_table_attributes(tbl_id, pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, + &write_size, &read_entry_size, &write_entry_size); + + if (ret) + return ret; + size += (write_entry_size * num_write_entries); + } + return size; +} + +#ifdef PME_SUSPEND_DEBUG +static void print_debug(uint32_t pme_rev1) +{ + int i = 0; + + pr_info("size of db is %d\n", sizeof_all_db_tables(pme_rev1)); + + do { + int num_read_entries, read_size, read_reply_size, write_size, + num_write_entries, read_entry_size, write_entry_size; + + get_table_attributes(table_list[i], pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, + &write_size, &read_entry_size, &write_entry_size); + + pr_info("Table Id %d\n", table_list[i]); + pr_info(" num_read_entries %d, r_sz %d, rr_sz %d, w_sz %d\n", + num_read_entries, read_size, read_reply_size, + write_size); + pr_info(" num_wr_entries %d, r_entry_size %d w_entry_size %d\n", + num_write_entries, read_entry_size, write_entry_size); + pr_info(" total read request size %d\n", + total_size_read_request(table_list[i], pme_rev1)); + pr_info(" total read reply request size %d\n", + total_size_read_response_request(table_list[i], + pme_rev1)); + pr_info(" total write request size %d\n", + total_size_write_request(table_list[i], pme_rev1)); + + if (table_list[i] == PME_LAST_TABLE) + break; + i++; + } while (1); +} +#endif + +static void free_databases(struct portal_backup_info *save_db) +{ + vfree(save_db->db.alldb); + save_db->db.alldb = NULL; +} + +static int alloc_databases(struct pme2_private_data *priv_data) +{ + int sizedb; + + sizedb = sizeof_all_db_tables(priv_data->pme_rev1); + if (sizedb < 0) { + pr_err("Error getting db size\n"); + return -EINVAL; + } + + priv_data->save_db.db.alldb = vzalloc(sizedb); + if (!priv_data->save_db.db.alldb) + return -ENOMEM; + + return 0; +} + +static int save_all_tables(struct portal_backup_info *save_db, + uint32_t pme_rev1) +{ + struct pmtcc_raw_db *db = &save_db->db; + enum pme_pmtcc_table_id tbl_id; + int i, ret; + uint8_t *current_tbl = db->alldb; + + for (i = 0; i < ARRAY_SIZE(table_list); i++) { + int num_read_entries, read_size, read_reply_size, write_size, + read_entry_size, write_entry_size, num_write_entries; + int idx; + struct pme_pmtcc_read_request_msg_t *entry; + struct qm_fd fd; + struct qm_sg_entry *sg_table = NULL; + uint8_t *input_data, *output_data; + enum pme_status status; + + tbl_id = table_list[i]; + ret = get_table_attributes(tbl_id, pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, + &write_size, &read_entry_size, &write_entry_size); + + /* Allocate input and output frame data */ + output_data = kzalloc(read_reply_size, GFP_KERNEL); + input_data = kzalloc(read_size, GFP_KERNEL); + sg_table = kzalloc(2 * sizeof(*sg_table), GFP_KERNEL); + + entry = (struct pme_pmtcc_read_request_msg_t *) + input_data; + entry->header.protocol_version = pme_rev1; + entry->header.msg_length = read_size; + entry->table_id = tbl_id; + + /* build fd */ + memset(&fd, 0, sizeof(fd)); + qm_sg_entry_set64(&sg_table[0], pme_suspend_map(save_db->pdev, + output_data)); + sg_table[0].length = read_reply_size; + qm_sg_entry_set64(&sg_table[1], pme_suspend_map(save_db->pdev, + input_data)); + sg_table[1].length = read_size; + sg_table[1].final = 1; + fd.format = qm_fd_compound; + qm_fd_addr_set64(&fd, pme_suspend_map(save_db->pdev, sg_table)); +#ifdef PME_SUSPEND_DEBUG + pr_info("Doing table %d\n", tbl_id); +#endif + for (idx = 0; idx < num_read_entries; idx++) { + entry->index = idx; + memset(output_data, 0, read_reply_size); + ret = pme_pwrmgmt_ctx_pmtcc(save_db->ctx, + QMAN_ENQUEUE_FLAG_WAIT, &fd); + + if (ret) + pr_err("error with pme_pwrmgmt_ctx_pmtcc\n"); + + wait_for_completion(&save_db->ctx->done); + + status = pme_fd_res_status(&save_db->ctx->result_fd); + if (status) { + ret = -EINVAL; + pr_err("PMTCC read status failed %d\n", status); + } + if (pme_fd_res_flags(&save_db->ctx->result_fd) & + PME_STATUS_UNRELIABLE) { + pr_err("pme %x\n", pme_fd_res_flags( + &save_db->ctx->result_fd)); + ret = -EINVAL; + } + /* copy the response */ + if (tbl_id == PME_UDG_TBL || + tbl_id == PME_EQUIVALENT_BYTE_TBL) { + /* Only copy over 8 lower bits to first byte */ + uint32_t tmp32; + uint8_t tmp8; + memcpy(&tmp32, output_data + read_size, + read_entry_size); + tmp8 = (uint8_t)tmp32; + memcpy(current_tbl + (idx * 1), &tmp8, 1); + } else { + memcpy(current_tbl + (idx * write_entry_size), + output_data + read_size, + write_entry_size); + } + } + current_tbl += num_write_entries * write_entry_size; + + /* Free input and output frame data */ + kfree(output_data); + kfree(input_data); + kfree(sg_table); + } + return 0; +} + +/* don't need to write zero to PME sram since POR is all zero */ +static int is_all_zero(uint8_t *buf, int size) +{ + int i; + for (i = 0; i < size; i++) { + if (buf[i] != 0) + return 0; + } + return 1; +} + +static int restore_all_tables(struct portal_backup_info *save_db, + uint32_t pme_rev1) +{ + struct pmtcc_raw_db *db = &save_db->db; + enum pme_pmtcc_table_id tbl_id; + int i, ret; + uint8_t *current_tbl = db->alldb; + + for (i = 0; i < ARRAY_SIZE(table_list); i++) { + int num_read_entries, read_size, read_reply_size, write_size, + read_entry_size, write_entry_size, num_write_entries; + int idx; + struct pme_pmtcc_write_request_msg_t *entry; + struct qm_fd fd; + uint8_t *input_data; + enum pme_status status; + + tbl_id = table_list[i]; + ret = get_table_attributes(tbl_id, pme_rev1, &num_read_entries, + &num_write_entries, &read_size, &read_reply_size, + &write_size, &read_entry_size, &write_entry_size); + + /* Allocate input frame data */ + input_data = kzalloc(write_size, GFP_KERNEL); + + entry = (struct pme_pmtcc_write_request_msg_t *) + input_data; + entry->header.protocol_version = pme_rev1; + entry->header.msg_type = 0x01; /* write */ + entry->header.msg_length = write_size; + entry->table_id = tbl_id; + + /* build fd */ + memset(&fd, 0, sizeof(fd)); + qm_fd_addr_set64(&fd, pme_suspend_map(save_db->pdev, + input_data)); + fd.format = qm_fd_contig_big; + fd.length29 = write_size; +#ifdef PME_SUSPEND_DEBUG + pr_info("Doing table %d\n", tbl_id); +#endif + for (idx = 0; idx < num_write_entries; idx++) { + if (is_all_zero(current_tbl + (idx * write_entry_size), + write_entry_size)) { + continue; + } + entry->indexed_entry.index = idx; + + memcpy(input_data + (write_size - write_entry_size), + current_tbl + (idx * write_entry_size), + write_entry_size); + + ret = pme_pwrmgmt_ctx_pmtcc(save_db->ctx, + QMAN_ENQUEUE_FLAG_WAIT, &fd); + + if (ret) + pr_err("error with pmtcc\n"); + + wait_for_completion(&save_db->ctx->done); + + status = pme_fd_res_status(&save_db->ctx->result_fd); + if (status) { + ret = -EINVAL; + pr_err("PMTCC write status fail %d\n", status); + } + if (pme_fd_res_flags(&save_db->ctx->result_fd) & + PME_STATUS_UNRELIABLE) { + pr_err("pme %x\n", pme_fd_res_flags( + &save_db->ctx->result_fd)); + ret = -EINVAL; + } + } + current_tbl += num_write_entries * write_entry_size; + + /* Free input and output frame data */ + kfree(input_data); + } + return 0; +} + +int fsl_pme_save_db(struct pme2_private_data *priv_data) +{ + int ret; + struct portal_backup_info *save_db = &priv_data->save_db; + +#ifdef PME_SUSPEND_DEBUG + print_debug(priv_data->pme_rev1); +#endif + ret = save_all_tables(save_db, priv_data->pme_rev1); + + return ret; +} + +static int is_pme_active(void) +{ + uint32_t val; + int ret; + + ret = pme_attr_get(pme_attr_pmstat, &val); + if (ret) { + pr_err("Error reading activity bit\n"); + return ret; + } + return val; +} + +/** + * pme_suspend - power management suspend function + * + * @priv_data: pme2 device private data + * + * Saves the pme device volatile state prior to suspension. + * CCSR space and SRAM state is saved to DDR + */ +int pme_suspend(struct pme2_private_data *priv_data) +{ + int ret; + struct ccsr_backup_info *ccsr_info; + struct portal_backup_info *db_info; + + ccsr_info = &priv_data->save_ccsr; + db_info = &priv_data->save_db; + + pme_attr_get(pme_attr_faconf_en, &ccsr_info->save_faconf_en); + pme_attr_get(pme_attr_cdcr, &ccsr_info->save_cdcr); + + /* disable pme */ + pme_attr_set(pme_attr_faconf_en, 0); + /* disable caching, only SRE will be flushed. FC caching already off */ + pme_attr_set(pme_attr_cdcr, 0xffffffff); + + /* wait until device is not active */ + while (is_pme_active()) { + cpu_relax(); + /* TODO: sanity check */ + } +#ifdef PME_SUSPEND_DEBUG + pr_info("PME is quiescent\n"); +#endif + + /* save CCSR space */ + save_all_ccsr(ccsr_info, priv_data->regs); + +#ifdef PME_SUSPEND_DEBUG + pr_info("First reg read is %u\n", + ccsr_info->regdb.pmfa.faconf); + pr_info("Last reg read is %u\n", + ccsr_info->regdb.gen.pm_ip_rev_2); +#endif + + /* save sram, must first configure the new exclusive fq before + * enabling pme */ + ret = pme2_exclusive_set(&db_info->ctx->rx_fq); + if (ret) + pr_err("Error getting exclusive mode\n"); + + /* save sram database, hook into pme_suspend. enable pme first */ + pme_attr_set(pme_attr_faconf_en, 1); + ret = fsl_pme_save_db(priv_data); + /* disable pme */ + pme_attr_set(pme_attr_faconf_en, 0); + + /* wait until device is not active */ + while (is_pme_active()) { + cpu_relax(); + /* TODO: sanity check */ + } +#ifdef PME_SUSPEND_DEBUG + pr_info("PME is quiescent\n"); +#endif + +#ifdef PME_SUSPEND_DEBUG + /* set the PME reset bit */ + pme_attr_set(pme_attr_faconf_rst, 1); + /* clear the PME reset bit */ + pme_attr_set(pme_attr_faconf_rst, 0); +#endif + + return 0; +} + +/** + * pme_resume - power management resume function + * + * @priv_data: pme2 device private data + * + * Restores the pme device to its original state prior to suspension. + * CCSR space and SRAM state is restored + */ +int pme_resume(struct pme2_private_data *priv_data) +{ + int ret; + struct ccsr_backup_info *ccsr_info; + struct portal_backup_info *db_info; + + ccsr_info = &priv_data->save_ccsr; + db_info = &priv_data->save_db; + +#ifdef PME_SUSPEND_DEBUG + pr_info("fsl_pme_restore_db\n"); + print_debug(priv_data->pme_rev1); +#endif + + /* when PME was saved, it was disabled. Therefore it will remain */ + restore_all_ccsr(ccsr_info, priv_data->regs); + /* restore caching state */ + pme_attr_set(pme_attr_cdcr, ccsr_info->save_cdcr); + + /* set private exclusive mode before enabling pme */ + /* save sram, must first configure the new exclusive fq before + * enabling pme */ + ret = pme2_exclusive_set(&db_info->ctx->rx_fq); + if (ret) + pr_err("Error getting exclusive mode\n"); + + /* save sram database, hook into pme_suspend. enable pme first */ + pme_attr_set(pme_attr_faconf_en, 1); + + ret = restore_all_tables(db_info, priv_data->pme_rev1); + + /* disable pme */ + pme_attr_set(pme_attr_faconf_en, 0); + /* wait until device is not active */ + while (is_pme_active()) { + cpu_relax(); + /* TODO: sanity check */ + } + + /* restore EFQC register */ + pme_attr_set(pme_attr_efqc, ccsr_info->regdb.pmfa.efqc); + + /* restore pme enable state */ + pme_attr_set(pme_attr_faconf_en, ccsr_info->save_faconf_en); + + free_databases(db_info); + + return 0; +} + + +/** + * init_pme_suspend - initialize pme resources for power management + * + * @priv_data: pme2 device private data + * + * All resources required to suspend the PME device are allocated. + * They include memory,frame queues, platform device + */ +int init_pme_suspend(struct pme2_private_data *priv_data) +{ + int ret; + struct ccsr_backup_info *ccsr_info; + struct portal_backup_info *db_info; + + ccsr_info = &priv_data->save_ccsr; + db_info = &priv_data->save_db; + + db_info->pdev = platform_device_alloc("fsl_pme_suspend", -1); + if (!db_info->pdev) + goto failed_alloc_device; + if (dma_set_mask(&db_info->pdev->dev, DMA_BIT_MASK(40))) + goto failed_dma_mask; + if (platform_device_add(db_info->pdev)) + goto failed_device_add; + + /* allocate frame queues */ + ret = create_pwrmgmt_ctx(db_info); + if (ret) + goto failed_create_pwrmgmt_ctx; + + ret = alloc_databases(priv_data); + if (ret) + goto failed_alloc_databases; + + return 0; + +failed_alloc_databases: + delete_pwrmgmt_ctx(db_info); +failed_create_pwrmgmt_ctx: + platform_device_del(db_info->pdev); +failed_device_add: +failed_dma_mask: + platform_device_put(db_info->pdev); + db_info->pdev = NULL; +failed_alloc_device: + return -ENOMEM; +} + +/** + * exit_pme_suspend - release pme resources for power management + * + * @priv_data: pme2 device private data + * + * All resources required to suspend the PME device are released. + * They include memory,frame queues, platform device + */ +void exit_pme_suspend(struct pme2_private_data *priv_data) +{ + struct portal_backup_info *db_info; + + db_info = &priv_data->save_db; + + free_databases(db_info); + delete_pwrmgmt_ctx(db_info); + platform_device_del(db_info->pdev); + platform_device_put(db_info->pdev); + db_info->pdev = NULL; +} + +#endif /* CONFIG_PM */ + |