From d2b4397bf87cf6547ca9fa75b6b84eada96c0848 Mon Sep 17 00:00:00 2001 From: Kamalesh Babulal Date: Tue, 6 Jan 2009 05:57:24 +0000 Subject: powerpc: Fix iseries drivers build failure without CONFIG_VIOPATH iSeries dependent drivers fail to build, when CONFIG_VIOPATH is disabled. Fix the problem by making those drivers select it. Signed-off-by: Kamalesh Babulal Signed-off-by: Benjamin Herrenschmidt --- drivers/char/Kconfig | 1 + drivers/scsi/Kconfig | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 35914b6e1d2..f5be8081cd8 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -616,6 +616,7 @@ config HVC_ISERIES default y select HVC_DRIVER select HVC_IRQ + select VIOPATH help iSeries machines support a hypervisor virtual console. diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index b7322976d2b..256c7bec7bd 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -884,6 +884,7 @@ config SCSI_IBMVSCSI tristate "IBM Virtual SCSI support" depends on PPC_PSERIES || PPC_ISERIES select SCSI_SRP_ATTRS + select VIOPATH if PPC_ISERIES help This is the IBM POWER Virtual SCSI Client -- cgit v1.2.3 From cffb4add03b1fc83026b06dc3664279cfbf70155 Mon Sep 17 00:00:00 2001 From: Jim Paris Date: Tue, 6 Jan 2009 11:32:10 +0000 Subject: mtd/ps3vram: Add ps3vram driver for accessing video RAM as MTD Add ps3vram driver, which exposes unused video RAM on the PS3 as a MTD device suitable for storage or swap. Fast data transfer is achieved using a local cache in system RAM and DMA transfers via the GPU. Signed-off-by: Vivien Chappelier Signed-off-by: Jim Paris Acked-by: Geoff Levand Acked-by: David Woodhouse Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/Kconfig | 7 + drivers/mtd/devices/Makefile | 1 + drivers/mtd/devices/ps3vram.c | 776 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 784 insertions(+) create mode 100644 drivers/mtd/devices/ps3vram.c (limited to 'drivers') diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 6fde0a2e356..bc33200535f 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -120,6 +120,13 @@ config MTD_PHRAM doesn't have access to, memory beyond the mem=xxx limit, nvram, memory on the video card, etc... +config MTD_PS3VRAM + tristate "PS3 video RAM" + depends on FB_PS3 + help + This driver allows you to use excess PS3 video RAM as volatile + storage or system swap. + config MTD_LART tristate "28F160xx flash driver for LART" depends on SA1100_LART diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 0993d5cf392..e51521df4e4 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_MTD_LART) += lart.o obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o +obj-$(CONFIG_MTD_PS3VRAM) += ps3vram.o diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c new file mode 100644 index 00000000000..26a4b57662f --- /dev/null +++ b/drivers/mtd/devices/ps3vram.c @@ -0,0 +1,776 @@ +/** + * ps3vram - Use extra PS3 video ram as MTD block device. + * + * Copyright (c) 2007-2008 Jim Paris + * Added support RSX DMA Vivien Chappelier + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEVICE_NAME "ps3vram" + +#define XDR_BUF_SIZE (2 * 1024 * 1024) /* XDR buffer (must be 1MiB aligned) */ +#define XDR_IOIF 0x0c000000 + +#define FIFO_BASE XDR_IOIF +#define FIFO_SIZE (64 * 1024) + +#define DMA_PAGE_SIZE (4 * 1024) + +#define CACHE_PAGE_SIZE (256 * 1024) +#define CACHE_PAGE_COUNT ((XDR_BUF_SIZE - FIFO_SIZE) / CACHE_PAGE_SIZE) + +#define CACHE_OFFSET CACHE_PAGE_SIZE +#define FIFO_OFFSET 0 + +#define CTRL_PUT 0x10 +#define CTRL_GET 0x11 +#define CTRL_TOP 0x15 + +#define UPLOAD_SUBCH 1 +#define DOWNLOAD_SUBCH 2 + +#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c +#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104 + +#define L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT 0x601 + +struct mtd_info ps3vram_mtd; + +#define CACHE_PAGE_PRESENT 1 +#define CACHE_PAGE_DIRTY 2 + +#define dbg(fmt, args...) \ + pr_debug("%s:%d " fmt "\n", __func__, __LINE__, ## args) + +struct ps3vram_tag { + unsigned int address; + unsigned int flags; +}; + +struct ps3vram_cache { + unsigned int page_count; + unsigned int page_size; + struct ps3vram_tag *tags; +}; + +struct ps3vram_priv { + uint64_t memory_handle; + uint64_t context_handle; + uint8_t *base; + uint32_t *ctrl; + uint32_t *reports; + uint8_t *xdr_buf; + + uint32_t *fifo_base; + uint32_t *fifo_ptr; + + struct ps3vram_cache cache; + + /* Used to serialize cache/DMA operations */ + struct mutex lock; +}; + +#define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */ +#define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */ +#define DMA_NOTIFIER_SIZE 0x40 + +#define NUM_NOTIFIERS 16 + +#define NOTIFIER 7 /* notifier used for completion report */ + +/* A trailing '-' means to subtract off ps3fb_videomemory.size */ +char *size = "256M-"; +module_param(size, charp, 0); +MODULE_PARM_DESC(size, "memory size"); + +static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier) +{ + return (void *) reports + + DMA_NOTIFIER_OFFSET_BASE + + DMA_NOTIFIER_SIZE * notifier; +} + +static void ps3vram_notifier_reset(struct mtd_info *mtd) +{ + int i; + struct ps3vram_priv *priv = mtd->priv; + uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + for (i = 0; i < 4; i++) + notify[i] = 0xffffffff; +} + +static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) +{ + struct ps3vram_priv *priv = mtd->priv; + uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + + timeout_ms *= 1000; + + do { + if (notify[3] == 0) + return 0; + + if (timeout_ms) + udelay(1); + } while (timeout_ms--); + + return -1; +} + +static void ps3vram_dump_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + uint32_t *fifo; + + pr_info("PUT = %08x GET = %08x\n", priv->ctrl[CTRL_PUT], + priv->ctrl[CTRL_GET]); + for (fifo = priv->fifo_base; fifo < priv->fifo_ptr; fifo++) + pr_info("%p: %08x\n", fifo, *fifo); +} + +static void ps3vram_dump_reports(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + int i; + + for (i = 0; i < NUM_NOTIFIERS; i++) { + uint32_t *n = ps3vram_get_notifier(priv->reports, i); + pr_info("%p: %08x\n", n, *n); + } +} + +static void ps3vram_init_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; + priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; +} + +static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) +{ + struct ps3vram_priv *priv = mtd->priv; + + /* wait until setup commands are processed */ + timeout *= 1000; + while (--timeout) { + if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET]) + break; + udelay(1); + } + if (timeout == 0) { + pr_err("FIFO timeout (%08x/%08x/%08x)\n", priv->ctrl[CTRL_PUT], + priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); + return -ETIMEDOUT; + } + + return 0; +} + +static inline void ps3vram_out_ring(struct ps3vram_priv *priv, uint32_t data) +{ + *(priv->fifo_ptr)++ = data; +} + +static inline void ps3vram_begin_ring(struct ps3vram_priv *priv, uint32_t chan, + uint32_t tag, uint32_t size) +{ + ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag); +} + +static void ps3vram_rewind_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + u64 status; + + ps3vram_out_ring(priv, 0x20000000 | (FIFO_BASE + FIFO_OFFSET)); + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET; + + /* asking the HV for a blit will kick the fifo */ + status = lv1_gpu_context_attribute(priv->context_handle, + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, + 0, 0, 0, 0); + if (status) + pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + + priv->fifo_ptr = priv->fifo_base; +} + +static void ps3vram_fire_ring(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + u64 status; + + mutex_lock(&ps3_gpu_mutex); + + priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET + + (priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t); + + /* asking the HV for a blit will kick the fifo */ + status = lv1_gpu_context_attribute(priv->context_handle, + L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, + 0, 0, 0, 0); + if (status) + pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + + if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) > + FIFO_SIZE - 1024) { + dbg("fifo full, rewinding"); + ps3vram_wait_ring(mtd, 200); + ps3vram_rewind_ring(mtd); + } + + mutex_unlock(&ps3_gpu_mutex); +} + +static void ps3vram_bind(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0, 1); + ps3vram_out_ring(priv, 0x31337303); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x180, 3); + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ + + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0, 1); + ps3vram_out_ring(priv, 0x3137c0de); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x180, 3); + ps3vram_out_ring(priv, DMA_NOTIFIER_HANDLE_BASE + NOTIFIER); + ps3vram_out_ring(priv, 0xfeed0000); /* DMA video RAM instance */ + ps3vram_out_ring(priv, 0xfeed0001); /* DMA system RAM instance */ + + ps3vram_fire_ring(mtd); +} + +static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, + unsigned int dst_offset, int len, int count) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, UPLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + ps3vram_out_ring(priv, XDR_IOIF + src_offset); + ps3vram_out_ring(priv, dst_offset); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, count); + ps3vram_out_ring(priv, (1 << 8) | 1); + ps3vram_out_ring(priv, 0); + + ps3vram_notifier_reset(mtd); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); + ps3vram_out_ring(priv, 0); + ps3vram_begin_ring(priv, UPLOAD_SUBCH, 0x100, 1); + ps3vram_out_ring(priv, 0); + ps3vram_fire_ring(mtd); + if (ps3vram_notifier_wait(mtd, 200) < 0) { + pr_err("notifier timeout\n"); + ps3vram_dump_ring(mtd); + ps3vram_dump_reports(mtd); + return -1; + } + + return 0; +} + +static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, + unsigned int dst_offset, int len, int count) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8); + ps3vram_out_ring(priv, src_offset); + ps3vram_out_ring(priv, XDR_IOIF + dst_offset); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, len); + ps3vram_out_ring(priv, count); + ps3vram_out_ring(priv, (1 << 8) | 1); + ps3vram_out_ring(priv, 0); + + ps3vram_notifier_reset(mtd); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, + NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY, 1); + ps3vram_out_ring(priv, 0); + ps3vram_begin_ring(priv, DOWNLOAD_SUBCH, 0x100, 1); + ps3vram_out_ring(priv, 0); + ps3vram_fire_ring(mtd); + if (ps3vram_notifier_wait(mtd, 200) < 0) { + pr_err("notifier timeout\n"); + ps3vram_dump_ring(mtd); + ps3vram_dump_reports(mtd); + return -1; + } + + return 0; +} + +static void ps3vram_cache_evict(struct mtd_info *mtd, int entry) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + + if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) { + dbg("flushing %d : 0x%08x", entry, cache->tags[entry].address); + if (ps3vram_upload(mtd, + CACHE_OFFSET + entry * cache->page_size, + cache->tags[entry].address, + DMA_PAGE_SIZE, + cache->page_size / DMA_PAGE_SIZE) < 0) { + pr_err("failed to upload from 0x%x to 0x%x size 0x%x\n", + entry * cache->page_size, + cache->tags[entry].address, + cache->page_size); + } + cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY; + } +} + +static void ps3vram_cache_load(struct mtd_info *mtd, int entry, + unsigned int address) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + + dbg("fetching %d : 0x%08x", entry, address); + if (ps3vram_download(mtd, + address, + CACHE_OFFSET + entry * cache->page_size, + DMA_PAGE_SIZE, + cache->page_size / DMA_PAGE_SIZE) < 0) { + pr_err("failed to download from 0x%x to 0x%x size 0x%x\n", + address, + entry * cache->page_size, + cache->page_size); + } + + cache->tags[entry].address = address; + cache->tags[entry].flags |= CACHE_PAGE_PRESENT; +} + + +static void ps3vram_cache_flush(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + int i; + + dbg("FLUSH"); + for (i = 0; i < cache->page_count; i++) { + ps3vram_cache_evict(mtd, i); + cache->tags[i].flags = 0; + } +} + +static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address) +{ + struct ps3vram_priv *priv = mtd->priv; + struct ps3vram_cache *cache = &priv->cache; + unsigned int base; + unsigned int offset; + int i; + static int counter; + + offset = (unsigned int) (address & (cache->page_size - 1)); + base = (unsigned int) (address - offset); + + /* fully associative check */ + for (i = 0; i < cache->page_count; i++) { + if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) && + cache->tags[i].address == base) { + dbg("found entry %d : 0x%08x", + i, cache->tags[i].address); + return i; + } + } + + /* choose a random entry */ + i = (jiffies + (counter++)) % cache->page_count; + dbg("using cache entry %d", i); + + ps3vram_cache_evict(mtd, i); + ps3vram_cache_load(mtd, i, base); + + return i; +} + +static int ps3vram_cache_init(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + pr_info("creating cache: %d entries, %d bytes pages\n", + CACHE_PAGE_COUNT, CACHE_PAGE_SIZE); + + priv->cache.page_count = CACHE_PAGE_COUNT; + priv->cache.page_size = CACHE_PAGE_SIZE; + priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * + CACHE_PAGE_COUNT, GFP_KERNEL); + if (priv->cache.tags == NULL) { + pr_err("could not allocate cache tags\n"); + return -ENOMEM; + } + + return 0; +} + +static void ps3vram_cache_cleanup(struct mtd_info *mtd) +{ + struct ps3vram_priv *priv = mtd->priv; + + ps3vram_cache_flush(mtd); + kfree(priv->cache.tags); +} + +static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct ps3vram_priv *priv = mtd->priv; + + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + + mutex_lock(&priv->lock); + + ps3vram_cache_flush(mtd); + + /* Set bytes to 0xFF */ + memset(priv->base + instr->addr, 0xFF, instr->len); + + mutex_unlock(&priv->lock); + + instr->state = MTD_ERASE_DONE; + mtd_erase_callback(instr); + + return 0; +} + + +static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned int cached, count; + + dbg("from = 0x%08x len = 0x%zx", (unsigned int) from, len); + + if (from >= mtd->size) + return -EINVAL; + + if (len > mtd->size - from) + len = mtd->size - from; + + /* Copy from vram to buf */ + count = len; + while (count) { + unsigned int offset, avail; + unsigned int entry; + + offset = (unsigned int) (from & (priv->cache.page_size - 1)); + avail = priv->cache.page_size - offset; + + mutex_lock(&priv->lock); + + entry = ps3vram_cache_match(mtd, from); + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; + + dbg("from=%08x cached=%08x offset=%08x avail=%08x count=%08x", + (unsigned)from, cached, offset, avail, count); + + if (avail > count) + avail = count; + memcpy(buf, priv->xdr_buf + cached, avail); + + mutex_unlock(&priv->lock); + + buf += avail; + count -= avail; + from += avail; + } + + *retlen = len; + return 0; +} + +static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct ps3vram_priv *priv = mtd->priv; + unsigned int cached, count; + + if (to >= mtd->size) + return -EINVAL; + + if (len > mtd->size - to) + len = mtd->size - to; + + /* Copy from buf to vram */ + count = len; + while (count) { + unsigned int offset, avail; + unsigned int entry; + + offset = (unsigned int) (to & (priv->cache.page_size - 1)); + avail = priv->cache.page_size - offset; + + mutex_lock(&priv->lock); + + entry = ps3vram_cache_match(mtd, to); + cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; + + dbg("to=%08x cached=%08x offset=%08x avail=%08x count=%08x", + (unsigned) to, cached, offset, avail, count); + + if (avail > count) + avail = count; + memcpy(priv->xdr_buf + cached, buf, avail); + + priv->cache.tags[entry].flags |= CACHE_PAGE_DIRTY; + + mutex_unlock(&priv->lock); + + buf += avail; + count -= avail; + to += avail; + } + + *retlen = len; + return 0; +} + +static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) +{ + struct ps3vram_priv *priv; + uint64_t status; + uint64_t ddr_lpar, ctrl_lpar, info_lpar, reports_lpar; + int64_t ddr_size; + uint64_t reports_size; + int ret = -ENOMEM; + char *rest; + + ret = -EIO; + ps3vram_mtd.priv = kzalloc(sizeof(struct ps3vram_priv), GFP_KERNEL); + if (!ps3vram_mtd.priv) + goto out; + priv = ps3vram_mtd.priv; + + mutex_init(&priv->lock); + + /* Allocate XDR buffer (1MiB aligned) */ + priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL, + get_order(XDR_BUF_SIZE)); + if (priv->xdr_buf == NULL) { + pr_err("ps3vram: could not allocate XDR buffer\n"); + ret = -ENOMEM; + goto out_free_priv; + } + + /* Put FIFO at begginning of XDR buffer */ + priv->fifo_base = (uint32_t *) (priv->xdr_buf + FIFO_OFFSET); + priv->fifo_ptr = priv->fifo_base; + + /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ + if (ps3_open_hv_device(dev)) { + pr_err("ps3vram: ps3_open_hv_device failed\n"); + ret = -EAGAIN; + goto out_close_gpu; + } + + /* Request memory */ + status = -1; + ddr_size = memparse(size, &rest); + if (*rest == '-') + ddr_size -= ps3fb_videomemory.size; + ddr_size = ALIGN(ddr_size, 1024*1024); + if (ddr_size <= 0) { + printk(KERN_ERR "ps3vram: specified size is too small\n"); + ret = -EINVAL; + goto out_close_gpu; + } + + while (ddr_size > 0) { + status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, + &priv->memory_handle, + &ddr_lpar); + if (status == 0) + break; + ddr_size -= 1024*1024; + } + if (status != 0 || ddr_size <= 0) { + pr_err("ps3vram: lv1_gpu_memory_allocate failed\n"); + ret = -ENOMEM; + goto out_free_xdr_buf; + } + pr_info("ps3vram: allocated %u MiB of DDR memory\n", + (unsigned int) (ddr_size / 1024 / 1024)); + + /* Request context */ + status = lv1_gpu_context_allocate(priv->memory_handle, + 0, + &priv->context_handle, + &ctrl_lpar, + &info_lpar, + &reports_lpar, + &reports_size); + if (status) { + pr_err("ps3vram: lv1_gpu_context_allocate failed\n"); + ret = -ENOMEM; + goto out_free_memory; + } + + /* Map XDR buffer to RSX */ + status = lv1_gpu_context_iomap(priv->context_handle, XDR_IOIF, + ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), + XDR_BUF_SIZE, 0); + if (status) { + pr_err("ps3vram: lv1_gpu_context_iomap failed\n"); + ret = -ENOMEM; + goto out_free_context; + } + + priv->base = ioremap(ddr_lpar, ddr_size); + if (!priv->base) { + pr_err("ps3vram: ioremap failed\n"); + ret = -ENOMEM; + goto out_free_context; + } + + priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); + if (!priv->ctrl) { + pr_err("ps3vram: ioremap failed\n"); + ret = -ENOMEM; + goto out_unmap_vram; + } + + priv->reports = ioremap(reports_lpar, reports_size); + if (!priv->reports) { + pr_err("ps3vram: ioremap failed\n"); + ret = -ENOMEM; + goto out_unmap_ctrl; + } + + mutex_lock(&ps3_gpu_mutex); + ps3vram_init_ring(&ps3vram_mtd); + mutex_unlock(&ps3_gpu_mutex); + + ps3vram_mtd.name = "ps3vram"; + ps3vram_mtd.size = ddr_size; + ps3vram_mtd.flags = MTD_CAP_RAM; + ps3vram_mtd.erase = ps3vram_erase; + ps3vram_mtd.point = NULL; + ps3vram_mtd.unpoint = NULL; + ps3vram_mtd.read = ps3vram_read; + ps3vram_mtd.write = ps3vram_write; + ps3vram_mtd.owner = THIS_MODULE; + ps3vram_mtd.type = MTD_RAM; + ps3vram_mtd.erasesize = CACHE_PAGE_SIZE; + ps3vram_mtd.writesize = 1; + + ps3vram_bind(&ps3vram_mtd); + + mutex_lock(&ps3_gpu_mutex); + ret = ps3vram_wait_ring(&ps3vram_mtd, 100); + mutex_unlock(&ps3_gpu_mutex); + if (ret < 0) { + pr_err("failed to initialize channels\n"); + ret = -ETIMEDOUT; + goto out_unmap_reports; + } + + ps3vram_cache_init(&ps3vram_mtd); + + if (add_mtd_device(&ps3vram_mtd)) { + pr_err("ps3vram: failed to register device\n"); + ret = -EAGAIN; + goto out_cache_cleanup; + } + + pr_info("ps3vram mtd device registered, %lu bytes\n", ddr_size); + return 0; + +out_cache_cleanup: + ps3vram_cache_cleanup(&ps3vram_mtd); +out_unmap_reports: + iounmap(priv->reports); +out_unmap_ctrl: + iounmap(priv->ctrl); +out_unmap_vram: + iounmap(priv->base); +out_free_context: + lv1_gpu_context_free(priv->context_handle); +out_free_memory: + lv1_gpu_memory_free(priv->memory_handle); +out_close_gpu: + ps3_close_hv_device(dev); +out_free_xdr_buf: + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); +out_free_priv: + kfree(ps3vram_mtd.priv); + ps3vram_mtd.priv = NULL; +out: + return ret; +} + +static int ps3vram_shutdown(struct ps3_system_bus_device *dev) +{ + struct ps3vram_priv *priv; + + priv = ps3vram_mtd.priv; + + del_mtd_device(&ps3vram_mtd); + ps3vram_cache_cleanup(&ps3vram_mtd); + iounmap(priv->reports); + iounmap(priv->ctrl); + iounmap(priv->base); + lv1_gpu_context_free(priv->context_handle); + lv1_gpu_memory_free(priv->memory_handle); + ps3_close_hv_device(dev); + free_pages((unsigned long) priv->xdr_buf, get_order(XDR_BUF_SIZE)); + kfree(priv); + return 0; +} + +static struct ps3_system_bus_driver ps3vram_driver = { + .match_id = PS3_MATCH_ID_GPU, + .match_sub_id = PS3_MATCH_SUB_ID_GPU_RAMDISK, + .core.name = DEVICE_NAME, + .core.owner = THIS_MODULE, + .probe = ps3vram_probe, + .remove = ps3vram_shutdown, + .shutdown = ps3vram_shutdown, +}; + +static int __init ps3vram_init(void) +{ + return ps3_system_bus_driver_register(&ps3vram_driver); +} + +static void __exit ps3vram_exit(void) +{ + ps3_system_bus_driver_unregister(&ps3vram_driver); +} + +module_init(ps3vram_init); +module_exit(ps3vram_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jim Paris "); +MODULE_DESCRIPTION("MTD driver for PS3 video RAM"); -- cgit v1.2.3 From 0a2d15b928e0b1673d4ed5f48d95af211b6fcc06 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 6 Jan 2009 11:32:03 +0000 Subject: mtd/ps3vram: Add modalias support to the ps3vram driver Update ps3vram driver to use the new ps3 three id modalias support. Signed-off-by: Geert Uytterhoeven Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 26a4b57662f..22f381fa784 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -774,3 +774,4 @@ module_exit(ps3vram_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Jim Paris "); MODULE_DESCRIPTION("MTD driver for PS3 video RAM"); +MODULE_ALIAS(PS3_MODULE_ALIAS_GPU_RAMDISK); -- cgit v1.2.3 From e7dd54cf17714c22665ad74b720f40fb64b3c565 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 6 Jan 2009 11:32:15 +0000 Subject: mtd/ps3vram: Remove ps3vram debug routines Remove the ps3vram debug routines ps3vram_dump_ring() and ps3vram_dump_reports(). These routines are not needed. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 29 ----------------------------- 1 file changed, 29 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 22f381fa784..18ca5219a98 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -87,9 +87,6 @@ struct ps3vram_priv { #define DMA_NOTIFIER_HANDLE_BASE 0x66604200 /* first DMA notifier handle */ #define DMA_NOTIFIER_OFFSET_BASE 0x1000 /* first DMA notifier offset */ #define DMA_NOTIFIER_SIZE 0x40 - -#define NUM_NOTIFIERS 16 - #define NOTIFIER 7 /* notifier used for completion report */ /* A trailing '-' means to subtract off ps3fb_videomemory.size */ @@ -131,28 +128,6 @@ static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) return -1; } -static void ps3vram_dump_ring(struct mtd_info *mtd) -{ - struct ps3vram_priv *priv = mtd->priv; - uint32_t *fifo; - - pr_info("PUT = %08x GET = %08x\n", priv->ctrl[CTRL_PUT], - priv->ctrl[CTRL_GET]); - for (fifo = priv->fifo_base; fifo < priv->fifo_ptr; fifo++) - pr_info("%p: %08x\n", fifo, *fifo); -} - -static void ps3vram_dump_reports(struct mtd_info *mtd) -{ - struct ps3vram_priv *priv = mtd->priv; - int i; - - for (i = 0; i < NUM_NOTIFIERS; i++) { - uint32_t *n = ps3vram_get_notifier(priv->reports, i); - pr_info("%p: %08x\n", n, *n); - } -} - static void ps3vram_init_ring(struct mtd_info *mtd) { struct ps3vram_priv *priv = mtd->priv; @@ -284,8 +259,6 @@ static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { pr_err("notifier timeout\n"); - ps3vram_dump_ring(mtd); - ps3vram_dump_reports(mtd); return -1; } @@ -317,8 +290,6 @@ static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { pr_err("notifier timeout\n"); - ps3vram_dump_ring(mtd); - ps3vram_dump_reports(mtd); return -1; } -- cgit v1.2.3 From f259d74e030faab15b95fb4bb56d7f424773c278 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 6 Jan 2009 11:32:21 +0000 Subject: mtd/ps3vram: Cleanup ps3vram driver messages Cleanup the ps3vram driver messages. Add a new struct device pointer variable dev to struct ps3vram_priv and use dev_dbg(), pr_dbg(), etc. where appropriate. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 116 +++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 48 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 18ca5219a98..f5cc2908bdc 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -53,9 +53,6 @@ struct mtd_info ps3vram_mtd; #define CACHE_PAGE_PRESENT 1 #define CACHE_PAGE_DIRTY 2 -#define dbg(fmt, args...) \ - pr_debug("%s:%d " fmt "\n", __func__, __LINE__, ## args) - struct ps3vram_tag { unsigned int address; unsigned int flags; @@ -78,6 +75,7 @@ struct ps3vram_priv { uint32_t *fifo_base; uint32_t *fifo_ptr; + struct device *dev; struct ps3vram_cache cache; /* Used to serialize cache/DMA operations */ @@ -148,8 +146,9 @@ static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) udelay(1); } if (timeout == 0) { - pr_err("FIFO timeout (%08x/%08x/%08x)\n", priv->ctrl[CTRL_PUT], - priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); + dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", + __func__, __LINE__, priv->ctrl[CTRL_PUT], + priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); return -ETIMEDOUT; } @@ -181,7 +180,8 @@ static void ps3vram_rewind_ring(struct mtd_info *mtd) L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0, 0, 0, 0); if (status) - pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", + __func__, __LINE__); priv->fifo_ptr = priv->fifo_base; } @@ -201,11 +201,13 @@ static void ps3vram_fire_ring(struct mtd_info *mtd) L1GPU_CONTEXT_ATTRIBUTE_FB_BLIT, 0, 0, 0, 0); if (status) - pr_err("ps3vram: lv1_gpu_context_attribute FB_BLIT failed\n"); + dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", + __func__, __LINE__); if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) > FIFO_SIZE - 1024) { - dbg("fifo full, rewinding"); + dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__, + __LINE__); ps3vram_wait_ring(mtd, 200); ps3vram_rewind_ring(mtd); } @@ -258,7 +260,8 @@ static int ps3vram_upload(struct mtd_info *mtd, unsigned int src_offset, ps3vram_out_ring(priv, 0); ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { - pr_err("notifier timeout\n"); + dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, + __LINE__); return -1; } @@ -289,7 +292,8 @@ static int ps3vram_download(struct mtd_info *mtd, unsigned int src_offset, ps3vram_out_ring(priv, 0); ps3vram_fire_ring(mtd); if (ps3vram_notifier_wait(mtd, 200) < 0) { - pr_err("notifier timeout\n"); + dev_dbg(priv->dev, "%s:%d: notifier timeout\n", __func__, + __LINE__); return -1; } @@ -302,16 +306,17 @@ static void ps3vram_cache_evict(struct mtd_info *mtd, int entry) struct ps3vram_cache *cache = &priv->cache; if (cache->tags[entry].flags & CACHE_PAGE_DIRTY) { - dbg("flushing %d : 0x%08x", entry, cache->tags[entry].address); + dev_dbg(priv->dev, "%s:%d: flushing %d : 0x%08x\n", __func__, + __LINE__, entry, cache->tags[entry].address); if (ps3vram_upload(mtd, CACHE_OFFSET + entry * cache->page_size, cache->tags[entry].address, DMA_PAGE_SIZE, cache->page_size / DMA_PAGE_SIZE) < 0) { - pr_err("failed to upload from 0x%x to 0x%x size 0x%x\n", - entry * cache->page_size, - cache->tags[entry].address, - cache->page_size); + dev_dbg(priv->dev, "%s:%d: failed to upload from " + "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, + entry * cache->page_size, + cache->tags[entry].address, cache->page_size); } cache->tags[entry].flags &= ~CACHE_PAGE_DIRTY; } @@ -323,16 +328,16 @@ static void ps3vram_cache_load(struct mtd_info *mtd, int entry, struct ps3vram_priv *priv = mtd->priv; struct ps3vram_cache *cache = &priv->cache; - dbg("fetching %d : 0x%08x", entry, address); + dev_dbg(priv->dev, "%s:%d: fetching %d : 0x%08x\n", __func__, __LINE__, + entry, address); if (ps3vram_download(mtd, address, CACHE_OFFSET + entry * cache->page_size, DMA_PAGE_SIZE, cache->page_size / DMA_PAGE_SIZE) < 0) { - pr_err("failed to download from 0x%x to 0x%x size 0x%x\n", - address, - entry * cache->page_size, - cache->page_size); + dev_err(priv->dev, "%s:%d: failed to download from " + "0x%x to 0x%x size 0x%x\n", __func__, __LINE__, address, + entry * cache->page_size, cache->page_size); } cache->tags[entry].address = address; @@ -346,7 +351,7 @@ static void ps3vram_cache_flush(struct mtd_info *mtd) struct ps3vram_cache *cache = &priv->cache; int i; - dbg("FLUSH"); + dev_dbg(priv->dev, "%s:%d: FLUSH\n", __func__, __LINE__); for (i = 0; i < cache->page_count; i++) { ps3vram_cache_evict(mtd, i); cache->tags[i].flags = 0; @@ -369,15 +374,15 @@ static unsigned int ps3vram_cache_match(struct mtd_info *mtd, loff_t address) for (i = 0; i < cache->page_count; i++) { if ((cache->tags[i].flags & CACHE_PAGE_PRESENT) && cache->tags[i].address == base) { - dbg("found entry %d : 0x%08x", - i, cache->tags[i].address); + dev_dbg(priv->dev, "%s:%d: found entry %d : 0x%08x\n", + __func__, __LINE__, i, cache->tags[i].address); return i; } } /* choose a random entry */ i = (jiffies + (counter++)) % cache->page_count; - dbg("using cache entry %d", i); + dev_dbg(priv->dev, "%s:%d: using entry %d\n", __func__, __LINE__, i); ps3vram_cache_evict(mtd, i); ps3vram_cache_load(mtd, i, base); @@ -389,18 +394,19 @@ static int ps3vram_cache_init(struct mtd_info *mtd) { struct ps3vram_priv *priv = mtd->priv; - pr_info("creating cache: %d entries, %d bytes pages\n", - CACHE_PAGE_COUNT, CACHE_PAGE_SIZE); - priv->cache.page_count = CACHE_PAGE_COUNT; priv->cache.page_size = CACHE_PAGE_SIZE; priv->cache.tags = kzalloc(sizeof(struct ps3vram_tag) * CACHE_PAGE_COUNT, GFP_KERNEL); if (priv->cache.tags == NULL) { - pr_err("could not allocate cache tags\n"); + dev_err(priv->dev, "%s:%d: could not allocate cache tags\n", + __func__, __LINE__); return -ENOMEM; } + dev_info(priv->dev, "created ram cache: %d entries, %d KiB each\n", + CACHE_PAGE_COUNT, CACHE_PAGE_SIZE / 1024); + return 0; } @@ -434,14 +440,14 @@ static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) return 0; } - static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct ps3vram_priv *priv = mtd->priv; unsigned int cached, count; - dbg("from = 0x%08x len = 0x%zx", (unsigned int) from, len); + dev_dbg(priv->dev, "%s:%d: from=0x%08x len=0x%zx\n", __func__, __LINE__, + (unsigned int)from, len); if (from >= mtd->size) return -EINVAL; @@ -463,8 +469,9 @@ static int ps3vram_read(struct mtd_info *mtd, loff_t from, size_t len, entry = ps3vram_cache_match(mtd, from); cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; - dbg("from=%08x cached=%08x offset=%08x avail=%08x count=%08x", - (unsigned)from, cached, offset, avail, count); + dev_dbg(priv->dev, "%s:%d: from=%08x cached=%08x offset=%08x " + "avail=%08x count=%08x\n", __func__, __LINE__, + (unsigned int)from, cached, offset, avail, count); if (avail > count) avail = count; @@ -507,8 +514,9 @@ static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, entry = ps3vram_cache_match(mtd, to); cached = CACHE_OFFSET + entry * priv->cache.page_size + offset; - dbg("to=%08x cached=%08x offset=%08x avail=%08x count=%08x", - (unsigned) to, cached, offset, avail, count); + dev_dbg(priv->dev, "%s:%d: to=%08x cached=%08x offset=%08x " + "avail=%08x count=%08x\n", __func__, __LINE__, + (unsigned int)to, cached, offset, avail, count); if (avail > count) avail = count; @@ -544,12 +552,14 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) priv = ps3vram_mtd.priv; mutex_init(&priv->lock); + priv->dev = &dev->core; /* Allocate XDR buffer (1MiB aligned) */ priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL, get_order(XDR_BUF_SIZE)); if (priv->xdr_buf == NULL) { - pr_err("ps3vram: could not allocate XDR buffer\n"); + dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_priv; } @@ -560,7 +570,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ if (ps3_open_hv_device(dev)) { - pr_err("ps3vram: ps3_open_hv_device failed\n"); + dev_err(&dev->core, "%s:%d: ps3_open_hv_device failed\n", + __func__, __LINE__); ret = -EAGAIN; goto out_close_gpu; } @@ -572,7 +583,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ddr_size -= ps3fb_videomemory.size; ddr_size = ALIGN(ddr_size, 1024*1024); if (ddr_size <= 0) { - printk(KERN_ERR "ps3vram: specified size is too small\n"); + dev_err(&dev->core, "%s:%d: specified size is too small\n", + __func__, __LINE__); ret = -EINVAL; goto out_close_gpu; } @@ -586,12 +598,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ddr_size -= 1024*1024; } if (status != 0 || ddr_size <= 0) { - pr_err("ps3vram: lv1_gpu_memory_allocate failed\n"); + dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_xdr_buf; } - pr_info("ps3vram: allocated %u MiB of DDR memory\n", - (unsigned int) (ddr_size / 1024 / 1024)); /* Request context */ status = lv1_gpu_context_allocate(priv->memory_handle, @@ -602,7 +613,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) &reports_lpar, &reports_size); if (status) { - pr_err("ps3vram: lv1_gpu_context_allocate failed\n"); + dev_err(&dev->core, "%s:%d: lv1_gpu_context_allocate failed\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_memory; } @@ -612,28 +624,32 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ps3_mm_phys_to_lpar(__pa(priv->xdr_buf)), XDR_BUF_SIZE, 0); if (status) { - pr_err("ps3vram: lv1_gpu_context_iomap failed\n"); + dev_err(&dev->core, "%s:%d: lv1_gpu_context_iomap failed\n", + __func__, __LINE__); ret = -ENOMEM; goto out_free_context; } priv->base = ioremap(ddr_lpar, ddr_size); if (!priv->base) { - pr_err("ps3vram: ioremap failed\n"); + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); ret = -ENOMEM; goto out_free_context; } priv->ctrl = ioremap(ctrl_lpar, 64 * 1024); if (!priv->ctrl) { - pr_err("ps3vram: ioremap failed\n"); + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); ret = -ENOMEM; goto out_unmap_vram; } priv->reports = ioremap(reports_lpar, reports_size); if (!priv->reports) { - pr_err("ps3vram: ioremap failed\n"); + dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, + __LINE__); ret = -ENOMEM; goto out_unmap_ctrl; } @@ -661,7 +677,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ret = ps3vram_wait_ring(&ps3vram_mtd, 100); mutex_unlock(&ps3_gpu_mutex); if (ret < 0) { - pr_err("failed to initialize channels\n"); + dev_err(&dev->core, "%s:%d: failed to initialize channels\n", + __func__, __LINE__); ret = -ETIMEDOUT; goto out_unmap_reports; } @@ -669,12 +686,15 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) ps3vram_cache_init(&ps3vram_mtd); if (add_mtd_device(&ps3vram_mtd)) { - pr_err("ps3vram: failed to register device\n"); + dev_err(&dev->core, "%s:%d: add_mtd_device failed\n", + __func__, __LINE__); ret = -EAGAIN; goto out_cache_cleanup; } - pr_info("ps3vram mtd device registered, %lu bytes\n", ddr_size); + dev_info(&dev->core, "reserved %u MiB of gpu memory\n", + (unsigned int)(ddr_size / 1024 / 1024)); + return 0; out_cache_cleanup: -- cgit v1.2.3 From 993e62e674ba670341f11f60398446bb37a88e8b Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Tue, 6 Jan 2009 11:32:28 +0000 Subject: mtd/ps3vram: Use proper kernel types Replace the use of stdint.h types with kernel types in the ps3vram driver. Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 56 +++++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index f5cc2908bdc..91cc2afbe14 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -65,15 +65,15 @@ struct ps3vram_cache { }; struct ps3vram_priv { - uint64_t memory_handle; - uint64_t context_handle; - uint8_t *base; - uint32_t *ctrl; - uint32_t *reports; - uint8_t *xdr_buf; + u64 memory_handle; + u64 context_handle; + u32 *ctrl; + u32 *reports; + u8 *base; + u8 *xdr_buf; - uint32_t *fifo_base; - uint32_t *fifo_ptr; + u32 *fifo_base; + u32 *fifo_ptr; struct device *dev; struct ps3vram_cache cache; @@ -92,7 +92,7 @@ char *size = "256M-"; module_param(size, charp, 0); MODULE_PARM_DESC(size, "memory size"); -static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier) +static u32 *ps3vram_get_notifier(u32 *reports, int notifier) { return (void *) reports + DMA_NOTIFIER_OFFSET_BASE + @@ -102,8 +102,9 @@ static inline uint32_t *ps3vram_get_notifier(uint32_t *reports, int notifier) static void ps3vram_notifier_reset(struct mtd_info *mtd) { int i; + struct ps3vram_priv *priv = mtd->priv; - uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); for (i = 0; i < 4; i++) notify[i] = 0xffffffff; } @@ -111,7 +112,7 @@ static void ps3vram_notifier_reset(struct mtd_info *mtd) static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) { struct ps3vram_priv *priv = mtd->priv; - uint32_t *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); + u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); timeout_ms *= 1000; @@ -155,13 +156,13 @@ static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) return 0; } -static inline void ps3vram_out_ring(struct ps3vram_priv *priv, uint32_t data) +static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data) { *(priv->fifo_ptr)++ = data; } -static inline void ps3vram_begin_ring(struct ps3vram_priv *priv, uint32_t chan, - uint32_t tag, uint32_t size) +static void ps3vram_begin_ring(struct ps3vram_priv *priv, u32 chan, + u32 tag, u32 size) { ps3vram_out_ring(priv, (size << 18) | (chan << 13) | tag); } @@ -194,7 +195,7 @@ static void ps3vram_fire_ring(struct mtd_info *mtd) mutex_lock(&ps3_gpu_mutex); priv->ctrl[CTRL_PUT] = FIFO_BASE + FIFO_OFFSET + - (priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t); + (priv->fifo_ptr - priv->fifo_base) * sizeof(u32); /* asking the HV for a blit will kick the fifo */ status = lv1_gpu_context_attribute(priv->context_handle, @@ -204,8 +205,8 @@ static void ps3vram_fire_ring(struct mtd_info *mtd) dev_err(priv->dev, "%s:%d: lv1_gpu_context_attribute failed\n", __func__, __LINE__); - if ((priv->fifo_ptr - priv->fifo_base) * sizeof(uint32_t) > - FIFO_SIZE - 1024) { + if ((priv->fifo_ptr - priv->fifo_base) * sizeof(u32) > + FIFO_SIZE - 1024) { dev_dbg(priv->dev, "%s:%d: fifo full, rewinding\n", __func__, __LINE__); ps3vram_wait_ring(mtd, 200); @@ -538,10 +539,13 @@ static int ps3vram_write(struct mtd_info *mtd, loff_t to, size_t len, static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) { struct ps3vram_priv *priv; - uint64_t status; - uint64_t ddr_lpar, ctrl_lpar, info_lpar, reports_lpar; - int64_t ddr_size; - uint64_t reports_size; + int status; + u64 ddr_lpar; + u64 ctrl_lpar; + u64 info_lpar; + u64 reports_lpar; + u64 ddr_size; + u64 reports_size; int ret = -ENOMEM; char *rest; @@ -555,8 +559,8 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) priv->dev = &dev->core; /* Allocate XDR buffer (1MiB aligned) */ - priv->xdr_buf = (uint8_t *) __get_free_pages(GFP_KERNEL, - get_order(XDR_BUF_SIZE)); + priv->xdr_buf = (void *)__get_free_pages(GFP_KERNEL, + get_order(XDR_BUF_SIZE)); if (priv->xdr_buf == NULL) { dev_dbg(&dev->core, "%s:%d: could not allocate XDR buffer\n", __func__, __LINE__); @@ -565,7 +569,7 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) } /* Put FIFO at begginning of XDR buffer */ - priv->fifo_base = (uint32_t *) (priv->xdr_buf + FIFO_OFFSET); + priv->fifo_base = (u32 *) (priv->xdr_buf + FIFO_OFFSET); priv->fifo_ptr = priv->fifo_base; /* XXX: Need to open GPU, in case ps3fb or snd_ps3 aren't loaded */ @@ -593,11 +597,11 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) status = lv1_gpu_memory_allocate(ddr_size, 0, 0, 0, 0, &priv->memory_handle, &ddr_lpar); - if (status == 0) + if (!status) break; ddr_size -= 1024*1024; } - if (status != 0 || ddr_size <= 0) { + if (status || ddr_size <= 0) { dev_err(&dev->core, "%s:%d: lv1_gpu_memory_allocate failed\n", __func__, __LINE__); ret = -ENOMEM; -- cgit v1.2.3 From 60c0c5987b0996a7c7c4c6d90f63ed413c368a71 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 7 Jan 2009 17:22:02 -0800 Subject: mtd/ps3vram: Use msleep in waits Replace the use of udelay() with msleep() in the looping wait routines ps3vram_notifier_wait() and ps3vram_wait_ring(). Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index 91cc2afbe14..d5924142d90 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -109,22 +109,19 @@ static void ps3vram_notifier_reset(struct mtd_info *mtd) notify[i] = 0xffffffff; } -static int ps3vram_notifier_wait(struct mtd_info *mtd, int timeout_ms) +static int ps3vram_notifier_wait(struct mtd_info *mtd, unsigned int timeout_ms) { struct ps3vram_priv *priv = mtd->priv; u32 *notify = ps3vram_get_notifier(priv->reports, NOTIFIER); - - timeout_ms *= 1000; + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); do { - if (notify[3] == 0) + if (!notify[3]) return 0; + msleep(1); + } while (time_before(jiffies, timeout)); - if (timeout_ms) - udelay(1); - } while (timeout_ms--); - - return -1; + return -ETIMEDOUT; } static void ps3vram_init_ring(struct mtd_info *mtd) @@ -135,25 +132,22 @@ static void ps3vram_init_ring(struct mtd_info *mtd) priv->ctrl[CTRL_GET] = FIFO_BASE + FIFO_OFFSET; } -static int ps3vram_wait_ring(struct mtd_info *mtd, int timeout) +static int ps3vram_wait_ring(struct mtd_info *mtd, unsigned int timeout_ms) { struct ps3vram_priv *priv = mtd->priv; + unsigned long timeout = jiffies + msecs_to_jiffies(timeout_ms); - /* wait until setup commands are processed */ - timeout *= 1000; - while (--timeout) { + do { if (priv->ctrl[CTRL_PUT] == priv->ctrl[CTRL_GET]) - break; - udelay(1); - } - if (timeout == 0) { - dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", - __func__, __LINE__, priv->ctrl[CTRL_PUT], - priv->ctrl[CTRL_GET], priv->ctrl[CTRL_TOP]); - return -ETIMEDOUT; - } + return 0; + msleep(1); + } while (time_before(jiffies, timeout)); - return 0; + dev_dbg(priv->dev, "%s:%d: FIFO timeout (%08x/%08x/%08x)\n", __func__, + __LINE__, priv->ctrl[CTRL_PUT], priv->ctrl[CTRL_GET], + priv->ctrl[CTRL_TOP]); + + return -ETIMEDOUT; } static void ps3vram_out_ring(struct ps3vram_priv *priv, u32 data) -- cgit v1.2.3 From 2efd72af0f18860927084df618f7419c82f69be3 Mon Sep 17 00:00:00 2001 From: Geoff Levand Date: Wed, 7 Jan 2009 17:22:07 -0800 Subject: mtd/ps3vram: Use _PAGE_NO_CACHE in memory ioremap Use _PAGE_NO_CACHE for gpu memory ioremap. Also, add __iomem attribute to gpu memory pointer and change use of memset() to memset_io(). Signed-off-by: Geoff Levand Signed-off-by: Benjamin Herrenschmidt --- drivers/mtd/devices/ps3vram.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/ps3vram.c b/drivers/mtd/devices/ps3vram.c index d5924142d90..d21e9beb7ed 100644 --- a/drivers/mtd/devices/ps3vram.c +++ b/drivers/mtd/devices/ps3vram.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -69,7 +70,7 @@ struct ps3vram_priv { u64 context_handle; u32 *ctrl; u32 *reports; - u8 *base; + u8 __iomem *ddr_base; u8 *xdr_buf; u32 *fifo_base; @@ -425,7 +426,7 @@ static int ps3vram_erase(struct mtd_info *mtd, struct erase_info *instr) ps3vram_cache_flush(mtd); /* Set bytes to 0xFF */ - memset(priv->base + instr->addr, 0xFF, instr->len); + memset_io(priv->ddr_base + instr->addr, 0xFF, instr->len); mutex_unlock(&priv->lock); @@ -628,8 +629,9 @@ static int __devinit ps3vram_probe(struct ps3_system_bus_device *dev) goto out_free_context; } - priv->base = ioremap(ddr_lpar, ddr_size); - if (!priv->base) { + priv->ddr_base = ioremap_flags(ddr_lpar, ddr_size, _PAGE_NO_CACHE); + + if (!priv->ddr_base) { dev_err(&dev->core, "%s:%d: ioremap failed\n", __func__, __LINE__); ret = -ENOMEM; @@ -702,7 +704,7 @@ out_unmap_reports: out_unmap_ctrl: iounmap(priv->ctrl); out_unmap_vram: - iounmap(priv->base); + iounmap(priv->ddr_base); out_free_context: lv1_gpu_context_free(priv->context_handle); out_free_memory: @@ -728,7 +730,7 @@ static int ps3vram_shutdown(struct ps3_system_bus_device *dev) ps3vram_cache_cleanup(&ps3vram_mtd); iounmap(priv->reports); iounmap(priv->ctrl); - iounmap(priv->base); + iounmap(priv->ddr_base); lv1_gpu_context_free(priv->context_handle); lv1_gpu_memory_free(priv->memory_handle); ps3_close_hv_device(dev); -- cgit v1.2.3 From 19b0bd025d6647549e07becf02b99e5168c17432 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Tue, 6 Jan 2009 14:01:23 +0000 Subject: powerpc/cell: Use correct types in beat files Only pass the address of a u64 if that is what the function requires. [Split out of a larger patch - sfr] [update comment - sfr] Signed-off-by: Ingo Molnar Signed-off-by: Stephen Rothwell Signed-off-by: Benjamin Herrenschmidt --- drivers/char/hvc_beat.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/char/hvc_beat.c b/drivers/char/hvc_beat.c index 91cdb35a920..0afc8b82212 100644 --- a/drivers/char/hvc_beat.c +++ b/drivers/char/hvc_beat.c @@ -44,7 +44,7 @@ static int hvc_beat_get_chars(uint32_t vtermno, char *buf, int cnt) static unsigned char q[sizeof(unsigned long) * 2] __attribute__((aligned(sizeof(unsigned long)))); static int qlen = 0; - unsigned long got; + u64 got; again: if (qlen) { @@ -63,7 +63,7 @@ again: } } if (beat_get_term_char(vtermno, &got, - ((unsigned long *)q), ((unsigned long *)q) + 1) == 0) { + ((u64 *)q), ((u64 *)q) + 1) == 0) { qlen = got; goto again; } -- cgit v1.2.3 From 5886188dc7ba9a76babcd37452f44079a9a77f71 Mon Sep 17 00:00:00 2001 From: Benjamin Krill Date: Wed, 7 Jan 2009 10:32:38 +0100 Subject: serial: Add driver for the Cell Network Processor serial port NWP device Add support for the nwp serial device which is connected to a DCR bus. It uses the of_serial device driver to determine necessary properties from the device tree. The supported device is added as serial port number 85. NWP stands for network processor and it is part of the QPACE - Quantum Chromodynamics Parallel Computing on the Cell Broadband Engine project. The implementation is a lightweight uart implementation with the focus to consume as little resources as possible and it is connected to a DCR bus. Signed-off-by: Benjamin Krill Signed-off-by: Arnd Bergmann Signed-off-by: Andrew Morton Signed-off-by: Benjamin Herrenschmidt --- drivers/serial/Kconfig | 19 +- drivers/serial/Makefile | 1 + drivers/serial/nwpserial.c | 475 +++++++++++++++++++++++++++++++++++++++++++++ drivers/serial/of_serial.c | 19 ++ 4 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 drivers/serial/nwpserial.c (limited to 'drivers') diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index b695ab3142d..64fcf8c2082 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1320,13 +1320,30 @@ config SERIAL_NETX_CONSOLE config SERIAL_OF_PLATFORM tristate "Serial port on Open Firmware platform bus" depends on PPC_OF - depends on SERIAL_8250 + depends on SERIAL_8250 || SERIAL_OF_PLATFORM_NWPSERIAL help If you have a PowerPC based system that has serial ports on a platform specific bus, you should enable this option. Currently, only 8250 compatible ports are supported, but others can easily be added. +config SERIAL_OF_PLATFORM_NWPSERIAL + tristate "NWP serial port driver" + depends on PPC_OF && PPC_DCR + select SERIAL_OF_PLATFORM + select SERIAL_CORE_CONSOLE + select SERIAL_CORE + help + This driver supports the cell network processor nwp serial + device. + +config SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE + bool "Console on NWP serial port" + depends on SERIAL_OF_PLATFORM_NWPSERIAL=y + select SERIAL_CORE_CONSOLE + help + Support for Console on the NWP serial ports. + config SERIAL_QE tristate "Freescale QUICC Engine serial port support" depends on QUICC_ENGINE diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index dfe775ac45b..8844c0a0392 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o +obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o obj-$(CONFIG_SERIAL_KS8695) += serial_ks8695.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_QE) += ucc_uart.o diff --git a/drivers/serial/nwpserial.c b/drivers/serial/nwpserial.c new file mode 100644 index 00000000000..32f3eaf0d26 --- /dev/null +++ b/drivers/serial/nwpserial.c @@ -0,0 +1,475 @@ +/* + * Serial Port driver for a NWP uart device + * + * Copyright (C) 2008 IBM Corp., Benjamin Krill + * + * 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 of the License, or (at your option) any later version. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define NWPSERIAL_NR 2 + +#define NWPSERIAL_STATUS_RXVALID 0x1 +#define NWPSERIAL_STATUS_TXFULL 0x2 + +struct nwpserial_port { + struct uart_port port; + dcr_host_t dcr_host; + unsigned int ier; + unsigned int mcr; +}; + +static DEFINE_MUTEX(nwpserial_mutex); +static struct nwpserial_port nwpserial_ports[NWPSERIAL_NR]; + +static void wait_for_bits(struct nwpserial_port *up, int bits) +{ + unsigned int status, tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + do { + status = dcr_read(up->dcr_host, UART_LSR); + + if (--tmout == 0) + break; + udelay(1); + } while ((status & bits) != bits); +} + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static void nwpserial_console_putchar(struct uart_port *port, int c) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void +nwpserial_console_write(struct console *co, const char *s, unsigned int count) +{ + struct nwpserial_port *up = &nwpserial_ports[co->index]; + unsigned long flags; + int locked = 1; + + if (oops_in_progress) + locked = spin_trylock_irqsave(&up->port.lock, flags); + else + spin_lock_irqsave(&up->port.lock, flags); + + /* save and disable interrupt */ + up->ier = dcr_read(up->dcr_host, UART_IER); + dcr_write(up->dcr_host, UART_IER, up->ier & ~UART_IER_RDI); + + uart_console_write(&up->port, s, count, nwpserial_console_putchar); + + /* wait for transmitter to become emtpy */ + while ((dcr_read(up->dcr_host, UART_LSR) & UART_LSR_THRE) == 0) + cpu_relax(); + + /* restore interrupt state */ + dcr_write(up->dcr_host, UART_IER, up->ier); + + if (locked) + spin_unlock_irqrestore(&up->port.lock, flags); +} + +static struct uart_driver nwpserial_reg; +static struct console nwpserial_console = { + .name = "ttySQ", + .write = nwpserial_console_write, + .device = uart_console_device, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &nwpserial_reg, +}; +#define NWPSERIAL_CONSOLE (&nwpserial_console) +#else +#define NWPSERIAL_CONSOLE NULL +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ + +/**************************************************************************/ + +static int nwpserial_request_port(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_release_port(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_config_port(struct uart_port *port, int flags) +{ + port->type = PORT_NWPSERIAL; +} + +static irqreturn_t nwpserial_interrupt(int irq, void *dev_id) +{ + struct nwpserial_port *up = dev_id; + struct tty_struct *tty = up->port.info->port.tty; + irqreturn_t ret; + unsigned int iir; + unsigned char ch; + + spin_lock(&up->port.lock); + + /* check if the uart was the interrupt source. */ + iir = dcr_read(up->dcr_host, UART_IIR); + if (!iir) { + ret = IRQ_NONE; + goto out; + } + + do { + up->port.icount.rx++; + ch = dcr_read(up->dcr_host, UART_RX); + if (up->port.ignore_status_mask != NWPSERIAL_STATUS_RXVALID) + tty_insert_flip_char(tty, ch, TTY_NORMAL); + } while (dcr_read(up->dcr_host, UART_RX) & UART_LSR_DR); + + tty_flip_buffer_push(tty); + ret = IRQ_HANDLED; + +out: + spin_unlock(&up->port.lock); + return ret; +} + +static int nwpserial_startup(struct uart_port *port) +{ + struct nwpserial_port *up; + int err; + + up = container_of(port, struct nwpserial_port, port); + + /* disable flow control by default */ + up->mcr = dcr_read(up->dcr_host, UART_MCR) & ~UART_MCR_AFE; + dcr_write(up->dcr_host, UART_MCR, up->mcr); + + /* register interrupt handler */ + err = request_irq(up->port.irq, nwpserial_interrupt, + IRQF_SHARED, "nwpserial", up); + if (err) + return err; + + /* enable interrupts */ + up->ier = UART_IER_RDI; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* enable receiving */ + up->port.ignore_status_mask &= ~NWPSERIAL_STATUS_RXVALID; + + return 0; +} + +static void nwpserial_shutdown(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + /* disable receiving */ + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* disable interrupts from this port */ + up->ier = 0; + dcr_write(up->dcr_host, UART_IER, up->ier); + + /* free irq */ + free_irq(up->port.irq, port); +} + +static int nwpserial_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + return -EINVAL; +} + +static const char *nwpserial_type(struct uart_port *port) +{ + return port->type == PORT_NWPSERIAL ? "nwpserial" : NULL; +} + +static void nwpserial_set_termios(struct uart_port *port, + struct ktermios *termios, struct ktermios *old) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + + up->port.read_status_mask = NWPSERIAL_STATUS_RXVALID + | NWPSERIAL_STATUS_TXFULL; + + up->port.ignore_status_mask = 0; + /* ignore all characters if CREAD is not set */ + if ((termios->c_cflag & CREAD) == 0) + up->port.ignore_status_mask |= NWPSERIAL_STATUS_RXVALID; + + /* Copy back the old hardware settings */ + if (old) + tty_termios_copy_hw(termios, old); +} + +static void nwpserial_break_ctl(struct uart_port *port, int ctl) +{ + /* N/A */ +} + +static void nwpserial_enable_ms(struct uart_port *port) +{ + /* N/A */ +} + +static void nwpserial_stop_rx(struct uart_port *port) +{ + struct nwpserial_port *up; + up = container_of(port, struct nwpserial_port, port); + /* don't forward any more data (like !CREAD) */ + up->port.ignore_status_mask = NWPSERIAL_STATUS_RXVALID; +} + +static void nwpserial_putchar(struct nwpserial_port *up, unsigned char c) +{ + /* check if tx buffer is full */ + wait_for_bits(up, UART_LSR_THRE); + dcr_write(up->dcr_host, UART_TX, c); + up->port.icount.tx++; +} + +static void nwpserial_start_tx(struct uart_port *port) +{ + struct nwpserial_port *up; + struct circ_buf *xmit; + up = container_of(port, struct nwpserial_port, port); + xmit = &up->port.info->xmit; + + if (port->x_char) { + nwpserial_putchar(up, up->port.x_char); + port->x_char = 0; + } + + while (!(uart_circ_empty(xmit) || uart_tx_stopped(&up->port))) { + nwpserial_putchar(up, xmit->buf[xmit->tail]); + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE-1); + } +} + +static unsigned int nwpserial_get_mctrl(struct uart_port *port) +{ + return 0; +} + +static void nwpserial_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + /* N/A */ +} + +static void nwpserial_stop_tx(struct uart_port *port) +{ + /* N/A */ +} + +static unsigned int nwpserial_tx_empty(struct uart_port *port) +{ + struct nwpserial_port *up; + unsigned long flags; + int ret; + up = container_of(port, struct nwpserial_port, port); + + spin_lock_irqsave(&up->port.lock, flags); + ret = dcr_read(up->dcr_host, UART_LSR); + spin_unlock_irqrestore(&up->port.lock, flags); + + return ret & UART_LSR_TEMT ? TIOCSER_TEMT : 0; +} + +static struct uart_ops nwpserial_pops = { + .tx_empty = nwpserial_tx_empty, + .set_mctrl = nwpserial_set_mctrl, + .get_mctrl = nwpserial_get_mctrl, + .stop_tx = nwpserial_stop_tx, + .start_tx = nwpserial_start_tx, + .stop_rx = nwpserial_stop_rx, + .enable_ms = nwpserial_enable_ms, + .break_ctl = nwpserial_break_ctl, + .startup = nwpserial_startup, + .shutdown = nwpserial_shutdown, + .set_termios = nwpserial_set_termios, + .type = nwpserial_type, + .release_port = nwpserial_release_port, + .request_port = nwpserial_request_port, + .config_port = nwpserial_config_port, + .verify_port = nwpserial_verify_port, +}; + +static struct uart_driver nwpserial_reg = { + .owner = THIS_MODULE, + .driver_name = "nwpserial", + .dev_name = "ttySQ", + .major = TTY_MAJOR, + .minor = 68, + .nr = NWPSERIAL_NR, + .cons = NWPSERIAL_CONSOLE, +}; + +int nwpserial_register_port(struct uart_port *port) +{ + struct nwpserial_port *up = NULL; + int ret = -1; + int i; + static int first = 1; + int dcr_len; + int dcr_base; + struct device_node *dn; + + mutex_lock(&nwpserial_mutex); + + dn = to_of_device(port->dev)->node; + if (dn == NULL) + goto out; + + /* get dcr base. */ + dcr_base = dcr_resource_start(dn, 0); + + /* find matching entry */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.iobase == dcr_base) { + up = &nwpserial_ports[i]; + break; + } + + /* we didn't find a mtching entry, search for a free port */ + if (up == NULL) + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN && + nwpserial_ports[i].port.iobase == 0) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) { + ret = -EBUSY; + goto out; + } + + if (first) + uart_register_driver(&nwpserial_reg); + first = 0; + + up->port.membase = port->membase; + up->port.irq = port->irq; + up->port.uartclk = port->uartclk; + up->port.fifosize = port->fifosize; + up->port.regshift = port->regshift; + up->port.iotype = port->iotype; + up->port.flags = port->flags; + up->port.mapbase = port->mapbase; + up->port.private_data = port->private_data; + + if (port->dev) + up->port.dev = port->dev; + + if (up->port.iobase != dcr_base) { + up->port.ops = &nwpserial_pops; + up->port.fifosize = 16; + + spin_lock_init(&up->port.lock); + + up->port.iobase = dcr_base; + dcr_len = dcr_resource_len(dn, 0); + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk(KERN_ERR "Cannot map DCR resources for NWPSERIAL"); + goto out; + } + } + + ret = uart_add_one_port(&nwpserial_reg, &up->port); + if (ret == 0) + ret = up->port.line; + +out: + mutex_unlock(&nwpserial_mutex); + + return ret; +} +EXPORT_SYMBOL(nwpserial_register_port); + +void nwpserial_unregister_port(int line) +{ + struct nwpserial_port *up = &nwpserial_ports[line]; + mutex_lock(&nwpserial_mutex); + uart_remove_one_port(&nwpserial_reg, &up->port); + + up->port.type = PORT_UNKNOWN; + + mutex_unlock(&nwpserial_mutex); +} +EXPORT_SYMBOL(nwpserial_unregister_port); + +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE +static int __init nwpserial_console_init(void) +{ + struct nwpserial_port *up = NULL; + struct device_node *dn; + const char *name; + int dcr_base; + int dcr_len; + int i; + + /* search for a free port */ + for (i = 0; i < NWPSERIAL_NR; i++) + if (nwpserial_ports[i].port.type == PORT_UNKNOWN) { + up = &nwpserial_ports[i]; + break; + } + + if (up == NULL) + return -1; + + name = of_get_property(of_chosen, "linux,stdout-path", NULL); + if (name == NULL) + return -1; + + dn = of_find_node_by_path(name); + if (!dn) + return -1; + + spin_lock_init(&up->port.lock); + up->port.ops = &nwpserial_pops; + up->port.type = PORT_NWPSERIAL; + up->port.fifosize = 16; + + dcr_base = dcr_resource_start(dn, 0); + dcr_len = dcr_resource_len(dn, 0); + up->port.iobase = dcr_base; + + up->dcr_host = dcr_map(dn, dcr_base, dcr_len); + if (!DCR_MAP_OK(up->dcr_host)) { + printk("Cannot map DCR resources for SERIAL"); + return -1; + } + register_console(&nwpserial_console); + return 0; +} +console_initcall(nwpserial_console_init); +#endif /* CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL_CONSOLE */ diff --git a/drivers/serial/of_serial.c b/drivers/serial/of_serial.c index 8fa0ff561e9..a821e3a3d66 100644 --- a/drivers/serial/of_serial.c +++ b/drivers/serial/of_serial.c @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -99,9 +100,16 @@ static int __devinit of_platform_serial_probe(struct of_device *ofdev, goto out; switch (port_type) { +#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: ret = serial8250_register_port(&port); break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + ret = nwpserial_register_port(&port); + break; +#endif default: /* need to add code for these */ case PORT_UNKNOWN: @@ -129,9 +137,16 @@ static int of_platform_serial_remove(struct of_device *ofdev) { struct of_serial_info *info = ofdev->dev.driver_data; switch (info->type) { +#ifdef CONFIG_SERIAL_8250 case PORT_8250 ... PORT_MAX_8250: serial8250_unregister_port(info->line); break; +#endif +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + case PORT_NWPSERIAL: + nwpserial_unregister_port(info->line); + break; +#endif default: /* need to add code for these */ break; @@ -148,6 +163,10 @@ static struct of_device_id __devinitdata of_platform_serial_table[] = { { .type = "serial", .compatible = "ns16450", .data = (void *)PORT_16450, }, { .type = "serial", .compatible = "ns16550", .data = (void *)PORT_16550, }, { .type = "serial", .compatible = "ns16750", .data = (void *)PORT_16750, }, +#ifdef CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL + { .type = "serial", .compatible = "ibm,qpace-nwp-serial", + .data = (void *)PORT_NWPSERIAL, }, +#endif { .type = "serial", .data = (void *)PORT_UNKNOWN, }, { /* end of list */ }, }; -- cgit v1.2.3