Signed-off-by: Micha Nelissen <micha.nelis...@prodrive.nl> --- arch/powerpc/sysdev/fsl_rio.c | 175 ++++++++++++++++++++++++++++++++++++++++- drivers/rapidio/rio-scan.c | 3 +- drivers/rapidio/rio.c | 121 ++++++++++++++++++++++++++++- include/linux/rio.h | 20 ++++- include/linux/rio_drv.h | 5 +- 5 files changed, 315 insertions(+), 9 deletions(-)
diff --git a/arch/powerpc/sysdev/fsl_rio.c b/arch/powerpc/sysdev/fsl_rio.c index 757a83f..3290971 100644 --- a/arch/powerpc/sysdev/fsl_rio.c +++ b/arch/powerpc/sysdev/fsl_rio.c @@ -26,6 +26,8 @@ #include <asm/io.h> +#define DRVNAME "fsl_rio" + /* RapidIO definition irq, which read from OF-tree */ #define IRQ_RIO_BELL(m) (((struct rio_priv *)(m->priv))->bellirq) #define IRQ_RIO_TX(m) (((struct rio_priv *)(m->priv))->txirq) @@ -61,6 +63,49 @@ #define RIO_MIN_RX_RING_SIZE 2 #define RIO_MAX_RX_RING_SIZE 2048 +#define RIO_OUTB_ATMU_COUNT 9 +#define RIO_INB_ATMU_COUNT 4 + +#define ROWTAR_LTGTID_MASK 0xC0000000 +#define ROWTAR_TREXAD_TID_MASK 0x3FC00000 +#define ROWTAR_TRAD_MASK 0x003FFFFF +#define ROWTAR_LTGTID_SHIFT 30 +#define ROWTAR_TREXAD_TID_SHIFT 22 +#define ROWTAR_TRAD_HOP_SHIFT 12 +#define ROWTAR_LTGTID_VAL_SHIFT 8 +#define ROWTAR_LTGTID_VAL_MASK 3 +#define ROWTAR_TREXAD_TID_VAL_MASK 0xFF +#define ROWTAR_TRAD_VAL_SHIFT 12 +#define ROWTAR_TRAD_MAINT_SHIFT 22 +#define ROWTEAR_LTGTID_VAL_SHIFT 10 +#define ROWBAR_BADD_VAL_SHIFT 12 +#define ROWBAR_BADD_MASK 0x000FFFFF +#define ROWAR_TFLOWLVL_HIGHEST 0x08000000 +#define ROWAR_TFLOWLVL_HIGH 0x04000000 +#define ROWAR_NSEG_MASK 0x00C00000 +#define ROWAR_NSSEG_MASK 0x00300000 +#define ROWAR_SIZE_MASK 0x0000003F +#define ROWAR_NSEG_SHIFT 22 +#define ROWAR_NSSEG_SHIFT 20 +#define ROWAR_SIZE_SHIFT 0 +#define ROWAR_ENABLE 0x80000000 +#define ROWAR_PCI 0x02000000 +#define ROWAR_NSEG_FOUR 0x00800000 +#define ROWAR_NSSEG_EIGHT 0x00300000 +#define ROWAR_RDTYP_NREAD 0x00040000 +#define ROWAR_RDTYP_MAINT_READ 0x00070000 +#define ROWAR_WRTYP_DOORBELL 0x00002000 /* parallel PHY only */ +#define ROWAR_WRTYP_NWRITE 0x00004000 +#define ROWAR_WRTYP_MAINT_WRITE 0x00007000 +#define ROWAR_SIZE_4KB 0x0000000B +#define ROWAR_SIZE_4MB 0x00000015 +#define ROWAR_MAX_SEGS 4 /* must be power of 2 */ +#define ROWAR_MAX_SSEGS 8 /* idem */ +#define ROWAR_SIZE_MIN 4096 +#define ROWSAR_SGTGTDID_MASK 0x000000FF +#define ROWSAR_RDTYP_NREAD 0x00400000 +#define ROWSAR_WRTYP_NWRITE 0x00040000 + #define DOORBELL_DMR_DI 0x00000002 #define DOORBELL_DSR_TE 0x00000080 #define DOORBELL_DSR_QFI 0x00000010 @@ -80,7 +125,7 @@ struct rio_atmu_regs { u32 rowbar; u32 pad2; u32 rowar; - u32 pad3[3]; + u32 rowsar[3]; }; struct rio_msg_regs { @@ -171,6 +216,9 @@ struct rio_priv { struct rio_dbell_ring dbell_ring; struct rio_msg_tx_ring msg_tx_ring; struct rio_msg_rx_ring msg_rx_ring; + int mem_atmu_segs[RIO_OUTB_ATMU_COUNT]; + unsigned long law_start_free; + unsigned long law_end; int bellirq; int txirq; int rxirq; @@ -870,6 +918,129 @@ fsl_rio_dbell_handler(int irq, void *dev_instance) return IRQ_HANDLED; } +int rio_open_outb_mem(struct rio_dev *rdev, int region, struct resource *res) +{ + struct rio_dev_mem *rmem; + resource_size_t start, len; + unsigned int seg, num_seg, num_sseg, used_segs, win_size_log; + unsigned long trad_start, sseg_len, win_start, win_size; + struct rio_mport *port = rdev->net->hport; + struct rio_priv *priv = port->priv; + u32 rowar, rowtar, rowsar; + u16 dev_destid, destid; + int i; + + rmem = rio_find_dev_mem(rdev->vid, rdev->did, region); + if (!rmem) + return -ENODEV; + + start = rmem->start; + len = rmem->len; + dev_destid = rdev->destid; + for (i = 0; i < RIO_OUTB_ATMU_COUNT; i++) { + rowar = in_be32(&priv->atmu_regs[i].rowar); + if (!(rowar & ROWAR_ENABLE)) + continue; + + rowtar = in_be32(&priv->atmu_regs[i].rowtar); + num_seg = 1 << ((rowar & ROWAR_NSEG_MASK) >> ROWAR_NSEG_SHIFT); + num_sseg = 1 << ((rowar & ROWAR_NSSEG_MASK) >> ROWAR_NSSEG_SHIFT); + sseg_len = (1 << ((rowar & ROWAR_SIZE_MASK) + 1)) / (num_seg * num_sseg); + /* trad_start is to be 33..2 */ + trad_start = (rowtar & ROWTAR_TRAD_MASK) << (ROWTAR_TRAD_VAL_SHIFT - 2); + if (start < trad_start || start + (len >> 2) > trad_start + (sseg_len >> 2)) + continue; + + destid = (rowtar & ROWTAR_TREXAD_TID_MASK) >> ROWTAR_TREXAD_TID_SHIFT; + if (port->sys_size) + destid |= (((rowtar & ROWTAR_LTGTID_MASK) >> ROWTAR_LTGTID_SHIFT) + << ROWTAR_LTGTID_VAL_SHIFT) | + (in_be32(&priv->atmu_regs[i].rowtear) + << ROWTEAR_LTGTID_VAL_SHIFT); + + seg = 0; + if (destid <= dev_destid && dev_destid < destid + num_sseg) + goto atmu_found; + used_segs = priv->mem_atmu_segs[i]; + for (; seg < used_segs; seg++) { + rowsar = in_be32(&priv->atmu_regs[i].rowsar[seg]); + destid = rowsar & ROWSAR_SGTGTDID_MASK; + if (destid <= dev_destid && dev_destid < destid + num_sseg) { + /* seg = 0 <=> second segment in ATMU */ + seg++; + goto atmu_found; + } + } + if (seg < ROWAR_MAX_SEGS) { + /* use segment for this destid range */ + out_be32(&priv->atmu_regs[i].rowsar[seg], ROWSAR_RDTYP_NREAD | + ROWSAR_WRTYP_NWRITE | (dev_destid & + (ROWSAR_SGTGTDID_MASK & ~(ROWAR_MAX_SSEGS-1)))); + seg++; + /* seg = number of extra segments = segment number */ + priv->mem_atmu_segs[i] = seg; + goto atmu_found; + } + } + + /* try to find a new ATMU to configure */ + for (i = 0; i < RIO_OUTB_ATMU_COUNT; i++) + if (!(in_be32(&priv->atmu_regs[i].rowar) & ROWAR_ENABLE)) + break; + + if (i == RIO_OUTB_ATMU_COUNT) + return -EBUSY; + + sseg_len = rio_find_largest_dev_mem(start); + if (sseg_len < ROWAR_SIZE_MIN) + sseg_len = ROWAR_SIZE_MIN; + num_seg = ROWAR_MAX_SEGS; + num_sseg = ROWAR_MAX_SSEGS; + win_size = sseg_len * num_seg * num_sseg; + /* align win_size to next power of 2 */ + win_size_log = __ilog2(win_size) + ((win_size & (win_size - 1)) != 0); + win_size = 1 << win_size_log; + /* align base address to next multiple of win_size */ + win_start = (priv->law_start_free + win_size - 1) & ~(win_size - 1); + /* check if enough outbound memory */ + if (priv->law_end - win_start < win_size) + return -EBUSY; + + pr_debug(DRVNAME ": opening new window %d at 0x%08lx, size 0x%08lx (log %u)\n", + i, win_start, win_size, win_size_log); + rowtar = ((dev_destid & (ROWTAR_TREXAD_TID_VAL_MASK & ~(ROWAR_MAX_SSEGS-1))) + << ROWTAR_TREXAD_TID_SHIFT) | (start >> (ROWTAR_TRAD_VAL_SHIFT - 2)); + if (port->sys_size) { + rowtar |= (((dev_destid >> ROWTAR_LTGTID_VAL_SHIFT) + & ROWTAR_LTGTID_VAL_MASK) << ROWTAR_LTGTID_SHIFT); + out_be32(&priv->atmu_regs[i].rowtear, + dev_destid >> ROWTEAR_LTGTID_VAL_SHIFT); + } + out_be32(&priv->atmu_regs[i].rowtar, rowtar); + out_be32(&priv->atmu_regs[i].rowbar, win_start >> ROWBAR_BADD_VAL_SHIFT); + out_be32(&priv->atmu_regs[i].rowar, ROWAR_ENABLE | ROWAR_PCI | + ROWAR_NSEG_FOUR | ROWAR_NSSEG_EIGHT | ROWAR_RDTYP_NREAD | + ROWAR_WRTYP_NWRITE | (win_size_log - 1)); + priv->law_start_free = win_start + win_size; + res->start = win_start; + seg = 0; + goto res_start_found; +atmu_found: + res->start = start - trad_start + + ((priv->atmu_regs[i].rowbar & ROWBAR_BADD_MASK) << ROWBAR_BADD_VAL_SHIFT); +res_start_found: + res->start += (seg * num_sseg + (dev_destid & (ROWAR_MAX_SSEGS-1))) * sseg_len; + res->end = res->start + len - 1; + pr_debug(DRVNAME ": device destid %d mapped to 0x%08x-0x%08x (seg %d)\n", + dev_destid, res->start, res->end, seg); + return 0; +} + +void rio_close_outb_mem(struct rio_dev *rdev, int window) +{ + /* TODO, keep track of window usage */ +} + /** * fsl_rio_doorbell_init - MPC85xx doorbell interface init * @mport: Master port implementing the inbound doorbell unit @@ -1168,6 +1339,8 @@ int fsl_rio_setup(struct of_device *dev) out_be32(&priv->maint_atmu_regs->rowar, 0x80077015); /* 4M */ priv->maint_win = ioremap(law_start, RIO_MAINT_WIN_SIZE); + priv->law_start_free = law_start + RIO_MAINT_WIN_SIZE; + priv->law_end = law_start + law_size; /* Configure outbound doorbell window */ out_be32(&priv->dbell_atmu_regs->rowbar, diff --git a/drivers/rapidio/rio-scan.c b/drivers/rapidio/rio-scan.c index 4541509..3389fa5 100644 --- a/drivers/rapidio/rio-scan.c +++ b/drivers/rapidio/rio-scan.c @@ -387,8 +387,7 @@ static struct rio_dev __devinit *rio_setup_device(struct rio_net *net, if ((rdev->pef & RIO_PEF_INB_DOORBELL) && (rdev->dst_ops & RIO_DST_OPS_DOORBELL)) - rio_init_dbell_res(&rdev->riores[RIO_DOORBELL_RESOURCE], - 0, 0xffff); + rio_init_dbell_res(&rdev->doorbell_res, 0, 0xffff); ret = rio_add_device(rdev); if (ret) diff --git a/drivers/rapidio/rio.c b/drivers/rapidio/rio.c index 6395c78..68eade9 100644 --- a/drivers/rapidio/rio.c +++ b/drivers/rapidio/rio.c @@ -28,6 +28,7 @@ #include "rio.h" static LIST_HEAD(rio_mports); +static LIST_HEAD(rio_dev_mem_list); /** * rio_local_get_device_id - Get the base/extended device id for a port @@ -305,8 +306,7 @@ struct resource *rio_request_outb_dbell(struct rio_dev *rdev, u16 start, rio_init_dbell_res(res, start, end); /* Make sure these doorbells aren't in use */ - if (request_resource(&rdev->riores[RIO_DOORBELL_RESOURCE], res) - < 0) { + if (request_resource(&rdev->doorbell_res, res) < 0) { kfree(res); res = NULL; } @@ -388,6 +388,74 @@ rio_mport_get_feature(struct rio_mport * port, int local, u16 destid, return 0; } +int rio_claim_resource(u16 vid, u16 did, u32 start, u32 len) +{ + struct rio_dev_mem *rmem; + + rmem = rio_find_dev_mem_by_addr(vid, did, start); + if (rmem != NULL) { + rmem->len = len; + return 0; + } + + rmem = kzalloc(sizeof(*rmem), GFP_KERNEL); + if (rmem == NULL) + return -ENOMEM; + + rmem->vendor_id = vid; + rmem->device_id = did; + rmem->start = start; + rmem->len = len; + spin_lock(&rio_global_list_lock); + list_add_tail(&rmem->node, &rio_dev_mem_list); + spin_unlock(&rio_global_list_lock); + return 0; +} + +int rio_request_region(struct rio_dev *rdev, int region, char *res_name) +{ + struct resource *res; + int rc; + + if (region >= RIO_MAX_REGIONS) + return -EINVAL; + + if (rdev->mem_res[region] != NULL) + return -EBUSY; + + res = kzalloc (sizeof(struct resource), GFP_KERNEL); + if (res == NULL) + return -ENOMEM; + + rc = rio_open_outb_mem(rdev, region, res); + if (rc < 0) + goto err_res; + + res->name = res_name; + rc = request_resource(&rdev->net->hport->iores, res); + if (rc < 0) + goto err_res; + + rdev->mem_res[region] = res; + goto out; +err_res: + kfree(res); + res = NULL; +out: + return rc; +} + +void rio_release_region(struct rio_dev *rdev, int region) +{ + if (region >= RIO_MAX_REGIONS) + return; + + rio_close_outb_mem(rdev, region); + release_resource(rdev->mem_res[region]); + kfree(rdev->mem_res[region]); + rdev->mem_res[region] = NULL; +} + /** * rio_get_asm - Begin or continue searching for a RIO device by vid/did/asm_vid/asm_did * @vid: RIO vid to match or %RIO_ANY_ID to match all vids @@ -451,6 +519,52 @@ struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from) return rio_get_asm(vid, did, RIO_ANY_ID, RIO_ANY_ID, from); } +static struct rio_dev_mem *rio_get_dev_mem(u16 vid, u16 did, int region, u32 start) +{ + struct rio_dev_mem *rmem; + + spin_lock(&rio_global_list_lock); + list_for_each_entry(rmem, &rio_dev_mem_list, node) { + if (vid == rmem->vendor_id && did == rmem->device_id) { + if (region <= 0 && (start == RIO_ANY_ADDR || start == rmem->start)) + goto out; + else + region--; + } + } + + rmem = NULL; +out: + spin_unlock(&rio_global_list_lock); + return rmem; +} + +struct rio_dev_mem *rio_find_dev_mem(u16 vid, u16 did, int region) +{ + return rio_get_dev_mem(vid, did, region, RIO_ANY_ADDR); +} + +struct rio_dev_mem *rio_find_dev_mem_by_addr(u16 vid, u16 did, u32 start) +{ + return rio_get_dev_mem(vid, did, -1, start); +} + +u32 rio_find_largest_dev_mem(u32 start) +{ + struct rio_dev_mem *rmem; + u32 len; + + len = 0; + spin_lock(&rio_global_list_lock); + list_for_each_entry(rmem, &rio_dev_mem_list, node) { + if (rmem->start == start && rmem->len > len) + len = rmem->len; + } + spin_unlock(&rio_global_list_lock); + + return len; +} + static void rio_fixup_device(struct rio_dev *dev) { } @@ -509,3 +623,6 @@ EXPORT_SYMBOL_GPL(rio_request_inb_mbox); EXPORT_SYMBOL_GPL(rio_release_inb_mbox); EXPORT_SYMBOL_GPL(rio_request_outb_mbox); EXPORT_SYMBOL_GPL(rio_release_outb_mbox); +EXPORT_SYMBOL_GPL(rio_claim_resource); +EXPORT_SYMBOL_GPL(rio_request_region); +EXPORT_SYMBOL_GPL(rio_release_region); diff --git a/include/linux/rio.h b/include/linux/rio.h index dc0c755..343d714 100644 --- a/include/linux/rio.h +++ b/include/linux/rio.h @@ -25,7 +25,7 @@ #define RIO_INVALID_DESTID 0xffff #define RIO_MAX_MPORT_RESOURCES 16 -#define RIO_MAX_DEV_RESOURCES 16 +#define RIO_MAX_REGIONS 16 #define RIO_GLOBAL_TABLE 0xff /* Indicates access of a switch's global routing table if it @@ -36,6 +36,8 @@ entry is invalid (no route exists for the device ID) */ +#define RIO_ANY_ADDR 0xffffffff + #define RIO_MAX_ROUTE_ENTRIES(size) (size ? (1 << 16) : (1 << 8)) #define RIO_ANY_DESTID(size) (size ? 0xffff : 0xff) @@ -69,6 +71,14 @@ extern struct list_head rio_devices; /* list of all devices */ struct rio_mport; +struct rio_dev_mem { + struct list_head node; + unsigned short vendor_id; + unsigned short device_id; + u32 start; /* bits 33..2 */ + u32 len; +}; + /** * struct rio_dev - RIO device info * @global_list: Node in list of all RIO devices @@ -89,7 +99,8 @@ struct rio_mport; * @rswitch: Pointer to &struct rio_switch if valid for this device * @driver: Driver claiming this device * @dev: Device model device - * @riores: RIO resources this device owns + * @mem_res: RIO resources this device owns + * @doorbell_res: Outbound doorbell ranges in use by drivers/users * @destid: Network destination ID */ struct rio_dev { @@ -111,7 +122,8 @@ struct rio_dev { struct rio_switch *rswitch; /* RIO switch info */ struct rio_driver *driver; /* RIO driver claiming this device */ struct device dev; /* LDM device structure */ - struct resource riores[RIO_MAX_DEV_RESOURCES]; + struct resource *mem_res[RIO_MAX_REGIONS]; + struct resource doorbell_res; u16 destid; }; @@ -330,5 +342,7 @@ extern int rio_open_inb_mbox(struct rio_mport *, void *, int, int); extern void rio_close_inb_mbox(struct rio_mport *, int); extern int rio_open_outb_mbox(struct rio_mport *, void *, int, int); extern void rio_close_outb_mbox(struct rio_mport *, int); +extern int rio_open_outb_mem(struct rio_dev *, int, struct resource *res); +extern void rio_close_outb_mem(struct rio_dev *, int); #endif /* LINUX_RIO_H */ diff --git a/include/linux/rio_drv.h b/include/linux/rio_drv.h index c93a58a..cbcd68d 100644 --- a/include/linux/rio_drv.h +++ b/include/linux/rio_drv.h @@ -407,7 +407,7 @@ extern struct resource *rio_request_outb_dbell(struct rio_dev *, u16, u16); extern int rio_release_outb_dbell(struct rio_dev *, struct resource *); /* Memory region management */ -int rio_claim_resource(struct rio_dev *, int); +int rio_claim_resource(u16 vid, u16 did, u32 start, u32 len); int rio_request_regions(struct rio_dev *, char *); void rio_release_regions(struct rio_dev *); int rio_request_region(struct rio_dev *, int, char *); @@ -461,5 +461,8 @@ extern u16 rio_local_get_device_id(struct rio_mport *port); extern struct rio_dev *rio_get_device(u16 vid, u16 did, struct rio_dev *from); extern struct rio_dev *rio_get_asm(u16 vid, u16 did, u16 asm_vid, u16 asm_did, struct rio_dev *from); +extern struct rio_dev_mem *rio_find_dev_mem(u16 vid, u16 did, int region); +extern struct rio_dev_mem *rio_find_dev_mem_by_addr(u16 vid, u16 did, u32 start); +extern u32 rio_find_largest_dev_mem(u32 start); #endif /* LINUX_RIO_DRV_H */ -- 1.6.5.7 _______________________________________________ Linuxppc-dev mailing list Linuxppc-dev@lists.ozlabs.org https://lists.ozlabs.org/listinfo/linuxppc-dev