0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012 #include <linux/kernel.h>
0013 #include <linux/irq.h>
0014 #include <linux/interrupt.h>
0015 #include <linux/module.h>
0016 #include <linux/cs5535.h>
0017 #include <linux/clockchips.h>
0018
0019 #define DRV_NAME "cs5535-clockevt"
0020
0021 static int timer_irq;
0022 module_param_hw_named(irq, timer_irq, int, irq, 0644);
0023 MODULE_PARM_DESC(irq, "Which IRQ to use for the clock source MFGPT ticks.");
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 static struct cs5535_mfgpt_timer *cs5535_event_clock;
0043
0044
0045
0046 #define MFGPT_DIVISOR 16
0047 #define MFGPT_SCALE 4
0048 #define MFGPT_HZ (32768 / MFGPT_DIVISOR)
0049 #define MFGPT_PERIODIC (MFGPT_HZ / HZ)
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059 static void disable_timer(struct cs5535_mfgpt_timer *timer)
0060 {
0061
0062 cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
0063 (uint16_t) ~MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP1 |
0064 MFGPT_SETUP_CMP2);
0065 }
0066
0067 static void start_timer(struct cs5535_mfgpt_timer *timer, uint16_t delta)
0068 {
0069 cs5535_mfgpt_write(timer, MFGPT_REG_CMP2, delta);
0070 cs5535_mfgpt_write(timer, MFGPT_REG_COUNTER, 0);
0071
0072 cs5535_mfgpt_write(timer, MFGPT_REG_SETUP,
0073 MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
0074 }
0075
0076 static int mfgpt_shutdown(struct clock_event_device *evt)
0077 {
0078 disable_timer(cs5535_event_clock);
0079 return 0;
0080 }
0081
0082 static int mfgpt_set_periodic(struct clock_event_device *evt)
0083 {
0084 disable_timer(cs5535_event_clock);
0085 start_timer(cs5535_event_clock, MFGPT_PERIODIC);
0086 return 0;
0087 }
0088
0089 static int mfgpt_next_event(unsigned long delta, struct clock_event_device *evt)
0090 {
0091 start_timer(cs5535_event_clock, delta);
0092 return 0;
0093 }
0094
0095 static struct clock_event_device cs5535_clockevent = {
0096 .name = DRV_NAME,
0097 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
0098 .set_state_shutdown = mfgpt_shutdown,
0099 .set_state_periodic = mfgpt_set_periodic,
0100 .set_state_oneshot = mfgpt_shutdown,
0101 .tick_resume = mfgpt_shutdown,
0102 .set_next_event = mfgpt_next_event,
0103 .rating = 250,
0104 };
0105
0106 static irqreturn_t mfgpt_tick(int irq, void *dev_id)
0107 {
0108 uint16_t val = cs5535_mfgpt_read(cs5535_event_clock, MFGPT_REG_SETUP);
0109
0110
0111 if (!(val & (MFGPT_SETUP_SETUP | MFGPT_SETUP_CMP2 | MFGPT_SETUP_CMP1)))
0112 return IRQ_NONE;
0113
0114
0115 disable_timer(cs5535_event_clock);
0116
0117 if (clockevent_state_detached(&cs5535_clockevent) ||
0118 clockevent_state_shutdown(&cs5535_clockevent))
0119 return IRQ_HANDLED;
0120
0121
0122 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_COUNTER, 0);
0123
0124
0125
0126 if (clockevent_state_periodic(&cs5535_clockevent))
0127 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP,
0128 MFGPT_SETUP_CNTEN | MFGPT_SETUP_CMP2);
0129
0130 cs5535_clockevent.event_handler(&cs5535_clockevent);
0131 return IRQ_HANDLED;
0132 }
0133
0134 static int __init cs5535_mfgpt_init(void)
0135 {
0136 unsigned long flags = IRQF_NOBALANCING | IRQF_TIMER | IRQF_SHARED;
0137 struct cs5535_mfgpt_timer *timer;
0138 int ret;
0139 uint16_t val;
0140
0141 timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);
0142 if (!timer) {
0143 printk(KERN_ERR DRV_NAME ": Could not allocate MFGPT timer\n");
0144 return -ENODEV;
0145 }
0146 cs5535_event_clock = timer;
0147
0148
0149 if (cs5535_mfgpt_setup_irq(timer, MFGPT_CMP2, &timer_irq)) {
0150 printk(KERN_ERR DRV_NAME ": Could not set up IRQ %d\n",
0151 timer_irq);
0152 goto err_timer;
0153 }
0154
0155
0156 ret = request_irq(timer_irq, mfgpt_tick, flags, DRV_NAME, timer);
0157 if (ret) {
0158 printk(KERN_ERR DRV_NAME ": Unable to set up the interrupt.\n");
0159 goto err_irq;
0160 }
0161
0162
0163 val = MFGPT_SCALE | (3 << 8);
0164
0165 cs5535_mfgpt_write(cs5535_event_clock, MFGPT_REG_SETUP, val);
0166
0167
0168 printk(KERN_INFO DRV_NAME
0169 ": Registering MFGPT timer as a clock event, using IRQ %d\n",
0170 timer_irq);
0171 clockevents_config_and_register(&cs5535_clockevent, MFGPT_HZ,
0172 0xF, 0xFFFE);
0173
0174 return 0;
0175
0176 err_irq:
0177 cs5535_mfgpt_release_irq(cs5535_event_clock, MFGPT_CMP2, &timer_irq);
0178 err_timer:
0179 cs5535_mfgpt_free_timer(cs5535_event_clock);
0180 printk(KERN_ERR DRV_NAME ": Unable to set up the MFGPT clock source\n");
0181 return -EIO;
0182 }
0183
0184 module_init(cs5535_mfgpt_init);
0185
0186 MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
0187 MODULE_DESCRIPTION("CS5535/CS5536 MFGPT clock event driver");
0188 MODULE_LICENSE("GPL");