text: /* Copyright 2008-2011 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.
   */
  
  #include "pme2_private.h"
  
  MODULE_AUTHOR("Geoff Thorpe");
  MODULE_LICENSE("Dual BSD/GPL");
  MODULE_DESCRIPTION("FSL PME2 (p4080) device usage");
  
  #define PME_RESIDUE_SIZE	128
  #define PME_RESIDUE_ALIGN	64
  #define PME_FLOW_SIZE		sizeof(struct pme_flow)
  #define PME_FLOW_ALIGN		32
  static struct kmem_cache *slab_residue;
  static struct kmem_cache *slab_flow;
  static struct kmem_cache *slab_fq;
  
  /* Hack to support "pme_map()". The point of this is that dma_map_single() now
   * requires a non-NULL device, so the idea is that address mapping must be
   * device-sensitive. Now the PAMU IO-MMU already takes care of this, as can be
   * seen by the device-tree structure generated by the hypervisor (each portal
   * node has sub-nodes for each h/w end-point it provides access to, and each
   * sub-node has its own LIODN configuration). So we just need to map cpu
   * pointers to (guest-)physical address and the PAMU takes care of the rest, so
   * this doesn't need to be portal-sensitive nor device-sensitive. */
  static struct platform_device *pdev;
  
  static int pme2_low_init(void)
  {
  	int ret = -ENOMEM;
  
  	slab_residue = kmem_cache_create("pme2_residue", PME_RESIDUE_SIZE,
  			PME_RESIDUE_ALIGN, SLAB_HWCACHE_ALIGN, NULL);
  	if (!slab_residue)
  		goto end;
  	slab_flow = kmem_cache_create("pme2_flow", PME_FLOW_SIZE,
  				PME_FLOW_ALIGN, 0, NULL);
  	if (!slab_flow)
  		goto end;
  	slab_fq = kmem_cache_create("pme2_fqslab", sizeof(struct qman_fq),
  			__alignof__(struct qman_fq), SLAB_HWCACHE_ALIGN, NULL);
  	if (!slab_fq)
  		goto end;
  	ret = -ENODEV;
  	pdev = platform_device_alloc("pme", -1);
  	if (!pdev)
  		goto end;
  	if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(40)))
  		goto end;
  	if (platform_device_add(pdev))
  		goto end;
  	return 0;
  end:
  	if (pdev) {
  		platform_device_put(pdev);
  		pdev = NULL;
  	}
  	if (slab_flow) {
  		kmem_cache_destroy(slab_flow);
  		slab_flow = NULL;
  	}
  	if (slab_residue) {
  		kmem_cache_destroy(slab_residue);
  		slab_residue = NULL;
  	}
  	if (slab_fq) {
  		kmem_cache_destroy(slab_fq);
  		slab_fq = NULL;
  	}
  	return ret;
  }
  
  static void pme2_low_exit(void)
  {
  	platform_device_del(pdev);
  	platform_device_put(pdev);
  	pdev = NULL;
  	kmem_cache_destroy(slab_fq);
  	kmem_cache_destroy(slab_flow);
  	kmem_cache_destroy(slab_residue);
  	slab_fq = slab_flow = slab_residue = NULL;
  }
  
  module_init(pme2_low_init);
  module_exit(pme2_low_exit);
  
  struct qman_fq *slabfq_alloc(void)
  {
  	return kmem_cache_alloc(slab_fq, GFP_KERNEL);
  }
  
  void slabfq_free(struct qman_fq *fq)
  {
  	kmem_cache_free(slab_fq, fq);
  }
  
  /***********************/
  /* low-level functions */
  /***********************/
  
  struct pme_hw_residue *pme_hw_residue_new(void)
  {
  	return kmem_cache_alloc(slab_residue, GFP_KERNEL);
  }
  EXPORT_SYMBOL(pme_hw_residue_new);
  
  void pme_hw_residue_free(struct pme_hw_residue *p)
  {
  	kmem_cache_free(slab_residue, p);
  }
  EXPORT_SYMBOL(pme_hw_residue_free);
  
  struct pme_hw_flow *pme_hw_flow_new(void)
  {
  	struct pme_flow *flow = kmem_cache_zalloc(slab_flow, GFP_KERNEL);
  	return (struct pme_hw_flow *)flow;
  }
  EXPORT_SYMBOL(pme_hw_flow_new);
  
  void pme_hw_flow_free(struct pme_hw_flow *p)
  {
  	kmem_cache_free(slab_flow, p);
  }
  EXPORT_SYMBOL(pme_hw_flow_free);
  
  static const struct pme_flow default_sw_flow = {
  	.sos = 1,
  	.srvm = 0,
  	.esee = 1,
  	.ren = 0,
  	.rlen = 0,
  	.seqnum_hi = 0,
  	.seqnum_lo = 0,
  	.sessionid = 0x7ffffff,
  	.rptr_hi = 0,
  	.rptr_lo = 0,
  	.clim = 0xffff,
  	.mlim = 0xffff
  };
  
  void pme_sw_flow_init(struct pme_flow *flow)
  {
  	memcpy(flow, &default_sw_flow, sizeof(*flow));
  }
  EXPORT_SYMBOL(pme_sw_flow_init);
  
  void pme_initfq(struct qm_mcc_initfq *initfq, struct pme_hw_flow *flow, u8 qos,
  		u8 rbpid, 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 = qos;
  	if (flow) {
  		dma_addr_t fcp = flow_map((struct pme_flow *)flow);
  		pme_a->mode = pme_mode_flow;
  		pme_context_a_set64(pme_a, fcp);
  	} else {
  		pme_a->mode = pme_mode_direct;
  		pme_context_a_set64(pme_a, 0);
  	}
  	pme_b->rbpid = rbpid;
  	pme_b->rfqid = rfqid;
  }
  EXPORT_SYMBOL(pme_initfq);
  
  void pme_fd_cmd_nop(struct qm_fd *fd)
  {
  	struct pme_cmd_nop *nop = (struct pme_cmd_nop *)&fd->cmd;
  	nop->cmd = pme_cmd_nop;
  }
  EXPORT_SYMBOL(pme_fd_cmd_nop);
  
  void pme_fd_cmd_fcw(struct qm_fd *fd, u8 flags, struct pme_flow *flow,
  		struct pme_hw_residue *residue)
  {
  	dma_addr_t f;
  	struct pme_cmd_flow_write *fcw = (struct pme_cmd_flow_write *)&fd->cmd;
  
  	BUG_ON(!flow);
  	BUG_ON((unsigned long)flow & 31);
  	fcw->cmd = pme_cmd_flow_write;
  	fcw->flags = flags;
  	if (flags & PME_CMD_FCW_RES) {
  		if (residue) {
  			dma_addr_t rptr = residue_map(residue);
  			BUG_ON(!residue);
  			BUG_ON((unsigned long)residue & 63);
  			pme_flow_rptr_set64(flow, rptr);
  		} else
  			pme_flow_rptr_set64(flow, 0);
  	}
  	f = flow_map(flow);
  	qm_fd_addr_set64(fd, f);
  	fd->format = qm_fd_contig;
  	fd->offset = 0;
  	fd->length20 = sizeof(*flow);
  }
  EXPORT_SYMBOL(pme_fd_cmd_fcw);
  
  void pme_fd_cmd_fcr(struct qm_fd *fd, struct pme_flow *flow)
  {
  	dma_addr_t f;
  	struct pme_cmd_flow_read *fcr = (struct pme_cmd_flow_read *)&fd->cmd;
  
  	BUG_ON(!flow);
  	BUG_ON((unsigned long)flow & 31);
  	fcr->cmd = pme_cmd_flow_read;
  	f = flow_map(flow);
  	qm_fd_addr_set64(fd, f);
  	fd->format = qm_fd_contig;
  	fd->offset = 0;
  	fd->length20 = sizeof(*flow);
  }
  EXPORT_SYMBOL(pme_fd_cmd_fcr);
  
  void pme_fd_cmd_pmtcc(struct qm_fd *fd)
  {
  	struct pme_cmd_pmtcc *pmtcc = (struct pme_cmd_pmtcc *)&fd->cmd;
  	pmtcc->cmd = pme_cmd_pmtcc;
  }
  EXPORT_SYMBOL(pme_fd_cmd_pmtcc);
  
  void pme_fd_cmd_scan(struct qm_fd *fd, u32 args)
  {
  	struct pme_cmd_scan *scan = (struct pme_cmd_scan *)&fd->cmd;
  	fd->cmd = args;
  	scan->cmd = pme_cmd_scan;
  }
  EXPORT_SYMBOL(pme_fd_cmd_scan);
  
  dma_addr_t pme_map(void *ptr)
  {
  	return dma_map_single(&pdev->dev, ptr, 1, DMA_BIDIRECTIONAL);
  }
  EXPORT_SYMBOL(pme_map);
  
  int pme_map_error(dma_addr_t dma_addr)
  {
  	return dma_mapping_error(&pdev->dev, dma_addr);
  }
  EXPORT_SYMBOL(pme_map_error);
  
  