/*
* Pin Control and GPIO driver for SuperH Pin Function Controller.
*
* Authors: Magnus Damm, Paul Mundt, Laurent Pinchart
*
* Copyright (C) 2008 Magnus Damm
* Copyright (C) 2009 - 2012 Paul Mundt
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#define DRV_NAME "sh-pfc"
#include <linux/bitops.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/pinctrl/machine.h>
#include <linux/platform_device.h>
#include <linux/psci.h>
#include <linux/slab.h>
#include "core.h"
static int sh_pfc_map_resources(struct sh_pfc *pfc,
struct platform_device *pdev)
{
unsigned int num_windows, num_irqs;
struct sh_pfc_window *windows;
unsigned int *irqs = NULL;
struct resource *res;
unsigned int i;
int irq;
/* Count the MEM and IRQ resources. */
for (num_windows = 0;; num_windows++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, num_windows);
if (!res)
break;
}
for (num_irqs = 0;; num_irqs++) {
irq = platform_get_irq(pdev, num_irqs);
if (irq == -EPROBE_DEFER)
return irq;
if (irq < 0)
break;
}
if (num_windows == 0)
return -EINVAL;
/* Allocate memory windows and IRQs arrays. */
windows = devm_kzalloc(pfc->dev, num_windows * sizeof(*windows),
GFP_KERNEL);
if (windows == NULL)
return -ENOMEM;
pfc->num_windows = num_windows;
pfc->windows = windows;
if (num_irqs) {
irqs = devm_kzalloc(pfc->dev, num_irqs * sizeof(*irqs),
GFP_KERNEL);
if (irqs == NULL)
return -ENOMEM;
pfc->num_irqs = num_irqs;
pfc->irqs = irqs;
}
/* Fill them. */
for (i = 0; i < num_windows; i++) {
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
windows->phys = res->start;
windows->size = resource_size(res);
windows->virt = devm_ioremap_resource(pfc->dev, res);
if (IS_ERR(windows->virt))
return -ENOMEM;
windows++;
}
for (i = 0; i < num_irqs; i++)
*irqs++ = platform_get_irq(pdev, i);
return 0;
}
static void __iomem *sh_pfc_phys_to_virt(struct sh_pfc *pfc, u32 reg)
{
struct sh_pfc_window *window;
phys_addr_t address = reg;
unsigned int i;
/* scan through physical windows and convert address */
for (i = 0; i < pfc->num_windows; i++) {
window = pfc->windows + i;
if (address < window->phys)
continue;
if (address >= (window->phys + window->size))
continue;
return window->virt + (address - window->phys);
}
BUG();
return NULL;
}
int sh_pfc_get_pin_index(struct sh_pfc *pfc, unsigned int pin)
{
unsigned int offset;
unsigned int i;
for (i = 0, offset = 0; i < pfc->nr_ranges; ++i) {
const struct sh_pfc_pin_range *range = &pfc->ranges[i];
if (pin <= range->end)
return pin >= range->start
? offset + pin - range->start : -1;
offset += range->end - range->start + 1;
}
return -EINVAL;
}
static int sh_pfc_enum_in_range(u16 enum_id, const struct pinmux_range *r)
{
if (enum_id < r->begin)
return 0;
if (enum_id > r->end)
return 0;
return 1;
}
u32 sh_pfc_read_raw_reg(void __iomem *mapped_reg, unsigned int reg_width)
{
switch (reg_width) {
case 8:
return ioread8(mapped_reg);
case 16:
return ioread16(mapped_reg);
case 32:
return ioread32(mapped_reg);
}
BUG();
return 0;
}
void sh_pfc_write_raw_reg(void __iomem *mapped_reg, unsigned int reg_width,
u32 data)
{
switch (reg_width) {
case 8:
iowrite8(data, mapped_reg);
return;
case 16:
iowrite16(data, mapped_reg);
return;
case 32:
iowrite32(data, mapped_reg);
return;
}
BUG();
}
u32 sh_pfc_read(struct sh_pfc *pfc, u32 reg)
{
return sh_pfc_read_raw_reg(sh_pfc_phys_to_virt(pfc, reg), 32);
}
void sh_pfc_write(struct sh_pfc *pfc, u32 reg, u32 data)
{