All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kristoffer Ericson <kristoffer.ericson@gmail.com>
To: linux-sh@vger.kernel.org
Subject: MFD for hd64461 thoughts and ideas
Date: Thu, 25 Sep 2008 20:18:14 +0000	[thread overview]
Message-ID: <20080925221814.51b06ed2.kristoffer.ericson@gmail.com> (raw)

Finally started looking at it, almost ready for testing.
Im not going for anything major currently, just actually
being able to replace the current structure.

Files:
drivers/mfd/hd64461.c
drivers/mfd/hd64461.h

A very basic layout is like this:

Machine files wishing to use driver sets resource with IRQ
(start->end) and chip memory area (start->end). These 
values are then used to set IRQ handling (demux) for all irq's 
involved (baseirq + 15) and also map the io area.

Now, to the issues :
1* Is it a good idea to link the TMU code in there also?
   I mean same file and all?
2* platforms wanting to use this driver should somehow
   setup their own specifics (like powering up), best
   way to handle this? 
3* hd64461 fb and pcmcia should be seperate, but should
   they also register with the "main" driver?
4* the current approach for hd64461 demuxer is to fiddle
   with the irq_desc[], as I understand this is not recommended(?).
   I've tried to workout a different approach in the code below
   where I simply set chip type for all involved irq's and hope
   that they therefore get acked/masked correctly.

I've studied the example drivers in drivers/mfd/ but seems
there are very little chips that are as complete as the hd64461 :P
seeing as it supplies FB / PCMCIA / TIMER / DEMUX...

Best wishes
Kristoffer

ps. included my code so far below, no nitpicking since its
not finished yet :) Suggestions are helpful though.


 /*
  *
  * MFD driver for the Hitachi HD64461 companion chip
  *
  * HD64461 chip contains 8 interrupts handled by an demuxer
  * They control pcmcia, compact flash, 
  *
  * (C) 2008 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
  *
  * Based on hd64461.c (C) 2000 YAEGASHI Takeshi
  */

#include <linux/sched.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <linux/errno.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include "hd64461.h"

struct hd64461_info {
    spinlock_t		lock;

    void __iomem	*base_addr;
    int			base_irq;
    int 		irq_end;
};

/* IO FUNCTIONS */
void hd64461_chip_writew(u16 value, struct hd64461_info *hd64461, int reg)
{
    unsigned long addr = hd64461->base_addr;
    
    addr += reg;

    outw(value, addr);
}
EXPORT_SYMBOL(hd64461_chip_writew);

void hd64461_chip_writeb(u8 value, struct hd64461_info *hd64461, int reg)
{
    unsigned long addr = hd64461->base_addr;
    
    addr += reg;
    
    outb(value, addr);
}
EXPORT_SYMBOL(hd64461_chip_writeb);

u16 hd64461_chip_readw(struct hd64461_info *hd64461, int reg)
{
    return(inw(hd64461->base_addr + reg));
}
EXPORT_SYMBOL(hd64461_chip_readw);

u8 hd64461_chip_readb(struct hd64461_info *hd64461, int reg)
{
    return(inb(hd64461->base_addr + reg));
}
EXPORT_SYMBOL(hd64461_chip_readb);

/* IRQ DEMUXER */
static void hd64461_irq_disable(unsigned int irq)
{
    struct hd64461_info *hd64461 = get_irq_chip_data(irq);
    unsigned int nimr;
    unsigned short mask = (1 << (irq - (hd64461->base_irq)));

    nimr = hd64461_chip_readw(hd64461, HD64461_NIMR);
    nimr |= mask;
    hd64461_chip_writew(nimr, hd64461, HD64461_NIMR);
}
    
static void hd64461_irq_mask_ack(unsigned int irq)
{
    hd64461_irq_disable(irq);
}

static void hd64461_irq_unmask(unsigned int irq)
{
    struct hd64461_info *hd64461 = get_irq_chip_data(irq);
    unsigned int nimr;
    unsigned short mask = 1 << (irq - (hd64461->base_irq));
    nimr &= ~mask;
    hd64461_chip_writew(nimr, hd64461, HD64461_NIMR);
}

static void hd64461_demux(unsigned int irq, struct irq_desc *desc)
{
    struct hd64461_info *hd64461 = get_irq_data(irq);
    unsigned short	bit;
    unsigned int	nirr, nimr, i;
    
    nirr = hd64461_chip_readw(hd64461, HD64461_NIRR);
    nimr = hd64461_chip_readw(hd64461, HD64461_NIMR);
    
    nirr &= ~nimr;
    
    /* What irq is causing our interrupt? */
    for (bit = 1, i = 0; i < 16; bit <<= 1, i++)
	if (nirr & bit)
	    break;
    
    /* Lets jump into the correct handler */
    desc = &irq_desc[(nirr + i)];
    desc->handle_irq((nirr + 1), desc);
}

static struct irq_chip hd64461_chip = {
    .name	= "hd64461",
    .ack	= hd64461_irq_mask_ack,
    .mask	= hd64461_irq_mask_ack,
    .unmask	= hd64461_irq_unmask,
};

static int __init hd64461_probe(struct platform_device *pdev)
{
    struct resource 	*res;
    struct hd64461_info *hd64461;
    
    int			irq;
    u16			status_data,v;
    
    /* Allocate our memory */
    hd64461 = kzalloc(size(*hd64461), GFP_KERNEL);
    if (!hd64461) {
	goto fault_0;
    }
    
    /* We get our base IRQ from the driver resource */
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

    if (!res) 
	goto fault_1;

    hd64461->base_irq  = res->start;
    hd64461->irq_end   = res->end;

    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

    if (!res)
	goto fault_1;
    
    hd64461->base_addr = ioremap(res->start, res->end - res->start);

    platform_set_drvdata(pdev, hd64461);    
    
    /* Ack all currently interrupts */
    hd64461_chip_writeb(0xffff, hd64461, HD64461_NIMR);
    
    /* Make sure all hd64461 interrupts are handled by us */
    for (irq = hd64461->base_irq; (irq < hd64461->irq_end); irq++) {
	set_irq_chip(irq, &hd64461_chip);
	set_irq_chip_data(irq, hd64461);
    }
    
    /* now lets set the base_irq handler (which everything runs through) */
    set_irq_type(hd64461->base_irq, IRQF_DISABLED);
    set_irq_data(hd64461->base_irq, hd64461);
    set_irq_chained_handler(hd64461->base_irq, hd64461_demux);
        
    printk(KERN_INFO "hd64461 configured at 0x%x on base irq %d (demux irq %d - %d)\n",
	    hd64461->base_addr, hd64461->base_irq, hd64461->base_irq, hd64461->irq_end);

    /* Lets set the wakeup status */
    status_data = hd64461_chip_readw(hd64461, HD64461_STBCR);
    status_data |= HD64461_STBCR_SURTST | HD64461_STBCR_SIRST  |
		   HD64461_STBCR_STM1ST | HD64461_STBCR_STM0ST |
		   HD64461_STBCR_SAFEST | HD64461_STBCR_SPC0ST |
		   HD64461_STBCR_SMIAST | HD64461_STBCR_SAFECKE_OST |
		   HD64461_STBCR_SAFECKE_IST;
		   
    /* set pcmcia and standby mode */
    v |= HD64461_STBCR_SPC1ST;
    
    hd64461_chip_writew(v, hd64461, HD64461_STBCR);		   

    /* powerup speaker and pcmcia0 */    
    v = hd64461_chip_readw(hd64461, HD64461_GPADR);
    v |= HD64461_GPADR_SPEAKER | HD64461_GPADR_PCMCIA0;
    hd64461_chip_writew(v, hd64461, HD64461_GPADR);

    /* Set the interrupt styles of both pcmcia */
    hd64461_chip_writew((HD64461_PCCGCR_VCC0 | HD64461_PCCGCR_VCC1), hd64461, HD64461_PCC0GCR);
    hd64461_chip_writew((HD64461_PCCGCR_VCC0 | HD64461_PCCGCR_VCC1), hd64461, HD64461_PCC1GCR);

    /* Set interrupt styles for pcmcia 1 (only storage, no io) */
    hd64461_chip_writeb(0x4c, hd64461, HD64461_PCC1CSCIER);
    hd64461_chip_writeb(0x00, hd64461, HD64461_PCC1CSCR);
    return 0;

fault_0:
    printk(KERN_ERR "Unable to aqcuire memory\n");
    return -ENOMEM;

fault_1:
    printk(KERN_ERR "Unable to get resources\n");
    return -ENODEV;
}

static int __exit hd64461_remove(struct platform_device *pdev)
{

return 0;
}

static struct platform_driver hd64461_driver = {
    .driver	= {
	.name	= "hd64461",
    },
    .remove	= __exit_p(hd64461_remove),
};

static int __init hd64461_init(void)
{
    return platform_driver_probe(&hd64461_driver, hd64461_probe);
}

static void __exit hd64461_exit(void)
{
    platform_driver_unregister(&hd64461_driver);
}
module_init(hd64461_init)
module_exit(hd64461_exit)

MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
MODULE_DESCRIPTION("Core Driver for HD64461");
MODULE_LICENSE("GPL");




-- 
Kristoffer Ericson <kristoffer.ericson@gmail.com>

             reply	other threads:[~2008-09-25 20:18 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-09-25 20:18 Kristoffer Ericson [this message]
2008-09-26  0:56 ` MFD for hd64461 thoughts and ideas Paul Mundt
2008-09-26  1:00 ` Paul Mundt
2008-09-26  9:46 ` Kristoffer Ericson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20080925221814.51b06ed2.kristoffer.ericson@gmail.com \
    --to=kristoffer.ericson@gmail.com \
    --cc=linux-sh@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.