Message ID | 1311873401-26075-1-git-send-email-tommy.lin.1101@gmail.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Fri, Jul 29, 2011 at 01:16:41AM +0800, Tommy Lin wrote: > +#include <linux/platform_device.h> > +#include <linux/proc_fs.h> > +#include <linux/io.h> > + > +#include <mach/gpio.h> Come on guys, you absolutely deserve your code to break as you're continuing to include mach/gpio.h directly, even after I've said I'm going to be moving stuff out of mach/gpio.h into asm/gpio.h... > +#include <asm-generic/gpio.h> > + > +#define gpio_get_value __gpio_get_value > +#define gpio_set_value __gpio_set_value > +#define gpio_cansleep __gpio_cansleep > +#define gpio_to_irq __gpio_to_irq Oh look, yet another identical implementation to add to the list... (it's this stuff which I'm moving after this merge window.) Please, make sure you include linux/gpio.h and not mach/gpio.h.
Hi Tommy, A few comments inline. Jamie On Fri, Jul 29, 2011 at 01:16:41AM +0800, Tommy Lin wrote: > This patch makes CNS3XXX platform support gpiolib. Users can > configure GPIO via gpiolib APIs easily on CNS3XXX platform. > > Signed-off-by: Tommy Lin <tommy.lin.1101@gmail.com> > --- [...] > +#define CONFIG_GPIO_CNS3XXX_DEBUG > + > +#ifdef CONFIG_GPIO_CNS3XXX_DEBUG > +#define __pr_debug(fmt, args...) printk(KERN_ERR fmt, ##args) > +#else > +#define __pr_debug(fmt, args...) > +#endif Could you use the kernel debug infrastructure for this? #define DEBUG and pr_debug()? > +#ifdef CONFIG_DEBUG_FS > +static void cns3xxx_dbg_show(struct seq_file *s, struct gpio_chip *chip); > +#else > +#define cns3xxx_dbg_show NULL > +#endif > + > +#define GPIO_PROC_NAME "gpio" > +static struct proc_dir_entry *proc_cns3xxx_gpio; > +static struct irq_chip cns3xxx_gpio_irq_chip; I think debugfs would be preferred for this rather than proc. > +struct chog_gpio_chip { > + struct gpio_chip chip; > + int irq; > + void __iomem *reg_base; > + void __iomem *reg_sharepin_en; > +}; > + > +#define INIT_CHOG_GPIO_CHIP(name, base_no, nr_gpio) \ > + { \ > + .chip = { \ > + .label = name, \ > + .owner = THIS_MODULE, \ > + .request = cns3xxx_request, \ > + .direction_input = cns3xxx_direction_in, \ > + .direction_output = cns3xxx_direction_out,\ > + .get = cns3xxx_get, \ > + .set = cns3xxx_set, \ > + .to_irq = cns3xxx_to_irq, \ > + .dbg_show = cns3xxx_dbg_show, \ > + .base = base_no, \ > + .ngpio = nr_gpio, \ > + .can_sleep = 0, \ > + }, \ > + } > + > + > +#define to_chog_gpio_chip(c) container_of(c, struct chog_gpio_chip, chip) > +#define nr_banks (sizeof(cns3xxx_gchip)/sizeof(struct chog_gpio_chip)) > + > +/* The CNS3XXX GPIO pins are shard with special functions which is described in > + * the following table. "none" in this table represent the corresponding pins > + * are dedicate GPIO. > + */ > +const char *sharepin_desc[] = { > + /* GPIOA group */ > +/* 0 */ "none", "none", "SD_PWR_ON", "OTG_DRV_VBUS", > +/* 4 */ "Don't use", "none", "none", "none", > +/* 8 */ "CIM_nOE", "LCD_Power", "SMI_nCS3", "SMI_nCS2", > +/* 12 */ "SMI_Clk", "SMI_nADV", "SMI_CRE", "SMI_Addr[26]", > +/* 16 */ "SD_nCD", "SD_nWP", "SD_CLK", "SD_CMD", > +/* 20 */ "SD_DT[7]", "SD_DT[6]", "SD_DT[5]", "SD_DT[4]", > +/* 24 */ "SD_DT[3]", "SD_DT[2]", "SD_DT[1]", "SD_DT[0]", > +/* 28 */ "SD_LED", "UR_RXD1", "UR_TXD1", "UR_RTS2", > + /* GPIOB group */ > +/* 0 */ "UR_CTS2", "UR_RXD2", "UR_TXD2", "PCMCLK", > +/* 4 */ "PCMFS", "PCMDT", "PCMDR", "SPInCS1", > +/* 8 */ "SPInCS2", "SPICLK", "SPIDT", "SPIDR", > +/* 12 */ "SCL", "SDA", "GMII2_CRS", "GMII2_COL", > +/* 16 */ "RGMII1_CRS", "RGMII1_COL", "RGMII0_CRS", "RGMII0_COL", > +/* 20 */ "MDC", "MDIO", "I2SCLK", "I2SFS", > +/* 24 */ "I2SDT", "I2SDR", "ClkOut", "Ext_Intr2", > +/* 28 */ "Ext_Intr1", "Ext_Intr0", "SATA_LED1", "SATA_LED0", > +}; > + > +struct cns3xxx_regs { > + char *name; > + void __iomem *addr; > + u32 offset; > +}; > + > +struct cns3xxx_regs gpio_regs[] = { > + {"Data Out", 0, GPIO_OUTPUT_OFFSET}, > + {"Data In", 0, GPIO_INPUT_OFFSET}, > + {"Direction", 0, GPIO_DIR_OFFSET}, > + {"Interrupt Enable", 0, GPIO_INTR_ENABLE_OFFSET}, > + {"Interrupt Raw Status", 0, GPIO_INTR_RAW_STATUS_OFFSET}, > + {"Interrupt Masked Status", 0, GPIO_INTR_MASKED_STATUS_OFFSET}, > + {"Interrupt Level Trigger", 0, GPIO_INTR_TRIGGER_METHOD_OFFSET}, > + {"Interrupt Both Edge", 0, GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET}, > + {"Interrupt Falling Edge", 0, GPIO_INTR_TRIGGER_TYPE_OFFSET}, > + {"Interrupt MASKED", 0, GPIO_INTR_MASK_OFFSET}, > + {"GPIO Bounce Enable", 0, GPIO_BOUNCE_ENABLE_OFFSET}, > + {"GPIO Bounce Prescale", 0, GPIO_BOUNCE_PRESCALE_OFFSET}, > +}; > + > +struct cns3xxx_regs misc_regs[] = { > + {"Drive Strength Register A", MISC_IO_PAD_DRIVE_STRENGTH_CTRL_A}, > + {"Drive Strength Register B", MISC_IO_PAD_DRIVE_STRENGTH_CTRL_B}, > + {"Pull Up/Down Ctrl A[15:0]", MISC_GPIOA_15_0_PULL_CTRL_REG}, > + {"Pull Up/Down Ctrl A[31:16]", MISC_GPIOA_16_31_PULL_CTRL_REG}, > + {"Pull Up/Down Ctrl B[15:0]", MISC_GPIOB_15_0_PULL_CTRL_REG}, > + {"Pull Up/Down Ctrl B[31:16]", MISC_GPIOB_16_31_PULL_CTRL_REG}, > +}; > + > + > +static int cns3xxx_request(struct gpio_chip *chip, unsigned offset) > +{ > + /* GPIOA4 is reserved for chip bonding configuration. Please don't use > + * and configure GPIOA4. > + */ > + if ((strcmp(chip->label, "GPIOA") == 0) && (offset == 4)) > + return -EINVAL; > + return 0; > +} > + > +/* > + * Configure the GPIO line as an input. > + */ > +static int cns3xxx_direction_in(struct gpio_chip *chip, unsigned offset) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + u32 reg; > + > + /* Clear corresponding register bit to set as input pin. */ > + reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + reg &= ~(1 << offset); > + writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + > + return 0; > +} > + > +/* > + * Set the state of an output GPIO line. > + */ > +static void cns3xxx_set(struct gpio_chip *chip, unsigned offset, int value) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + > + if (value) > + /* Write 1 to set corresponding bit output "HIGH" > + * Multi-bit write is allowed. Write 0 makes no change. > + */ > + writel(1 << offset, > + cns3xxx_gpio->reg_base + GPIO_BIT_SET_OFFSET); > + else > + /* Write 1 to set corresponding bit output "LOW" > + * Multi-bit write is allowed. Write 0 makes no change. > + */ > + writel(1 << offset, > + cns3xxx_gpio->reg_base + GPIO_BIT_CLEAR_OFFSET); > +} > + > +/* > + * Configure the GPIO line as an output, with default state. > + */ > +static int cns3xxx_direction_out(struct gpio_chip *chip, > + unsigned offset, int value) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + u32 reg; > + > + /* Set corresponding register bit to set as output pin. */ > + reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + reg |= 1 << offset; > + writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + > + cns3xxx_set(chip, offset, value); > + > + return 0; > +} > + > +/* > + * Read the state of a GPIO line. > + */ > +static int cns3xxx_get(struct gpio_chip *chip, unsigned offset) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + u32 reg; > + int ret; > + > + reg = readl(cns3xxx_gpio->reg_base + GPIO_INPUT_OFFSET); > + ret = reg & (1 << offset); > + > + return ret; > +} The generic gpio (include/linux/basic_mmio_gpio.h) driver will support all of these accessors and provide all of the value caching too. You can add your .request method in to make sure you can't access the bond option pins but still use the other default accessors. > + > +/* > + * GPIO interrtups are remapped to unused irq number. > + * The remapped GPIO IRQ number start from NR_IRQS_CNS3XXX (96). Here is the > + * table of GPIO to irq mapping table. > + * > + * GPIOA GPIOB | GPIOA GPIOB > + * No. IRQ IRQ | No. IRQ IRQ > + * 0 96 128 | 16 112 144 > + * 1 97 129 | 17 113 145 > + * 2 98 130 | 18 114 146 > + * 3 99 131 | 19 115 147 > + * 4 100 132 | 20 116 148 > + * 5 101 133 | 21 117 149 > + * 6 102 134 | 22 118 150 > + * 7 103 135 | 23 119 151 > + * 8 104 136 | 24 120 152 > + * 9 105 137 | 25 121 153 > + * 10 106 138 | 26 122 154 > + * 11 107 139 | 27 123 155 > + * 12 108 140 | 28 124 156 > + * 13 109 141 | 29 125 157 > + * 14 110 142 | 30 126 158 > + * 15 111 143 | 31 127 159 > + */ > +static int cns3xxx_to_irq(struct gpio_chip *chip, unsigned offset) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + > + return offset + NR_IRQS_CNS3XXX + cns3xxx_gpio->chip.base; > +} > + > +static unsigned cns3xxx_irq_to_gpio_offset(struct gpio_chip *chip, int irq) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + > + return irq - NR_IRQS_CNS3XXX - cns3xxx_gpio->chip.base; > +} > + > +int cns3xxx_gpio_set_irq_type(struct irq_data *data, unsigned int type) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset; > + u32 reg_level, reg_both, reg_low, index; > + > + offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + index = 1 << offset; > + > + reg_level = readl(base + GPIO_INTR_TRIGGER_METHOD_OFFSET); > + reg_both = readl(base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET); > + reg_low = readl(base + GPIO_INTR_TRIGGER_TYPE_OFFSET); > + > + switch (type) { > + case IRQ_TYPE_EDGE_RISING: > + reg_level &= ~index; > + reg_both &= ~index; > + reg_low &= ~index; > + break; > + case IRQ_TYPE_EDGE_FALLING: > + reg_level &= ~index; > + reg_both &= ~index; > + reg_low |= index; > + break; > + case IRQ_TYPE_EDGE_BOTH: > + reg_level &= ~index; > + reg_both |= index; > + break; > + case IRQ_TYPE_LEVEL_LOW: > + reg_level |= index; > + reg_low |= index; > + break; > + case IRQ_TYPE_LEVEL_HIGH: > + reg_level |= index; > + reg_low &= ~index; > + break; > + default: > + return -EINVAL; > + } > + > + cns3xxx_direction_in(chip, offset); > + writel(reg_level, base + GPIO_INTR_TRIGGER_METHOD_OFFSET); > + writel(reg_both, base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET); > + writel(reg_low, base + GPIO_INTR_TRIGGER_TYPE_OFFSET); > + > + return 0; > +} > + > +static void cns3xxx_irq_enable(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_ENABLE_OFFSET); > + reg |= (1 << offset); > + writel(reg, base + GPIO_INTR_ENABLE_OFFSET); > +} > + > +static void cns3xxx_irq_disable(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_ENABLE_OFFSET); > + reg &= ~(1 << offset); > + writel(reg, base + GPIO_INTR_ENABLE_OFFSET); > +} > + > +static void cns3xxx_gpio_mask(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_MASK_OFFSET); > + reg |= (1 << offset); > + writel(reg, base + GPIO_INTR_MASK_OFFSET); > +} > + > +static void cns3xxx_gpio_unmask(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_MASK_OFFSET); > + reg &= ~(1 << offset); > + writel(reg, base + GPIO_INTR_MASK_OFFSET); > +} > + > +static struct irq_chip cns3xxx_gpio_irq_chip = { > + .name = "GPIO", > + .irq_enable = cns3xxx_irq_enable, > + .irq_disable = cns3xxx_irq_disable, > + .irq_mask = cns3xxx_gpio_mask, > + .irq_unmask = cns3xxx_gpio_unmask, > + .irq_set_type = cns3xxx_gpio_set_irq_type, > +}; Thomas provided a generic irq chip infrastructure for this sort of thing (struct irq_chip_generic) that would be useful here. > + > +static struct chog_gpio_chip cns3xxx_gchip[] = { > + /* label, base, ngpio */ > + INIT_CHOG_GPIO_CHIP("GPIOA", 0x00, MAX_GPIOA_NO), > + INIT_CHOG_GPIO_CHIP("GPIOB", MAX_GPIOA_NO, MAX_GPIOB_NO), > +}; > + > + > +static int cns3xxx_gpio_read_proc(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + int num = 0, i, nr_regs; > + > + nr_regs = sizeof(gpio_regs)/sizeof(struct cns3xxx_regs); > + num += sprintf(page + num, > + "Register Description GPIOA GPIOB\n" > + "==================== ===== =====\n"); > + num += sprintf(page + num, "%-26.26s: %08x %08x\n", "GPIO Disable", > + readl(cns3xxx_gchip[0].reg_sharepin_en), > + readl(cns3xxx_gchip[1].reg_sharepin_en)); > + for (i = 0; i < nr_regs; i++) { > + num += sprintf(page + num, "%-26.26s: %08x %08x\n", > + gpio_regs[i].name, > + readl(cns3xxx_gchip[0].reg_base + gpio_regs[i].offset), > + readl(cns3xxx_gchip[1].reg_base + gpio_regs[i].offset)); > + } > + > + num += sprintf(page + num, "\n" > + "Register Description Value\n" > + "==================== =====\n"); > + nr_regs = sizeof(misc_regs)/sizeof(struct cns3xxx_regs); > + for (i = 0; i < nr_regs; i++) { > + num += sprintf(page + num, "%-26.26s: %08x\n", > + misc_regs[i].name, > + readl(misc_regs[i].addr)); > + } > + > + return num; > +} > + > +/* > + * Turn on corresponding shared pin function. > + * Turn on shared pin function will also disable GPIO function. Related GPIO > + * control registers are still accessable but not reflect to pin. > + */ > +int cns3xxx_sharepin_request(unsigned gpio, const char *label) > +{ > + struct gpio_chip *chip; > + int i, reg, ret, offset = gpio; > + > + ret = gpio_request(gpio, label); > + if (ret) { > + __pr_debug("gpio%d(%s) already in use! Err=%d\n", > + gpio, label, ret); > + return ret; > + } > + > + for (i = 0; i < nr_banks; i++) { > + chip = &cns3xxx_gchip[i].chip; > + if (offset > chip->ngpio) { > + offset -= chip->ngpio; > + continue; > + } > + reg = readl(cns3xxx_gchip[i].reg_sharepin_en); > + reg |= (1 << offset); > + writel(reg, cns3xxx_gchip[i].reg_sharepin_en); > + __pr_debug("%s[%d] is occupied by %s function!\n", > + chip->label, offset, sharepin_desc[gpio]); > + break; > + } > + > + return 0; > +} > +EXPORT_SYMBOL(cns3xxx_sharepin_request); > + > +/* > + * Turn off corresponding share pin function. > + */ > +void cns3xxx_sharepin_free(unsigned gpio) > +{ > + struct gpio_chip *chip; > + int i, reg, offset = gpio; > + > + gpio_free(gpio); > + > + for (i = 0; i < nr_banks; i++) { > + chip = &cns3xxx_gchip[i].chip; > + if (offset > chip->ngpio) { > + offset -= chip->ngpio; > + continue; > + } > + reg = readl(cns3xxx_gchip[i].reg_sharepin_en); > + reg &= ~(1 << offset); > + writel(reg, cns3xxx_gchip[i].reg_sharepin_en); > + printk(KERN_INFO "%s[%d] share pin function (%s) disabled!\n", > + chip->label, offset, sharepin_desc[gpio]); > + break; > + } > +} > +EXPORT_SYMBOL(cns3xxx_sharepin_free); > + > + > +#ifdef CONFIG_DEBUG_FS > + > +#include <linux/debugfs.h> > +#include <linux/seq_file.h> > + > +static void cns3xxx_dbg_show(struct seq_file *s, struct gpio_chip *chip) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + int i, is_out; > + unsigned gpio; > + const char *gpio_label; > + > + for (i = 0; i < chip->ngpio; i++) { > + gpio = chip->base + i; > + gpio_label = gpiochip_is_requested(chip, i); > + if (!gpio_label) > + continue; > + is_out = test_bit(i, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + seq_printf(s, " gpio-%-3d [%-20.20s] %s %s (%s%-2d)\n", > + gpio, gpio_label, > + is_out ? "out" : "in ", > + chip->get(chip, i) ? "hi" : "lo", > + chip->label, i); > + } > +} > + > +static int cns3xxx_dbg_gpio_show_all(struct seq_file *s, void *unused) > +{ > + int i, j, is_out, disabled; > + unsigned gpio; > + const char *gpio_label; > + struct gpio_chip *chip; > + > + for (j = 0; j < nr_banks; j++) { > + chip = &cns3xxx_gchip[j].chip; > + seq_printf(s, " Label Mode Dir Value Note\n"); > + seq_printf(s, " ============================================================\n"); > + for (i = 0; i < chip->ngpio; i++) { > + gpio = chip->base + i; > + disabled = test_bit(i, cns3xxx_gchip[j].reg_sharepin_en); > + gpio_label = gpiochip_is_requested(chip, i); > + if (!gpio_label) { > + if (disabled) > + gpio_label = sharepin_desc[gpio]; > + else > + gpio_label = "Not requested"; > + } > + is_out = test_bit(i, cns3xxx_gchip[j].reg_base + GPIO_DIR_OFFSET); > + seq_printf(s, " gpio-%-3d [%-20.20s] %-10.10s%s %s (%s%-2d)\n", > + gpio, gpio_label, > + disabled ? "Function" : "GPIO", > + is_out ? "out" : "in ", > + chip->get(chip, i) ? "hi" : "lo", > + chip->label, i); > + } > + seq_printf(s, "\n"); > + } > + > + return 0; > +} > + > +static int dbg_gpio_open(struct inode *inode, struct file *file) > +{ > + return single_open(file, cns3xxx_dbg_gpio_show_all, &inode->i_private); > +} > + > +static const struct file_operations debug_fops = { > + .open = dbg_gpio_open, > + .read = seq_read, > + .llseek = seq_lseek, > + .release = single_release, > +}; > + > +static int __init cns3xxx_gpio_debuginit(void) > +{ > + (void) debugfs_create_file("gpio-cns3xxx", S_IRUGO, > + NULL, NULL, &debug_fops); > + return 0; > +} > +late_initcall(cns3xxx_gpio_debuginit); > + > +#endif /* CONFIG_DEBUG_FS */ > + > +static void chained_gpio_irq_handler(unsigned irq, struct irq_desc *desc) > +{ > + struct chog_gpio_chip *chip = irq_get_handler_data(irq); > + struct irq_chip *ichip = irq_desc_get_chip(desc); > + unsigned i; > + int target_irq; > + u32 status; > + > + chained_irq_enter(ichip, desc); > + > + status = readl(chip->reg_base + GPIO_INTR_MASKED_STATUS_OFFSET); > + > + /* Clean System irq status */ > + writel(status, chip->reg_base + GPIO_INTR_CLEAR_OFFSET); > + > + for (i = 0; i < chip->chip.ngpio; i++) { > + if (status & (1 << i)) { > + target_irq = cns3xxx_to_irq(&chip->chip, i); > + printk(KERN_INFO "Invoke cascaded irq %d from irq %d\n", > + target_irq, chip->irq); > + generic_handle_irq(target_irq); > + } > + } > + > + chained_irq_exit(ichip, desc); > +} > + > +static int __devinit gpio_probe(struct platform_device *pdev) > +{ > + int i, j, nr_gpios = 0, irq = 0; > + struct resource *res; > + void __iomem *misc_reg; > + > + cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); Should this be using the clk framework (clk_get() + clk_enable())? > + cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); > + > + if (cns3xxx_proc_dir) { > + proc_cns3xxx_gpio = create_proc_entry(GPIO_PROC_NAME, > + S_IFREG | S_IRUGO, cns3xxx_proc_dir) ; > + if (proc_cns3xxx_gpio) > + proc_cns3xxx_gpio->read_proc = cns3xxx_gpio_read_proc; > + } > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "MISC"); > + if (res) > + misc_reg = (void __iomem *)res->start; request_mem_region() and ioremap(). IORESOURCE_MEM resources should be physical addresses. > + else > + return -ENODEV; > + > + /* Scan and match GPIO resources */ > + for (i = 0; i < nr_banks; i++) { > + /* Fetech GPIO base address */ > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > + cns3xxx_gchip[i].chip.label); > + if (!res) > + continue; > + cns3xxx_gchip[i].reg_base = (void __iomem *)res->start; The same here. > + > + /* Fetech GPIO interrupt number */ > + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, > + cns3xxx_gchip[i].chip.label); > + if (!res) > + continue; > + irq = res->start; > + cns3xxx_gchip[i].irq = irq; > + __pr_debug("%s base=0x%08x, irq=%d", > + cns3xxx_gchip[i].chip.label, > + (u32)cns3xxx_gchip[i].reg_base, irq); > + > + cns3xxx_gchip[i].chip.dev = &pdev->dev; > + cns3xxx_gchip[i].reg_sharepin_en = misc_reg + 0x14 + i * 4; > + > + gpiochip_add(&cns3xxx_gchip[i].chip); > + > + /* Clear All Interrupt Status */ > + writel(0xFFFFFFFF, cns3xxx_gchip[i].reg_base + > + GPIO_INTR_CLEAR_OFFSET); > + > + /* Initial irq_chip to handle virtual GPIO irqs. */ > + for (j = 0; j < cns3xxx_gchip[i].chip.ngpio; j++) { > + irq = cns3xxx_to_irq(&cns3xxx_gchip[i].chip, j); > + irq_set_chip_and_handler(irq, &cns3xxx_gpio_irq_chip, > + handle_simple_irq); > + set_irq_flags(irq, IRQF_VALID); > + irq_set_chip_data(irq, &cns3xxx_gchip[i]); > + } > + irq_set_chained_handler(cns3xxx_gchip[i].irq, > + chained_gpio_irq_handler); > + irq_set_handler_data(cns3xxx_gchip[i].irq, > + &cns3xxx_gchip[i]); > + > + nr_gpios += cns3xxx_gchip[i].chip.ngpio; > + if (nr_gpios >= MAX_GPIO_NO) > + break; > + } > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int gpio_cns3xxx_suspend(struct platform_device *dev, pm_message_t state) > +{ > + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); > + > + return 0; > +} > + > +static int gpio_cns3xxx_resume(struct platform_device *dev) > +{ > + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); > + > + return 0; > +} > +#endif /* CONFIG_PM */ > + > +static struct platform_driver gpio_driver = { > + .probe = gpio_probe, > +#ifdef CONFIG_PM > + .suspend = gpio_cns3xxx_suspend, > + .resume = gpio_cns3xxx_resume, > +#endif /* CONFIG_PM */ You can put the power management operations in .dev_pm_ops, but as they don't do anything you can just remove them. > + .driver = { > + .owner = THIS_MODULE, > + .name = "cns3xxx-gpio", > + }, > +}; > + > +int __init cns3xxx_gpio_init(void) > +{ > + return platform_driver_probe(&gpio_driver, gpio_probe); > +} > + > +void __exit cns3xxx_gpio_exit(void) > +{ > + if (proc_cns3xxx_gpio) > + remove_proc_entry(GPIO_PROC_NAME, cns3xxx_proc_dir); > +} > + > +module_init(cns3xxx_gpio_init); > +module_exit(cns3xxx_gpio_exit); > + > +MODULE_LICENSE("GPL"); > + > diff --git a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h > index 191c8e5..217bb4d 100644 > --- a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h > +++ b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h > @@ -624,9 +624,15 @@ int cns3xxx_cpu_clock(void); > > #define NR_IRQS_CNS3XXX (IRQ_TC11MP_GIC_START + 64) > > -#if !defined(NR_IRQS) || (NR_IRQS < NR_IRQS_CNS3XXX) > +#define MAX_GPIOA_NO 32 > +#define MAX_GPIOB_NO 32 > +#define MAX_GPIO_NO (MAX_GPIOA_NO + MAX_GPIOB_NO) > + > +#if !defined(NR_IRQS) || (NR_IRQS < (NR_IRQS_CNS3XXX + MAX_GPIO_NO)) > #undef NR_IRQS > -#define NR_IRQS NR_IRQS_CNS3XXX > +#define NR_IRQS (NR_IRQS_CNS3XXX + MAX_GPIO_NO) > #endif > > +extern struct proc_dir_entry *cns3xxx_proc_dir; > + > #endif /* __MACH_BOARD_CNS3XXX_H */ > diff --git a/arch/arm/mach-cns3xxx/include/mach/gpio.h b/arch/arm/mach-cns3xxx/include/mach/gpio.h > new file mode 100644 > index 0000000..22efca8 > --- /dev/null > +++ b/arch/arm/mach-cns3xxx/include/mach/gpio.h > @@ -0,0 +1,63 @@ > +/******************************************************************************* > + * > + * Copyright (c) 2011 Cavium > + * > + * This file is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License, Version 2, as > + * published by the Free Software Foundation. > + * > + * This file is distributed in the hope that it will be useful, > + * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or > + * NONINFRINGEMENT. See the GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this file; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or > + * visit http://www.gnu.org/licenses/. > + * > + * This file may also be available under a different license from Cavium. > + * Contact Cavium for more information > + * > + ******************************************************************************/ > + > +#ifndef _CNS3XXX_GPIO_H_ > +#define _CNS3XXX_GPIO_H_ > + > +#include <mach/cns3xxx.h> > + > +#define ARCH_NR_GPIOS MAX_GPIO_NO > + > +#include <asm-generic/gpio.h> > + > +#define GPIO_OUTPUT_OFFSET 0x00 > +#define GPIO_INPUT_OFFSET 0x04 > +#define GPIO_DIR_OFFSET 0x08 > +#define GPIO_BIT_SET_OFFSET 0x10 > +#define GPIO_BIT_CLEAR_OFFSET 0x14 > +#define GPIO_INTR_ENABLE_OFFSET 0x20 > +#define GPIO_INTR_RAW_STATUS_OFFSET 0x24 > +#define GPIO_INTR_MASKED_STATUS_OFFSET 0x28 > +#define GPIO_INTR_MASK_OFFSET 0x2C > +#define GPIO_INTR_CLEAR_OFFSET 0x30 > +#define GPIO_INTR_TRIGGER_METHOD_OFFSET 0x34 > +#define GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET 0x38 > +#define GPIO_INTR_TRIGGER_TYPE_OFFSET 0x3C > +#define GPIO_BOUNCE_ENABLE_OFFSET 0x40 > +#define GPIO_BOUNCE_PRESCALE_OFFSET 0x44 > + > + > +#define gpio_get_value __gpio_get_value > +#define gpio_set_value __gpio_set_value > +#define gpio_cansleep __gpio_cansleep > +#define gpio_to_irq __gpio_to_irq > + > +#define GPIOA(n) n > +#define GPIOB(n) (MAX_GPIOA_NO + n) > + > +/* Function prototype */ > +int cns3xxx_sharepin_request(unsigned gpio, const char *label); > +void cns3xxx_sharepin_free(unsigned gpio); > + > +#endif > + > -- > 1.7.6 > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Hi Jamie, Thanks for your comments. > [...] >> +#define CONFIG_GPIO_CNS3XXX_DEBUG >> + >> +#ifdef CONFIG_GPIO_CNS3XXX_DEBUG >> +#define __pr_debug(fmt, args...) printk(KERN_ERR fmt, ##args) >> +#else >> +#define __pr_debug(fmt, args...) >> +#endif > > Could you use the kernel debug infrastructure for this? #define DEBUG > and pr_debug()? Ok I will do it. >> +static struct proc_dir_entry *proc_cns3xxx_gpio; >> +static struct irq_chip cns3xxx_gpio_irq_chip; > > I think debugfs would be preferred for this rather than proc. The proc entry is used in the old package, so I have to keep it. Can I keep it? Or should I move it to debugfs? >> +static int cns3xxx_request(struct gpio_chip *chip, unsigned offset) >> +{ >> + /* GPIOA4 is reserved for chip bonding configuration. Please don't use >> + * and configure GPIOA4. >> + */ >> + if ((strcmp(chip->label, "GPIOA") == 0) && (offset == 4)) >> + return -EINVAL; >> + return 0; >> +} >> + > > The generic gpio (include/linux/basic_mmio_gpio.h) driver will support > all of these accessors and provide all of the value caching too. You > can add your .request method in to make sure you can't access the bond > option pins but still use the other default accessors. >> +static struct irq_chip cns3xxx_gpio_irq_chip = { >> + .name = "GPIO", >> + .irq_enable = cns3xxx_irq_enable, >> + .irq_disable = cns3xxx_irq_disable, >> + .irq_mask = cns3xxx_gpio_mask, >> + .irq_unmask = cns3xxx_gpio_unmask, >> + .irq_set_type = cns3xxx_gpio_set_irq_type, >> +}; > > Thomas provided a generic irq chip infrastructure for this sort of thing > (struct irq_chip_generic) that would be useful here. I have to study how to use basic_mmio_gpio.h and irq_chip_generic. >> + void __iomem *misc_reg; >> + >> + cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); > > Should this be using the clk framework (clk_get() + clk_enable())? The platform device didn't implement these function yet! Maybe I should complete the work first. >> + cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); >> + >> + if (cns3xxx_proc_dir) { >> + proc_cns3xxx_gpio = create_proc_entry(GPIO_PROC_NAME, >> + S_IFREG | S_IRUGO, cns3xxx_proc_dir) ; >> + if (proc_cns3xxx_gpio) >> + proc_cns3xxx_gpio->read_proc = cns3xxx_gpio_read_proc; >> + } >> + >> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "MISC"); >> + if (res) >> + misc_reg = (void __iomem *)res->start; > > request_mem_region() and ioremap(). IORESOURCE_MEM resources should be > physical addresses. The memory space is statically mapped when platform initialized so I just query virtual memory space here. Should I use ioremap() rather than static mapping? > >> + else >> + return -ENODEV; >> + >> + /* Scan and match GPIO resources */ >> + for (i = 0; i < nr_banks; i++) { >> + /* Fetech GPIO base address */ >> + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, >> + cns3xxx_gchip[i].chip.label); >> + if (!res) >> + continue; >> + cns3xxx_gchip[i].reg_base = (void __iomem *)res->start; > > The same here. > >> +#ifdef CONFIG_PM >> +static int gpio_cns3xxx_suspend(struct platform_device *dev, pm_message_t state) >> +{ >> + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); >> + >> + return 0; >> +} >> + >> +static int gpio_cns3xxx_resume(struct platform_device *dev) >> +{ >> + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); >> + >> + return 0; >> +} >> +#endif /* CONFIG_PM */ >> + >> +static struct platform_driver gpio_driver = { >> + .probe = gpio_probe, >> +#ifdef CONFIG_PM >> + .suspend = gpio_cns3xxx_suspend, >> + .resume = gpio_cns3xxx_resume, >> +#endif /* CONFIG_PM */ > > You can put the power management operations in .dev_pm_ops, but as they > don't do anything you can just remove them. OK, I will remove it.
Hi,
On Fri, Jul 29, 2011 at 01:16:41AM +0800, Tommy Lin wrote:
> arch/arm/mach-cns3xxx/gpio.c | 692 ++++++++++++++++++++++++++
why is this driver under arch/arm/mach-cns3xxx instead of drivers/gpio ?
On Fri, Jul 29, 2011 at 01:16:41AM +0800, Tommy Lin wrote: > +/* The CNS3XXX GPIO pins are shard with special functions which is described in > + * the following table. "none" in this table represent the corresponding pins > + * are dedicate GPIO. > + */ > +const char *sharepin_desc[] = { static ? > + /* GPIOA group */ > +/* 0 */ "none", "none", "SD_PWR_ON", "OTG_DRV_VBUS", > +/* 4 */ "Don't use", "none", "none", "none", > +/* 8 */ "CIM_nOE", "LCD_Power", "SMI_nCS3", "SMI_nCS2", > +/* 12 */ "SMI_Clk", "SMI_nADV", "SMI_CRE", "SMI_Addr[26]", > +/* 16 */ "SD_nCD", "SD_nWP", "SD_CLK", "SD_CMD", > +/* 20 */ "SD_DT[7]", "SD_DT[6]", "SD_DT[5]", "SD_DT[4]", > +/* 24 */ "SD_DT[3]", "SD_DT[2]", "SD_DT[1]", "SD_DT[0]", > +/* 28 */ "SD_LED", "UR_RXD1", "UR_TXD1", "UR_RTS2", > + /* GPIOB group */ > +/* 0 */ "UR_CTS2", "UR_RXD2", "UR_TXD2", "PCMCLK", > +/* 4 */ "PCMFS", "PCMDT", "PCMDR", "SPInCS1", > +/* 8 */ "SPInCS2", "SPICLK", "SPIDT", "SPIDR", > +/* 12 */ "SCL", "SDA", "GMII2_CRS", "GMII2_COL", > +/* 16 */ "RGMII1_CRS", "RGMII1_COL", "RGMII0_CRS", "RGMII0_COL", > +/* 20 */ "MDC", "MDIO", "I2SCLK", "I2SFS", > +/* 24 */ "I2SDT", "I2SDR", "ClkOut", "Ext_Intr2", > +/* 28 */ "Ext_Intr1", "Ext_Intr0", "SATA_LED1", "SATA_LED0", > +}; > + > +struct cns3xxx_regs { > + char *name; > + void __iomem *addr; > + u32 offset; > +}; > + > +struct cns3xxx_regs gpio_regs[] = { static? > + {"Data Out", 0, GPIO_OUTPUT_OFFSET}, > + {"Data In", 0, GPIO_INPUT_OFFSET}, > + {"Direction", 0, GPIO_DIR_OFFSET}, > + {"Interrupt Enable", 0, GPIO_INTR_ENABLE_OFFSET}, > + {"Interrupt Raw Status", 0, GPIO_INTR_RAW_STATUS_OFFSET}, > + {"Interrupt Masked Status", 0, GPIO_INTR_MASKED_STATUS_OFFSET}, > + {"Interrupt Level Trigger", 0, GPIO_INTR_TRIGGER_METHOD_OFFSET}, > + {"Interrupt Both Edge", 0, GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET}, > + {"Interrupt Falling Edge", 0, GPIO_INTR_TRIGGER_TYPE_OFFSET}, > + {"Interrupt MASKED", 0, GPIO_INTR_MASK_OFFSET}, > + {"GPIO Bounce Enable", 0, GPIO_BOUNCE_ENABLE_OFFSET}, > + {"GPIO Bounce Prescale", 0, GPIO_BOUNCE_PRESCALE_OFFSET}, > +}; > + > +struct cns3xxx_regs misc_regs[] = { static? > + {"Drive Strength Register A", MISC_IO_PAD_DRIVE_STRENGTH_CTRL_A}, > + {"Drive Strength Register B", MISC_IO_PAD_DRIVE_STRENGTH_CTRL_B}, > + {"Pull Up/Down Ctrl A[15:0]", MISC_GPIOA_15_0_PULL_CTRL_REG}, > + {"Pull Up/Down Ctrl A[31:16]", MISC_GPIOA_16_31_PULL_CTRL_REG}, > + {"Pull Up/Down Ctrl B[15:0]", MISC_GPIOB_15_0_PULL_CTRL_REG}, > + {"Pull Up/Down Ctrl B[31:16]", MISC_GPIOB_16_31_PULL_CTRL_REG}, > +}; > + > + > +static int cns3xxx_request(struct gpio_chip *chip, unsigned offset) > +{ > + /* GPIOA4 is reserved for chip bonding configuration. Please don't use > + * and configure GPIOA4. > + */ > + if ((strcmp(chip->label, "GPIOA") == 0) && (offset == 4)) > + return -EINVAL; > + return 0; > +} > + > +/* > + * Configure the GPIO line as an input. > + */ > +static int cns3xxx_direction_in(struct gpio_chip *chip, unsigned offset) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + u32 reg; > + > + /* Clear corresponding register bit to set as input pin. */ > + reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + reg &= ~(1 << offset); > + writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + > + return 0; > +} > + > +/* > + * Set the state of an output GPIO line. > + */ > +static void cns3xxx_set(struct gpio_chip *chip, unsigned offset, int value) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + > + if (value) > + /* Write 1 to set corresponding bit output "HIGH" > + * Multi-bit write is allowed. Write 0 makes no change. > + */ > + writel(1 << offset, > + cns3xxx_gpio->reg_base + GPIO_BIT_SET_OFFSET); > + else > + /* Write 1 to set corresponding bit output "LOW" > + * Multi-bit write is allowed. Write 0 makes no change. > + */ > + writel(1 << offset, > + cns3xxx_gpio->reg_base + GPIO_BIT_CLEAR_OFFSET); > +} > + > +/* > + * Configure the GPIO line as an output, with default state. > + */ > +static int cns3xxx_direction_out(struct gpio_chip *chip, > + unsigned offset, int value) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + u32 reg; > + > + /* Set corresponding register bit to set as output pin. */ > + reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + reg |= 1 << offset; > + writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); > + > + cns3xxx_set(chip, offset, value); It is considered good form to set the bit state first before setting it as an output to avoid unintentional glitches. Please check whether your hardware can do this and if so modify the code to do so. That's why we don't have a 'direction' function and a separate 'set_value' function... > + > + return 0; > +} > + > +/* > + * Read the state of a GPIO line. > + */ > +static int cns3xxx_get(struct gpio_chip *chip, unsigned offset) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + u32 reg; > + int ret; > + > + reg = readl(cns3xxx_gpio->reg_base + GPIO_INPUT_OFFSET); > + ret = reg & (1 << offset); > + > + return ret; > +} > + > +/* > + * GPIO interrtups are remapped to unused irq number. > + * The remapped GPIO IRQ number start from NR_IRQS_CNS3XXX (96). Here is the > + * table of GPIO to irq mapping table. > + * > + * GPIOA GPIOB | GPIOA GPIOB > + * No. IRQ IRQ | No. IRQ IRQ > + * 0 96 128 | 16 112 144 > + * 1 97 129 | 17 113 145 > + * 2 98 130 | 18 114 146 > + * 3 99 131 | 19 115 147 > + * 4 100 132 | 20 116 148 > + * 5 101 133 | 21 117 149 > + * 6 102 134 | 22 118 150 > + * 7 103 135 | 23 119 151 > + * 8 104 136 | 24 120 152 > + * 9 105 137 | 25 121 153 > + * 10 106 138 | 26 122 154 > + * 11 107 139 | 27 123 155 > + * 12 108 140 | 28 124 156 > + * 13 109 141 | 29 125 157 > + * 14 110 142 | 30 126 158 > + * 15 111 143 | 31 127 159 > + */ > +static int cns3xxx_to_irq(struct gpio_chip *chip, unsigned offset) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + > + return offset + NR_IRQS_CNS3XXX + cns3xxx_gpio->chip.base; > +} > + > +static unsigned cns3xxx_irq_to_gpio_offset(struct gpio_chip *chip, int irq) > +{ > + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); > + > + return irq - NR_IRQS_CNS3XXX - cns3xxx_gpio->chip.base; > +} > + > +int cns3xxx_gpio_set_irq_type(struct irq_data *data, unsigned int type) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset; > + u32 reg_level, reg_both, reg_low, index; > + > + offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + index = 1 << offset; > + > + reg_level = readl(base + GPIO_INTR_TRIGGER_METHOD_OFFSET); > + reg_both = readl(base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET); > + reg_low = readl(base + GPIO_INTR_TRIGGER_TYPE_OFFSET); > + > + switch (type) { > + case IRQ_TYPE_EDGE_RISING: > + reg_level &= ~index; > + reg_both &= ~index; > + reg_low &= ~index; > + break; > + case IRQ_TYPE_EDGE_FALLING: > + reg_level &= ~index; > + reg_both &= ~index; > + reg_low |= index; > + break; > + case IRQ_TYPE_EDGE_BOTH: > + reg_level &= ~index; > + reg_both |= index; > + break; > + case IRQ_TYPE_LEVEL_LOW: > + reg_level |= index; > + reg_low |= index; > + break; > + case IRQ_TYPE_LEVEL_HIGH: > + reg_level |= index; > + reg_low &= ~index; > + break; > + default: > + return -EINVAL; > + } > + > + cns3xxx_direction_in(chip, offset); > + writel(reg_level, base + GPIO_INTR_TRIGGER_METHOD_OFFSET); > + writel(reg_both, base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET); > + writel(reg_low, base + GPIO_INTR_TRIGGER_TYPE_OFFSET); > + > + return 0; > +} > + > +static void cns3xxx_irq_enable(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_ENABLE_OFFSET); > + reg |= (1 << offset); > + writel(reg, base + GPIO_INTR_ENABLE_OFFSET); > +} > + > +static void cns3xxx_irq_disable(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_ENABLE_OFFSET); > + reg &= ~(1 << offset); > + writel(reg, base + GPIO_INTR_ENABLE_OFFSET); > +} > + > +static void cns3xxx_gpio_mask(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_MASK_OFFSET); > + reg |= (1 << offset); > + writel(reg, base + GPIO_INTR_MASK_OFFSET); > +} > + > +static void cns3xxx_gpio_unmask(struct irq_data *data) > +{ > + struct gpio_chip *chip = data->chip_data; > + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; > + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); > + u32 reg; > + > + reg = readl(base + GPIO_INTR_MASK_OFFSET); > + reg &= ~(1 << offset); > + writel(reg, base + GPIO_INTR_MASK_OFFSET); > +} > + > +static struct irq_chip cns3xxx_gpio_irq_chip = { > + .name = "GPIO", > + .irq_enable = cns3xxx_irq_enable, > + .irq_disable = cns3xxx_irq_disable, > + .irq_mask = cns3xxx_gpio_mask, > + .irq_unmask = cns3xxx_gpio_unmask, > + .irq_set_type = cns3xxx_gpio_set_irq_type, > +}; > + > +static struct chog_gpio_chip cns3xxx_gchip[] = { > + /* label, base, ngpio */ > + INIT_CHOG_GPIO_CHIP("GPIOA", 0x00, MAX_GPIOA_NO), > + INIT_CHOG_GPIO_CHIP("GPIOB", MAX_GPIOA_NO, MAX_GPIOB_NO), > +}; > + > + > +static int cns3xxx_gpio_read_proc(char *page, char **start, off_t off, > + int count, int *eof, void *data) > +{ > + int num = 0, i, nr_regs; > + > + nr_regs = sizeof(gpio_regs)/sizeof(struct cns3xxx_regs); ARRAY_SIZE(gpio_regs) > + num += sprintf(page + num, > + "Register Description GPIOA GPIOB\n" > + "==================== ===== =====\n"); > + num += sprintf(page + num, "%-26.26s: %08x %08x\n", "GPIO Disable", > + readl(cns3xxx_gchip[0].reg_sharepin_en), > + readl(cns3xxx_gchip[1].reg_sharepin_en)); > + for (i = 0; i < nr_regs; i++) { > + num += sprintf(page + num, "%-26.26s: %08x %08x\n", > + gpio_regs[i].name, > + readl(cns3xxx_gchip[0].reg_base + gpio_regs[i].offset), > + readl(cns3xxx_gchip[1].reg_base + gpio_regs[i].offset)); > + } > + > + num += sprintf(page + num, "\n" > + "Register Description Value\n" > + "==================== =====\n"); > + nr_regs = sizeof(misc_regs)/sizeof(struct cns3xxx_regs); ARRAY_SIZE(misc_regs) > + for (i = 0; i < nr_regs; i++) { > + num += sprintf(page + num, "%-26.26s: %08x\n", > + misc_regs[i].name, > + readl(misc_regs[i].addr)); > + } > + > + return num; > +} ... > +static int __devinit gpio_probe(struct platform_device *pdev) > +{ > + int i, j, nr_gpios = 0, irq = 0; > + struct resource *res; > + void __iomem *misc_reg; > + > + cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); > + cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); > + > + if (cns3xxx_proc_dir) { > + proc_cns3xxx_gpio = create_proc_entry(GPIO_PROC_NAME, > + S_IFREG | S_IRUGO, cns3xxx_proc_dir) ; > + if (proc_cns3xxx_gpio) > + proc_cns3xxx_gpio->read_proc = cns3xxx_gpio_read_proc; > + } > + > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "MISC"); > + if (res) > + misc_reg = (void __iomem *)res->start; That cast says in a big way "I'm doing something wrong". Casts normally mean that. Resources are, by fact, bus or physical addresses. They live in a unique namespace. Putting virtual addresses in them corrupts that namespace, and can potentially cause unexpected failures. > + else > + return -ENODEV; > + > + /* Scan and match GPIO resources */ > + for (i = 0; i < nr_banks; i++) { > + /* Fetech GPIO base address */ > + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, > + cns3xxx_gchip[i].chip.label); > + if (!res) > + continue; > + cns3xxx_gchip[i].reg_base = (void __iomem *)res->start; Ditto. > + > + /* Fetech GPIO interrupt number */ > + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, > + cns3xxx_gchip[i].chip.label); > + if (!res) > + continue; > + irq = res->start; > + cns3xxx_gchip[i].irq = irq; > + __pr_debug("%s base=0x%08x, irq=%d", > + cns3xxx_gchip[i].chip.label, > + (u32)cns3xxx_gchip[i].reg_base, irq); > + > + cns3xxx_gchip[i].chip.dev = &pdev->dev; > + cns3xxx_gchip[i].reg_sharepin_en = misc_reg + 0x14 + i * 4; > + > + gpiochip_add(&cns3xxx_gchip[i].chip); > + > + /* Clear All Interrupt Status */ > + writel(0xFFFFFFFF, cns3xxx_gchip[i].reg_base + > + GPIO_INTR_CLEAR_OFFSET); > + > + /* Initial irq_chip to handle virtual GPIO irqs. */ > + for (j = 0; j < cns3xxx_gchip[i].chip.ngpio; j++) { > + irq = cns3xxx_to_irq(&cns3xxx_gchip[i].chip, j); > + irq_set_chip_and_handler(irq, &cns3xxx_gpio_irq_chip, > + handle_simple_irq); > + set_irq_flags(irq, IRQF_VALID); > + irq_set_chip_data(irq, &cns3xxx_gchip[i]); > + } > + irq_set_chained_handler(cns3xxx_gchip[i].irq, > + chained_gpio_irq_handler); > + irq_set_handler_data(cns3xxx_gchip[i].irq, > + &cns3xxx_gchip[i]); > + > + nr_gpios += cns3xxx_gchip[i].chip.ngpio; > + if (nr_gpios >= MAX_GPIO_NO) > + break; > + } > + > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int gpio_cns3xxx_suspend(struct platform_device *dev, pm_message_t state) > +{ > + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); > + > + return 0; > +} > + > +static int gpio_cns3xxx_resume(struct platform_device *dev) > +{ > + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); > + > + return 0; > +} > +#endif /* CONFIG_PM */ If no suspend/resume is implemented, then don't provide these functions at all.
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 09ebf0b..ea48dd0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -333,6 +333,7 @@ config ARCH_CNS3XXX select ARM_GIC select MIGHT_HAVE_PCI select PCI_DOMAINS if PCI + select ARCH_REQUIRE_GPIOLIB help Support for Cavium Networks CNS3XXX platform. diff --git a/arch/arm/configs/cns3420vb_defconfig b/arch/arm/configs/cns3420vb_defconfig index 313627a..c83d14c 100644 --- a/arch/arm/configs/cns3420vb_defconfig +++ b/arch/arm/configs/cns3420vb_defconfig @@ -52,6 +52,7 @@ CONFIG_SERIAL_8250_CONSOLE=y CONFIG_LEGACY_PTY_COUNT=16 # CONFIG_HW_RANDOM is not set # CONFIG_HWMON is not set +CONFIG_GPIO_SYSFS=y # CONFIG_VGA_CONSOLE is not set # CONFIG_HID_SUPPORT is not set # CONFIG_USB_SUPPORT is not set diff --git a/arch/arm/mach-cns3xxx/Makefile b/arch/arm/mach-cns3xxx/Makefile index 11033f1..7bb9c88 100644 --- a/arch/arm/mach-cns3xxx/Makefile +++ b/arch/arm/mach-cns3xxx/Makefile @@ -1,3 +1,4 @@ +obj-y += gpio.o obj-$(CONFIG_ARCH_CNS3XXX) += core.o pm.o devices.o obj-$(CONFIG_PCI) += pcie.o obj-$(CONFIG_MACH_CNS3420VB) += cns3420vb.o diff --git a/arch/arm/mach-cns3xxx/cns3420vb.c b/arch/arm/mach-cns3xxx/cns3420vb.c index 3e7d149..34c4a87 100644 --- a/arch/arm/mach-cns3xxx/cns3420vb.c +++ b/arch/arm/mach-cns3xxx/cns3420vb.c @@ -24,6 +24,7 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/physmap.h> #include <linux/mtd/partitions.h> +#include <linux/proc_fs.h> #include <asm/setup.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -158,10 +159,55 @@ static struct platform_device cns3xxx_usb_ohci_device = { }, }; +/* GPIO */ +static struct resource cns3xxx_gpio_resources[] = { + [0] = { + .start = IRQ_CNS3XXX_GPIOA, + .end = IRQ_CNS3XXX_GPIOA, + .flags = IORESOURCE_IRQ, + .name = "GPIOA" + }, + [1] = { + .start = CNS3XXX_GPIOA_BASE_VIRT, + .end = CNS3XXX_GPIOA_BASE_VIRT + 0x44, + .flags = IORESOURCE_MEM, + .name = "GPIOA", + }, + [2] = { + .start = IRQ_CNS3XXX_GPIOB, + .end = IRQ_CNS3XXX_GPIOB, + .flags = IORESOURCE_IRQ, + .name = "GPIOB" + }, + [3] = { + .start = CNS3XXX_GPIOB_BASE_VIRT, + .end = CNS3XXX_GPIOB_BASE_VIRT + 0x44, + .flags = IORESOURCE_MEM, + .name = "GPIOB", + }, + [4] = { + .start = CNS3XXX_MISC_BASE_VIRT, + .end = CNS3XXX_MISC_BASE_VIRT + 0x800, + .flags = IORESOURCE_MEM, + .name = "MISC" + }, +}; + +static struct platform_device cns3xxx_gpio_device = { + .name = "cns3xxx-gpio", + .id = -1, + .num_resources = ARRAY_SIZE(cns3xxx_gpio_resources), + .resource = cns3xxx_gpio_resources, +}; + + +struct proc_dir_entry *cns3xxx_proc_dir; + /* * Initialization */ static struct platform_device *cns3420_pdevs[] __initdata = { + &cns3xxx_gpio_device, &cns3420_nor_pdev, &cns3xxx_usb_ehci_device, &cns3xxx_usb_ohci_device, @@ -176,6 +222,8 @@ static void __init cns3420_init(void) cns3xxx_ahci_init(); cns3xxx_sdhci_init(); + cns3xxx_proc_dir = proc_mkdir("cns3xxx", NULL); + pm_power_off = cns3xxx_power_off; } diff --git a/arch/arm/mach-cns3xxx/gpio.c b/arch/arm/mach-cns3xxx/gpio.c new file mode 100644 index 0000000..cafb65f --- /dev/null +++ b/arch/arm/mach-cns3xxx/gpio.c @@ -0,0 +1,692 @@ +/******************************************************************************* + * + * arch/arm/mach-cns3xxx/gpio.c + * + * GPIO driver for the CNS3XXX SOCs + * + * Copyright (c) 2011 Cavium + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, + * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or + * visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium for more information + * + ******************************************************************************/ + +#include <linux/platform_device.h> +#include <linux/proc_fs.h> +#include <linux/io.h> + +#include <mach/gpio.h> +#include <mach/cns3xxx.h> + +#include <asm/mach/irq.h> + +#define CONFIG_GPIO_CNS3XXX_DEBUG + +#ifdef CONFIG_GPIO_CNS3XXX_DEBUG +#define __pr_debug(fmt, args...) printk(KERN_ERR fmt, ##args) +#else +#define __pr_debug(fmt, args...) +#endif + + +#ifdef CONFIG_DEBUG_FS +static void cns3xxx_dbg_show(struct seq_file *s, struct gpio_chip *chip); +#else +#define cns3xxx_dbg_show NULL +#endif + +#define GPIO_PROC_NAME "gpio" +static struct proc_dir_entry *proc_cns3xxx_gpio; +static struct irq_chip cns3xxx_gpio_irq_chip; + +struct chog_gpio_chip { + struct gpio_chip chip; + int irq; + void __iomem *reg_base; + void __iomem *reg_sharepin_en; +}; + +#define INIT_CHOG_GPIO_CHIP(name, base_no, nr_gpio) \ + { \ + .chip = { \ + .label = name, \ + .owner = THIS_MODULE, \ + .request = cns3xxx_request, \ + .direction_input = cns3xxx_direction_in, \ + .direction_output = cns3xxx_direction_out,\ + .get = cns3xxx_get, \ + .set = cns3xxx_set, \ + .to_irq = cns3xxx_to_irq, \ + .dbg_show = cns3xxx_dbg_show, \ + .base = base_no, \ + .ngpio = nr_gpio, \ + .can_sleep = 0, \ + }, \ + } + + +#define to_chog_gpio_chip(c) container_of(c, struct chog_gpio_chip, chip) +#define nr_banks (sizeof(cns3xxx_gchip)/sizeof(struct chog_gpio_chip)) + +/* The CNS3XXX GPIO pins are shard with special functions which is described in + * the following table. "none" in this table represent the corresponding pins + * are dedicate GPIO. + */ +const char *sharepin_desc[] = { + /* GPIOA group */ +/* 0 */ "none", "none", "SD_PWR_ON", "OTG_DRV_VBUS", +/* 4 */ "Don't use", "none", "none", "none", +/* 8 */ "CIM_nOE", "LCD_Power", "SMI_nCS3", "SMI_nCS2", +/* 12 */ "SMI_Clk", "SMI_nADV", "SMI_CRE", "SMI_Addr[26]", +/* 16 */ "SD_nCD", "SD_nWP", "SD_CLK", "SD_CMD", +/* 20 */ "SD_DT[7]", "SD_DT[6]", "SD_DT[5]", "SD_DT[4]", +/* 24 */ "SD_DT[3]", "SD_DT[2]", "SD_DT[1]", "SD_DT[0]", +/* 28 */ "SD_LED", "UR_RXD1", "UR_TXD1", "UR_RTS2", + /* GPIOB group */ +/* 0 */ "UR_CTS2", "UR_RXD2", "UR_TXD2", "PCMCLK", +/* 4 */ "PCMFS", "PCMDT", "PCMDR", "SPInCS1", +/* 8 */ "SPInCS2", "SPICLK", "SPIDT", "SPIDR", +/* 12 */ "SCL", "SDA", "GMII2_CRS", "GMII2_COL", +/* 16 */ "RGMII1_CRS", "RGMII1_COL", "RGMII0_CRS", "RGMII0_COL", +/* 20 */ "MDC", "MDIO", "I2SCLK", "I2SFS", +/* 24 */ "I2SDT", "I2SDR", "ClkOut", "Ext_Intr2", +/* 28 */ "Ext_Intr1", "Ext_Intr0", "SATA_LED1", "SATA_LED0", +}; + +struct cns3xxx_regs { + char *name; + void __iomem *addr; + u32 offset; +}; + +struct cns3xxx_regs gpio_regs[] = { + {"Data Out", 0, GPIO_OUTPUT_OFFSET}, + {"Data In", 0, GPIO_INPUT_OFFSET}, + {"Direction", 0, GPIO_DIR_OFFSET}, + {"Interrupt Enable", 0, GPIO_INTR_ENABLE_OFFSET}, + {"Interrupt Raw Status", 0, GPIO_INTR_RAW_STATUS_OFFSET}, + {"Interrupt Masked Status", 0, GPIO_INTR_MASKED_STATUS_OFFSET}, + {"Interrupt Level Trigger", 0, GPIO_INTR_TRIGGER_METHOD_OFFSET}, + {"Interrupt Both Edge", 0, GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET}, + {"Interrupt Falling Edge", 0, GPIO_INTR_TRIGGER_TYPE_OFFSET}, + {"Interrupt MASKED", 0, GPIO_INTR_MASK_OFFSET}, + {"GPIO Bounce Enable", 0, GPIO_BOUNCE_ENABLE_OFFSET}, + {"GPIO Bounce Prescale", 0, GPIO_BOUNCE_PRESCALE_OFFSET}, +}; + +struct cns3xxx_regs misc_regs[] = { + {"Drive Strength Register A", MISC_IO_PAD_DRIVE_STRENGTH_CTRL_A}, + {"Drive Strength Register B", MISC_IO_PAD_DRIVE_STRENGTH_CTRL_B}, + {"Pull Up/Down Ctrl A[15:0]", MISC_GPIOA_15_0_PULL_CTRL_REG}, + {"Pull Up/Down Ctrl A[31:16]", MISC_GPIOA_16_31_PULL_CTRL_REG}, + {"Pull Up/Down Ctrl B[15:0]", MISC_GPIOB_15_0_PULL_CTRL_REG}, + {"Pull Up/Down Ctrl B[31:16]", MISC_GPIOB_16_31_PULL_CTRL_REG}, +}; + + +static int cns3xxx_request(struct gpio_chip *chip, unsigned offset) +{ + /* GPIOA4 is reserved for chip bonding configuration. Please don't use + * and configure GPIOA4. + */ + if ((strcmp(chip->label, "GPIOA") == 0) && (offset == 4)) + return -EINVAL; + return 0; +} + +/* + * Configure the GPIO line as an input. + */ +static int cns3xxx_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + u32 reg; + + /* Clear corresponding register bit to set as input pin. */ + reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); + reg &= ~(1 << offset); + writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); + + return 0; +} + +/* + * Set the state of an output GPIO line. + */ +static void cns3xxx_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + + if (value) + /* Write 1 to set corresponding bit output "HIGH" + * Multi-bit write is allowed. Write 0 makes no change. + */ + writel(1 << offset, + cns3xxx_gpio->reg_base + GPIO_BIT_SET_OFFSET); + else + /* Write 1 to set corresponding bit output "LOW" + * Multi-bit write is allowed. Write 0 makes no change. + */ + writel(1 << offset, + cns3xxx_gpio->reg_base + GPIO_BIT_CLEAR_OFFSET); +} + +/* + * Configure the GPIO line as an output, with default state. + */ +static int cns3xxx_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + u32 reg; + + /* Set corresponding register bit to set as output pin. */ + reg = readl(cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); + reg |= 1 << offset; + writel(reg, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); + + cns3xxx_set(chip, offset, value); + + return 0; +} + +/* + * Read the state of a GPIO line. + */ +static int cns3xxx_get(struct gpio_chip *chip, unsigned offset) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + u32 reg; + int ret; + + reg = readl(cns3xxx_gpio->reg_base + GPIO_INPUT_OFFSET); + ret = reg & (1 << offset); + + return ret; +} + +/* + * GPIO interrtups are remapped to unused irq number. + * The remapped GPIO IRQ number start from NR_IRQS_CNS3XXX (96). Here is the + * table of GPIO to irq mapping table. + * + * GPIOA GPIOB | GPIOA GPIOB + * No. IRQ IRQ | No. IRQ IRQ + * 0 96 128 | 16 112 144 + * 1 97 129 | 17 113 145 + * 2 98 130 | 18 114 146 + * 3 99 131 | 19 115 147 + * 4 100 132 | 20 116 148 + * 5 101 133 | 21 117 149 + * 6 102 134 | 22 118 150 + * 7 103 135 | 23 119 151 + * 8 104 136 | 24 120 152 + * 9 105 137 | 25 121 153 + * 10 106 138 | 26 122 154 + * 11 107 139 | 27 123 155 + * 12 108 140 | 28 124 156 + * 13 109 141 | 29 125 157 + * 14 110 142 | 30 126 158 + * 15 111 143 | 31 127 159 + */ +static int cns3xxx_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + + return offset + NR_IRQS_CNS3XXX + cns3xxx_gpio->chip.base; +} + +static unsigned cns3xxx_irq_to_gpio_offset(struct gpio_chip *chip, int irq) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + + return irq - NR_IRQS_CNS3XXX - cns3xxx_gpio->chip.base; +} + +int cns3xxx_gpio_set_irq_type(struct irq_data *data, unsigned int type) +{ + struct gpio_chip *chip = data->chip_data; + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; + unsigned offset; + u32 reg_level, reg_both, reg_low, index; + + offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); + index = 1 << offset; + + reg_level = readl(base + GPIO_INTR_TRIGGER_METHOD_OFFSET); + reg_both = readl(base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET); + reg_low = readl(base + GPIO_INTR_TRIGGER_TYPE_OFFSET); + + switch (type) { + case IRQ_TYPE_EDGE_RISING: + reg_level &= ~index; + reg_both &= ~index; + reg_low &= ~index; + break; + case IRQ_TYPE_EDGE_FALLING: + reg_level &= ~index; + reg_both &= ~index; + reg_low |= index; + break; + case IRQ_TYPE_EDGE_BOTH: + reg_level &= ~index; + reg_both |= index; + break; + case IRQ_TYPE_LEVEL_LOW: + reg_level |= index; + reg_low |= index; + break; + case IRQ_TYPE_LEVEL_HIGH: + reg_level |= index; + reg_low &= ~index; + break; + default: + return -EINVAL; + } + + cns3xxx_direction_in(chip, offset); + writel(reg_level, base + GPIO_INTR_TRIGGER_METHOD_OFFSET); + writel(reg_both, base + GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET); + writel(reg_low, base + GPIO_INTR_TRIGGER_TYPE_OFFSET); + + return 0; +} + +static void cns3xxx_irq_enable(struct irq_data *data) +{ + struct gpio_chip *chip = data->chip_data; + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); + u32 reg; + + reg = readl(base + GPIO_INTR_ENABLE_OFFSET); + reg |= (1 << offset); + writel(reg, base + GPIO_INTR_ENABLE_OFFSET); +} + +static void cns3xxx_irq_disable(struct irq_data *data) +{ + struct gpio_chip *chip = data->chip_data; + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); + u32 reg; + + reg = readl(base + GPIO_INTR_ENABLE_OFFSET); + reg &= ~(1 << offset); + writel(reg, base + GPIO_INTR_ENABLE_OFFSET); +} + +static void cns3xxx_gpio_mask(struct irq_data *data) +{ + struct gpio_chip *chip = data->chip_data; + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); + u32 reg; + + reg = readl(base + GPIO_INTR_MASK_OFFSET); + reg |= (1 << offset); + writel(reg, base + GPIO_INTR_MASK_OFFSET); +} + +static void cns3xxx_gpio_unmask(struct irq_data *data) +{ + struct gpio_chip *chip = data->chip_data; + void __iomem *base = to_chog_gpio_chip(chip)->reg_base; + unsigned offset = cns3xxx_irq_to_gpio_offset(chip, data->irq); + u32 reg; + + reg = readl(base + GPIO_INTR_MASK_OFFSET); + reg &= ~(1 << offset); + writel(reg, base + GPIO_INTR_MASK_OFFSET); +} + +static struct irq_chip cns3xxx_gpio_irq_chip = { + .name = "GPIO", + .irq_enable = cns3xxx_irq_enable, + .irq_disable = cns3xxx_irq_disable, + .irq_mask = cns3xxx_gpio_mask, + .irq_unmask = cns3xxx_gpio_unmask, + .irq_set_type = cns3xxx_gpio_set_irq_type, +}; + +static struct chog_gpio_chip cns3xxx_gchip[] = { + /* label, base, ngpio */ + INIT_CHOG_GPIO_CHIP("GPIOA", 0x00, MAX_GPIOA_NO), + INIT_CHOG_GPIO_CHIP("GPIOB", MAX_GPIOA_NO, MAX_GPIOB_NO), +}; + + +static int cns3xxx_gpio_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int num = 0, i, nr_regs; + + nr_regs = sizeof(gpio_regs)/sizeof(struct cns3xxx_regs); + num += sprintf(page + num, + "Register Description GPIOA GPIOB\n" + "==================== ===== =====\n"); + num += sprintf(page + num, "%-26.26s: %08x %08x\n", "GPIO Disable", + readl(cns3xxx_gchip[0].reg_sharepin_en), + readl(cns3xxx_gchip[1].reg_sharepin_en)); + for (i = 0; i < nr_regs; i++) { + num += sprintf(page + num, "%-26.26s: %08x %08x\n", + gpio_regs[i].name, + readl(cns3xxx_gchip[0].reg_base + gpio_regs[i].offset), + readl(cns3xxx_gchip[1].reg_base + gpio_regs[i].offset)); + } + + num += sprintf(page + num, "\n" + "Register Description Value\n" + "==================== =====\n"); + nr_regs = sizeof(misc_regs)/sizeof(struct cns3xxx_regs); + for (i = 0; i < nr_regs; i++) { + num += sprintf(page + num, "%-26.26s: %08x\n", + misc_regs[i].name, + readl(misc_regs[i].addr)); + } + + return num; +} + +/* + * Turn on corresponding shared pin function. + * Turn on shared pin function will also disable GPIO function. Related GPIO + * control registers are still accessable but not reflect to pin. + */ +int cns3xxx_sharepin_request(unsigned gpio, const char *label) +{ + struct gpio_chip *chip; + int i, reg, ret, offset = gpio; + + ret = gpio_request(gpio, label); + if (ret) { + __pr_debug("gpio%d(%s) already in use! Err=%d\n", + gpio, label, ret); + return ret; + } + + for (i = 0; i < nr_banks; i++) { + chip = &cns3xxx_gchip[i].chip; + if (offset > chip->ngpio) { + offset -= chip->ngpio; + continue; + } + reg = readl(cns3xxx_gchip[i].reg_sharepin_en); + reg |= (1 << offset); + writel(reg, cns3xxx_gchip[i].reg_sharepin_en); + __pr_debug("%s[%d] is occupied by %s function!\n", + chip->label, offset, sharepin_desc[gpio]); + break; + } + + return 0; +} +EXPORT_SYMBOL(cns3xxx_sharepin_request); + +/* + * Turn off corresponding share pin function. + */ +void cns3xxx_sharepin_free(unsigned gpio) +{ + struct gpio_chip *chip; + int i, reg, offset = gpio; + + gpio_free(gpio); + + for (i = 0; i < nr_banks; i++) { + chip = &cns3xxx_gchip[i].chip; + if (offset > chip->ngpio) { + offset -= chip->ngpio; + continue; + } + reg = readl(cns3xxx_gchip[i].reg_sharepin_en); + reg &= ~(1 << offset); + writel(reg, cns3xxx_gchip[i].reg_sharepin_en); + printk(KERN_INFO "%s[%d] share pin function (%s) disabled!\n", + chip->label, offset, sharepin_desc[gpio]); + break; + } +} +EXPORT_SYMBOL(cns3xxx_sharepin_free); + + +#ifdef CONFIG_DEBUG_FS + +#include <linux/debugfs.h> +#include <linux/seq_file.h> + +static void cns3xxx_dbg_show(struct seq_file *s, struct gpio_chip *chip) +{ + struct chog_gpio_chip *cns3xxx_gpio = to_chog_gpio_chip(chip); + int i, is_out; + unsigned gpio; + const char *gpio_label; + + for (i = 0; i < chip->ngpio; i++) { + gpio = chip->base + i; + gpio_label = gpiochip_is_requested(chip, i); + if (!gpio_label) + continue; + is_out = test_bit(i, cns3xxx_gpio->reg_base + GPIO_DIR_OFFSET); + seq_printf(s, " gpio-%-3d [%-20.20s] %s %s (%s%-2d)\n", + gpio, gpio_label, + is_out ? "out" : "in ", + chip->get(chip, i) ? "hi" : "lo", + chip->label, i); + } +} + +static int cns3xxx_dbg_gpio_show_all(struct seq_file *s, void *unused) +{ + int i, j, is_out, disabled; + unsigned gpio; + const char *gpio_label; + struct gpio_chip *chip; + + for (j = 0; j < nr_banks; j++) { + chip = &cns3xxx_gchip[j].chip; + seq_printf(s, " Label Mode Dir Value Note\n"); + seq_printf(s, " ============================================================\n"); + for (i = 0; i < chip->ngpio; i++) { + gpio = chip->base + i; + disabled = test_bit(i, cns3xxx_gchip[j].reg_sharepin_en); + gpio_label = gpiochip_is_requested(chip, i); + if (!gpio_label) { + if (disabled) + gpio_label = sharepin_desc[gpio]; + else + gpio_label = "Not requested"; + } + is_out = test_bit(i, cns3xxx_gchip[j].reg_base + GPIO_DIR_OFFSET); + seq_printf(s, " gpio-%-3d [%-20.20s] %-10.10s%s %s (%s%-2d)\n", + gpio, gpio_label, + disabled ? "Function" : "GPIO", + is_out ? "out" : "in ", + chip->get(chip, i) ? "hi" : "lo", + chip->label, i); + } + seq_printf(s, "\n"); + } + + return 0; +} + +static int dbg_gpio_open(struct inode *inode, struct file *file) +{ + return single_open(file, cns3xxx_dbg_gpio_show_all, &inode->i_private); +} + +static const struct file_operations debug_fops = { + .open = dbg_gpio_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int __init cns3xxx_gpio_debuginit(void) +{ + (void) debugfs_create_file("gpio-cns3xxx", S_IRUGO, + NULL, NULL, &debug_fops); + return 0; +} +late_initcall(cns3xxx_gpio_debuginit); + +#endif /* CONFIG_DEBUG_FS */ + +static void chained_gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct chog_gpio_chip *chip = irq_get_handler_data(irq); + struct irq_chip *ichip = irq_desc_get_chip(desc); + unsigned i; + int target_irq; + u32 status; + + chained_irq_enter(ichip, desc); + + status = readl(chip->reg_base + GPIO_INTR_MASKED_STATUS_OFFSET); + + /* Clean System irq status */ + writel(status, chip->reg_base + GPIO_INTR_CLEAR_OFFSET); + + for (i = 0; i < chip->chip.ngpio; i++) { + if (status & (1 << i)) { + target_irq = cns3xxx_to_irq(&chip->chip, i); + printk(KERN_INFO "Invoke cascaded irq %d from irq %d\n", + target_irq, chip->irq); + generic_handle_irq(target_irq); + } + } + + chained_irq_exit(ichip, desc); +} + +static int __devinit gpio_probe(struct platform_device *pdev) +{ + int i, j, nr_gpios = 0, irq = 0; + struct resource *res; + void __iomem *misc_reg; + + cns3xxx_pwr_clk_en(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); + cns3xxx_pwr_soft_rst(0x1 << PM_CLK_GATE_REG_OFFSET_GPIO); + + if (cns3xxx_proc_dir) { + proc_cns3xxx_gpio = create_proc_entry(GPIO_PROC_NAME, + S_IFREG | S_IRUGO, cns3xxx_proc_dir) ; + if (proc_cns3xxx_gpio) + proc_cns3xxx_gpio->read_proc = cns3xxx_gpio_read_proc; + } + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "MISC"); + if (res) + misc_reg = (void __iomem *)res->start; + else + return -ENODEV; + + /* Scan and match GPIO resources */ + for (i = 0; i < nr_banks; i++) { + /* Fetech GPIO base address */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + cns3xxx_gchip[i].chip.label); + if (!res) + continue; + cns3xxx_gchip[i].reg_base = (void __iomem *)res->start; + + /* Fetech GPIO interrupt number */ + res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, + cns3xxx_gchip[i].chip.label); + if (!res) + continue; + irq = res->start; + cns3xxx_gchip[i].irq = irq; + __pr_debug("%s base=0x%08x, irq=%d", + cns3xxx_gchip[i].chip.label, + (u32)cns3xxx_gchip[i].reg_base, irq); + + cns3xxx_gchip[i].chip.dev = &pdev->dev; + cns3xxx_gchip[i].reg_sharepin_en = misc_reg + 0x14 + i * 4; + + gpiochip_add(&cns3xxx_gchip[i].chip); + + /* Clear All Interrupt Status */ + writel(0xFFFFFFFF, cns3xxx_gchip[i].reg_base + + GPIO_INTR_CLEAR_OFFSET); + + /* Initial irq_chip to handle virtual GPIO irqs. */ + for (j = 0; j < cns3xxx_gchip[i].chip.ngpio; j++) { + irq = cns3xxx_to_irq(&cns3xxx_gchip[i].chip, j); + irq_set_chip_and_handler(irq, &cns3xxx_gpio_irq_chip, + handle_simple_irq); + set_irq_flags(irq, IRQF_VALID); + irq_set_chip_data(irq, &cns3xxx_gchip[i]); + } + irq_set_chained_handler(cns3xxx_gchip[i].irq, + chained_gpio_irq_handler); + irq_set_handler_data(cns3xxx_gchip[i].irq, + &cns3xxx_gchip[i]); + + nr_gpios += cns3xxx_gchip[i].chip.ngpio; + if (nr_gpios >= MAX_GPIO_NO) + break; + } + + return 0; +} + +#ifdef CONFIG_PM +static int gpio_cns3xxx_suspend(struct platform_device *dev, pm_message_t state) +{ + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); + + return 0; +} + +static int gpio_cns3xxx_resume(struct platform_device *dev) +{ + __pr_debug("%s,%s,%d\n", __FILE__, __func__, __LINE__); + + return 0; +} +#endif /* CONFIG_PM */ + +static struct platform_driver gpio_driver = { + .probe = gpio_probe, +#ifdef CONFIG_PM + .suspend = gpio_cns3xxx_suspend, + .resume = gpio_cns3xxx_resume, +#endif /* CONFIG_PM */ + .driver = { + .owner = THIS_MODULE, + .name = "cns3xxx-gpio", + }, +}; + +int __init cns3xxx_gpio_init(void) +{ + return platform_driver_probe(&gpio_driver, gpio_probe); +} + +void __exit cns3xxx_gpio_exit(void) +{ + if (proc_cns3xxx_gpio) + remove_proc_entry(GPIO_PROC_NAME, cns3xxx_proc_dir); +} + +module_init(cns3xxx_gpio_init); +module_exit(cns3xxx_gpio_exit); + +MODULE_LICENSE("GPL"); + diff --git a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h index 191c8e5..217bb4d 100644 --- a/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h +++ b/arch/arm/mach-cns3xxx/include/mach/cns3xxx.h @@ -624,9 +624,15 @@ int cns3xxx_cpu_clock(void); #define NR_IRQS_CNS3XXX (IRQ_TC11MP_GIC_START + 64) -#if !defined(NR_IRQS) || (NR_IRQS < NR_IRQS_CNS3XXX) +#define MAX_GPIOA_NO 32 +#define MAX_GPIOB_NO 32 +#define MAX_GPIO_NO (MAX_GPIOA_NO + MAX_GPIOB_NO) + +#if !defined(NR_IRQS) || (NR_IRQS < (NR_IRQS_CNS3XXX + MAX_GPIO_NO)) #undef NR_IRQS -#define NR_IRQS NR_IRQS_CNS3XXX +#define NR_IRQS (NR_IRQS_CNS3XXX + MAX_GPIO_NO) #endif +extern struct proc_dir_entry *cns3xxx_proc_dir; + #endif /* __MACH_BOARD_CNS3XXX_H */ diff --git a/arch/arm/mach-cns3xxx/include/mach/gpio.h b/arch/arm/mach-cns3xxx/include/mach/gpio.h new file mode 100644 index 0000000..22efca8 --- /dev/null +++ b/arch/arm/mach-cns3xxx/include/mach/gpio.h @@ -0,0 +1,63 @@ +/******************************************************************************* + * + * Copyright (c) 2011 Cavium + * + * This file is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 2, as + * published by the Free Software Foundation. + * + * This file is distributed in the hope that it will be useful, + * but AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or + * NONINFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this file; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA or + * visit http://www.gnu.org/licenses/. + * + * This file may also be available under a different license from Cavium. + * Contact Cavium for more information + * + ******************************************************************************/ + +#ifndef _CNS3XXX_GPIO_H_ +#define _CNS3XXX_GPIO_H_ + +#include <mach/cns3xxx.h> + +#define ARCH_NR_GPIOS MAX_GPIO_NO + +#include <asm-generic/gpio.h> + +#define GPIO_OUTPUT_OFFSET 0x00 +#define GPIO_INPUT_OFFSET 0x04 +#define GPIO_DIR_OFFSET 0x08 +#define GPIO_BIT_SET_OFFSET 0x10 +#define GPIO_BIT_CLEAR_OFFSET 0x14 +#define GPIO_INTR_ENABLE_OFFSET 0x20 +#define GPIO_INTR_RAW_STATUS_OFFSET 0x24 +#define GPIO_INTR_MASKED_STATUS_OFFSET 0x28 +#define GPIO_INTR_MASK_OFFSET 0x2C +#define GPIO_INTR_CLEAR_OFFSET 0x30 +#define GPIO_INTR_TRIGGER_METHOD_OFFSET 0x34 +#define GPIO_INTR_TRIGGER_BOTH_EDGES_OFFSET 0x38 +#define GPIO_INTR_TRIGGER_TYPE_OFFSET 0x3C +#define GPIO_BOUNCE_ENABLE_OFFSET 0x40 +#define GPIO_BOUNCE_PRESCALE_OFFSET 0x44 + + +#define gpio_get_value __gpio_get_value +#define gpio_set_value __gpio_set_value +#define gpio_cansleep __gpio_cansleep +#define gpio_to_irq __gpio_to_irq + +#define GPIOA(n) n +#define GPIOB(n) (MAX_GPIOA_NO + n) + +/* Function prototype */ +int cns3xxx_sharepin_request(unsigned gpio, const char *label); +void cns3xxx_sharepin_free(unsigned gpio); + +#endif +
This patch makes CNS3XXX platform support gpiolib. Users can configure GPIO via gpiolib APIs easily on CNS3XXX platform. Signed-off-by: Tommy Lin <tommy.lin.1101@gmail.com> --- arch/arm/Kconfig | 1 + arch/arm/configs/cns3420vb_defconfig | 1 + arch/arm/mach-cns3xxx/Makefile | 1 + arch/arm/mach-cns3xxx/cns3420vb.c | 48 ++ arch/arm/mach-cns3xxx/gpio.c | 692 ++++++++++++++++++++++++++ arch/arm/mach-cns3xxx/include/mach/cns3xxx.h | 10 +- arch/arm/mach-cns3xxx/include/mach/gpio.h | 63 +++ 7 files changed, 814 insertions(+), 2 deletions(-) create mode 100644 arch/arm/mach-cns3xxx/gpio.c create mode 100644 arch/arm/mach-cns3xxx/include/mach/gpio.h