Blame view
drivers/mfd/htc-pasic3.c
4.87 KB
b886d83c5
|
1 |
// SPDX-License-Identifier: GPL-2.0-only |
5dc3339aa
|
2 3 4 5 |
/* * Core driver for HTC PASIC3 LED/DS1WM chip. * * Copyright (C) 2006 Philipp Zabel <philipp.zabel@gmail.com> |
5dc3339aa
|
6 7 8 9 10 |
*/ #include <linux/init.h> #include <linux/module.h> #include <linux/platform_device.h> |
5dc3339aa
|
11 12 13 14 |
#include <linux/gpio.h> #include <linux/io.h> #include <linux/irq.h> #include <linux/interrupt.h> |
0254a8f49
|
15 16 |
#include <linux/mfd/core.h> #include <linux/mfd/ds1wm.h> |
5dc3339aa
|
17 |
#include <linux/mfd/htc-pasic3.h> |
5a0e3ad6a
|
18 |
#include <linux/slab.h> |
5dc3339aa
|
19 |
|
5dc3339aa
|
20 21 22 |
struct pasic3_data { void __iomem *mapping; unsigned int bus_shift; |
5dc3339aa
|
23 24 25 26 |
}; #define REG_ADDR 5 #define REG_DATA 6 |
5dc3339aa
|
27 28 29 30 31 32 33 34 |
#define READ_MODE 0x80 /* * write to a secondary register on the PASIC3 */ void pasic3_write_register(struct device *dev, u32 reg, u8 val) { |
1902a9e62
|
35 |
struct pasic3_data *asic = dev_get_drvdata(dev); |
5dc3339aa
|
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
int bus_shift = asic->bus_shift; void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift); void __iomem *data = asic->mapping + (REG_DATA << bus_shift); __raw_writeb(~READ_MODE & reg, addr); __raw_writeb(val, data); } EXPORT_SYMBOL(pasic3_write_register); /* for leds-pasic3 */ /* * read from a secondary register on the PASIC3 */ u8 pasic3_read_register(struct device *dev, u32 reg) { |
1902a9e62
|
50 |
struct pasic3_data *asic = dev_get_drvdata(dev); |
5dc3339aa
|
51 52 53 54 55 56 57 58 59 60 61 62 |
int bus_shift = asic->bus_shift; void __iomem *addr = asic->mapping + (REG_ADDR << bus_shift); void __iomem *data = asic->mapping + (REG_DATA << bus_shift); __raw_writeb(READ_MODE | reg, addr); return __raw_readb(data); } EXPORT_SYMBOL(pasic3_read_register); /* for leds-pasic3 */ /* * LEDs */ |
0254a8f49
|
63 64 65 |
static struct mfd_cell led_cell __initdata = { .name = "leds-pasic3", }; |
5dc3339aa
|
66 67 68 69 |
/* * DS1WM */ |
0254a8f49
|
70 |
static int ds1wm_enable(struct platform_device *pdev) |
5dc3339aa
|
71 72 73 74 75 76 77 78 79 |
{ struct device *dev = pdev->dev.parent; int c; c = pasic3_read_register(dev, 0x28); pasic3_write_register(dev, 0x28, c & 0x7f); dev_dbg(dev, "DS1WM OWM_EN low (active) %02x ", c & 0x7f); |
0254a8f49
|
80 |
return 0; |
5dc3339aa
|
81 |
} |
0254a8f49
|
82 |
static int ds1wm_disable(struct platform_device *pdev) |
5dc3339aa
|
83 84 85 86 87 88 89 90 91 |
{ struct device *dev = pdev->dev.parent; int c; c = pasic3_read_register(dev, 0x28); pasic3_write_register(dev, 0x28, c | 0x80); dev_dbg(dev, "DS1WM OWM_EN high (inactive) %02x ", c | 0x80); |
0254a8f49
|
92 |
return 0; |
5dc3339aa
|
93 |
} |
0254a8f49
|
94 95 |
static struct ds1wm_driver_data ds1wm_pdata = { .active_high = 0, |
f607e7fc5
|
96 |
.reset_recover_delay = 1, |
5dc3339aa
|
97 |
}; |
0254a8f49
|
98 99 100 101 102 103 104 105 106 107 108 |
static struct resource ds1wm_resources[] __initdata = { [0] = { .start = 0, .flags = IORESOURCE_MEM, }, [1] = { .start = 0, .end = 0, .flags = IORESOURCE_IRQ, }, }; |
5dc3339aa
|
109 |
|
5ac98553a
|
110 |
static const struct mfd_cell ds1wm_cell __initconst = { |
0254a8f49
|
111 112 113 |
.name = "ds1wm", .enable = ds1wm_enable, .disable = ds1wm_disable, |
121ea573a
|
114 115 |
.platform_data = &ds1wm_pdata, .pdata_size = sizeof(ds1wm_pdata), |
0254a8f49
|
116 117 118 |
.num_resources = 2, .resources = ds1wm_resources, }; |
5dc3339aa
|
119 120 121 |
static int __init pasic3_probe(struct platform_device *pdev) { |
334a41ce9
|
122 |
struct pasic3_platform_data *pdata = dev_get_platdata(&pdev->dev); |
5dc3339aa
|
123 124 125 126 |
struct device *dev = &pdev->dev; struct pasic3_data *asic; struct resource *r; int ret; |
0254a8f49
|
127 128 129 130 131 132 133 134 |
int irq = 0; r = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (r) { ds1wm_resources[1].flags = IORESOURCE_IRQ | (r->flags & (IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE)); irq = r->start; } |
5dc3339aa
|
135 136 137 |
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!r) return -ENXIO; |
0254a8f49
|
138 |
if (!request_mem_region(r->start, resource_size(r), "pasic3")) |
5dc3339aa
|
139 |
return -EBUSY; |
8692881e3
|
140 |
asic = devm_kzalloc(dev, sizeof(struct pasic3_data), GFP_KERNEL); |
5dc3339aa
|
141 142 143 144 |
if (!asic) return -ENOMEM; platform_set_drvdata(pdev, asic); |
0254a8f49
|
145 |
asic->mapping = ioremap(r->start, resource_size(r)); |
5dc3339aa
|
146 147 148 |
if (!asic->mapping) { dev_err(dev, "couldn't ioremap PASIC3 "); |
5dc3339aa
|
149 150 |
return -ENOMEM; } |
0254a8f49
|
151 152 |
/* calculate bus shift from mem resource */ asic->bus_shift = (resource_size(r) - 5) >> 3; |
47c10edd7
|
153 154 155 156 |
if (pdata && pdata->clock_rate) { ds1wm_pdata.clock_rate = pdata->clock_rate; /* the first 5 PASIC3 registers control the DS1WM */ ds1wm_resources[0].end = (5 << asic->bus_shift) - 1; |
47c10edd7
|
157 |
ret = mfd_add_devices(&pdev->dev, pdev->id, |
0848c94fb
|
158 |
&ds1wm_cell, 1, r, irq, NULL); |
47c10edd7
|
159 160 161 162 163 164 |
if (ret < 0) dev_warn(dev, "failed to register DS1WM "); } if (pdata && pdata->led_pdata) { |
8ac93beaa
|
165 166 |
led_cell.platform_data = pdata->led_pdata; led_cell.pdata_size = sizeof(struct pasic3_leds_machinfo); |
0848c94fb
|
167 168 |
ret = mfd_add_devices(&pdev->dev, pdev->id, &led_cell, 1, r, 0, NULL); |
5dc3339aa
|
169 170 171 172 173 174 175 176 177 178 179 180 |
if (ret < 0) dev_warn(dev, "failed to register LED device "); } return 0; } static int pasic3_remove(struct platform_device *pdev) { struct pasic3_data *asic = platform_get_drvdata(pdev); struct resource *r; |
0254a8f49
|
181 |
mfd_remove_devices(&pdev->dev); |
5dc3339aa
|
182 183 184 |
iounmap(asic->mapping); r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
0254a8f49
|
185 |
release_mem_region(r->start, resource_size(r)); |
5dc3339aa
|
186 187 |
return 0; } |
4f46d6e7e
|
188 |
MODULE_ALIAS("platform:pasic3"); |
5dc3339aa
|
189 190 191 192 193 194 |
static struct platform_driver pasic3_driver = { .driver = { .name = "pasic3", }, .remove = pasic3_remove, }; |
e8fe6a827
|
195 |
module_platform_driver_probe(pasic3_driver, pasic3_probe); |
5dc3339aa
|
196 197 198 199 |
MODULE_AUTHOR("Philipp Zabel <philipp.zabel@gmail.com>"); MODULE_DESCRIPTION("Core driver for HTC PASIC3"); MODULE_LICENSE("GPL"); |