silicom-platform.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. // SPDX-License-Identifier: GPL-2.0+
  2. //
  3. // silicom-platform.c - Silicom MEC170x platform driver
  4. //
  5. // Copyright (C) 2023 Henry Shi <henrys@silicom-usa.com>
  6. #include <linux/bitfield.h>
  7. #include <linux/bits.h>
  8. #include <linux/dmi.h>
  9. #include <linux/hwmon.h>
  10. #include <linux/init.h>
  11. #include <linux/ioport.h>
  12. #include <linux/io.h>
  13. #include <linux/kernel.h>
  14. #include <linux/kobject.h>
  15. #include <linux/led-class-multicolor.h>
  16. #include <linux/module.h>
  17. #include <linux/mutex.h>
  18. #include <linux/platform_device.h>
  19. #include <linux/string.h>
  20. #include <linux/sysfs.h>
  21. #include <linux/units.h>
  22. #include <linux/gpio/driver.h>
  23. #define MEC_POWER_CYCLE_ADDR 0x24
  24. #define MEC_EFUSE_LSB_ADDR 0x28
  25. #define MEC_GPIO_IN_POS 0x08
  26. #define MEC_IO_BASE 0x0800
  27. #define MEC_IO_LEN 0x8
  28. #define IO_REG_BANK 0x0
  29. #define DEFAULT_CHAN_LO 0
  30. #define DEFAULT_CHAN_HI 0
  31. #define DEFAULT_CHAN_LO_T 0xc
  32. #define MEC_ADDR (MEC_IO_BASE + 0x02)
  33. #define EC_ADDR_LSB MEC_ADDR
  34. #define SILICOM_MEC_MAGIC 0x5a
  35. #define MEC_PORT_CHANNEL_MASK GENMASK(2, 0)
  36. #define MEC_PORT_DWORD_OFFSET GENMASK(31, 3)
  37. #define MEC_DATA_OFFSET_MASK GENMASK(1, 0)
  38. #define MEC_PORT_OFFSET_MASK GENMASK(7, 2)
  39. #define MEC_TEMP_LOC GENMASK(31, 16)
  40. #define MEC_VERSION_LOC GENMASK(15, 8)
  41. #define MEC_VERSION_MAJOR GENMASK(15, 14)
  42. #define MEC_VERSION_MINOR GENMASK(13, 8)
  43. #define EC_ADDR_MSB (MEC_IO_BASE + 0x3)
  44. #define MEC_DATA_OFFSET(offset) (MEC_IO_BASE + 0x04 + (offset))
  45. #define OFFSET_BIT_TO_CHANNEL(off, bit) ((((off) + 0x014) << 3) | (bit))
  46. #define CHANNEL_TO_OFFSET(chan) (((chan) >> 3) - 0x14)
  47. static DEFINE_MUTEX(mec_io_mutex);
  48. static unsigned int efuse_status;
  49. static unsigned int mec_uc_version;
  50. static unsigned int power_cycle;
  51. static const struct hwmon_channel_info *silicom_fan_control_info[] = {
  52. HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_LABEL),
  53. HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_LABEL),
  54. NULL
  55. };
  56. struct silicom_platform_info {
  57. int io_base;
  58. int io_len;
  59. struct led_classdev_mc *led_info;
  60. struct gpio_chip *gpiochip;
  61. u8 *gpio_channels;
  62. u16 ngpio;
  63. };
  64. static const char * const plat_0222_gpio_names[] = {
  65. "AUTOM0_SFP_TX_FAULT",
  66. "SLOT2_LED_OUT",
  67. "SIM_M2_SLOT2_B_DET",
  68. "SIM_M2_SLOT2_A_DET",
  69. "SLOT1_LED_OUT",
  70. "SIM_M2_SLOT1_B_DET",
  71. "SIM_M2_SLOT1_A_DET",
  72. "SLOT0_LED_OUT",
  73. "WAN_SFP0_RX_LOS",
  74. "WAN_SFP0_PRSNT_N",
  75. "WAN_SFP0_TX_FAULT",
  76. "AUTOM1_SFP_RX_LOS",
  77. "AUTOM1_SFP_PRSNT_N",
  78. "AUTOM1_SFP_TX_FAULT",
  79. "AUTOM0_SFP_RX_LOS",
  80. "AUTOM0_SFP_PRSNT_N",
  81. "WAN_SFP1_RX_LOS",
  82. "WAN_SFP1_PRSNT_N",
  83. "WAN_SFP1_TX_FAULT",
  84. "SIM_M2_SLOT1_MUX_SEL",
  85. "W_DISABLE_M2_SLOT1_N",
  86. "W_DISABLE_MPCIE_SLOT0_N",
  87. "W_DISABLE_M2_SLOT0_N",
  88. "BT_COMMAND_MODE",
  89. "WAN_SFP1_TX_DISABLE",
  90. "WAN_SFP0_TX_DISABLE",
  91. "AUTOM1_SFP_TX_DISABLE",
  92. "AUTOM0_SFP_TX_DISABLE",
  93. "SIM_M2_SLOT2_MUX_SEL",
  94. "W_DISABLE_M2_SLOT2_N",
  95. "RST_CTL_M2_SLOT_1_N",
  96. "RST_CTL_M2_SLOT_2_N",
  97. "PM_USB_PWR_EN_BOT",
  98. "PM_USB_PWR_EN_TOP",
  99. };
  100. static u8 plat_0222_gpio_channels[] = {
  101. OFFSET_BIT_TO_CHANNEL(0x00, 0),
  102. OFFSET_BIT_TO_CHANNEL(0x00, 1),
  103. OFFSET_BIT_TO_CHANNEL(0x00, 2),
  104. OFFSET_BIT_TO_CHANNEL(0x00, 3),
  105. OFFSET_BIT_TO_CHANNEL(0x00, 4),
  106. OFFSET_BIT_TO_CHANNEL(0x00, 5),
  107. OFFSET_BIT_TO_CHANNEL(0x00, 6),
  108. OFFSET_BIT_TO_CHANNEL(0x00, 7),
  109. OFFSET_BIT_TO_CHANNEL(0x01, 0),
  110. OFFSET_BIT_TO_CHANNEL(0x01, 1),
  111. OFFSET_BIT_TO_CHANNEL(0x01, 2),
  112. OFFSET_BIT_TO_CHANNEL(0x01, 3),
  113. OFFSET_BIT_TO_CHANNEL(0x01, 4),
  114. OFFSET_BIT_TO_CHANNEL(0x01, 5),
  115. OFFSET_BIT_TO_CHANNEL(0x01, 6),
  116. OFFSET_BIT_TO_CHANNEL(0x01, 7),
  117. OFFSET_BIT_TO_CHANNEL(0x02, 0),
  118. OFFSET_BIT_TO_CHANNEL(0x02, 1),
  119. OFFSET_BIT_TO_CHANNEL(0x02, 2),
  120. OFFSET_BIT_TO_CHANNEL(0x09, 0),
  121. OFFSET_BIT_TO_CHANNEL(0x09, 1),
  122. OFFSET_BIT_TO_CHANNEL(0x09, 2),
  123. OFFSET_BIT_TO_CHANNEL(0x09, 3),
  124. OFFSET_BIT_TO_CHANNEL(0x0a, 0),
  125. OFFSET_BIT_TO_CHANNEL(0x0a, 1),
  126. OFFSET_BIT_TO_CHANNEL(0x0a, 2),
  127. OFFSET_BIT_TO_CHANNEL(0x0a, 3),
  128. OFFSET_BIT_TO_CHANNEL(0x0a, 4),
  129. OFFSET_BIT_TO_CHANNEL(0x0a, 5),
  130. OFFSET_BIT_TO_CHANNEL(0x0a, 6),
  131. OFFSET_BIT_TO_CHANNEL(0x0b, 0),
  132. OFFSET_BIT_TO_CHANNEL(0x0b, 1),
  133. OFFSET_BIT_TO_CHANNEL(0x0b, 2),
  134. OFFSET_BIT_TO_CHANNEL(0x0b, 3),
  135. };
  136. static struct platform_device *silicom_platform_dev;
  137. static struct led_classdev_mc *silicom_led_info __initdata;
  138. static struct gpio_chip *silicom_gpiochip __initdata;
  139. static u8 *silicom_gpio_channels __initdata;
  140. static int silicom_mec_port_get(unsigned int offset)
  141. {
  142. unsigned short mec_data_addr;
  143. unsigned short mec_port_addr;
  144. u8 reg;
  145. mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_DATA_OFFSET_MASK;
  146. mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, offset) & MEC_PORT_OFFSET_MASK;
  147. mutex_lock(&mec_io_mutex);
  148. outb(mec_port_addr, MEC_ADDR);
  149. reg = inb(MEC_DATA_OFFSET(mec_data_addr));
  150. mutex_unlock(&mec_io_mutex);
  151. return (reg >> (offset & MEC_PORT_CHANNEL_MASK)) & 0x01;
  152. }
  153. static enum led_brightness silicom_mec_led_get(int channel)
  154. {
  155. /* Outputs are active low */
  156. return silicom_mec_port_get(channel) ? LED_OFF : LED_ON;
  157. }
  158. static void silicom_mec_port_set(int channel, int on)
  159. {
  160. unsigned short mec_data_addr;
  161. unsigned short mec_port_addr;
  162. u8 reg;
  163. mec_data_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_DATA_OFFSET_MASK;
  164. mec_port_addr = FIELD_GET(MEC_PORT_DWORD_OFFSET, channel) & MEC_PORT_OFFSET_MASK;
  165. mutex_lock(&mec_io_mutex);
  166. outb(mec_port_addr, MEC_ADDR);
  167. reg = inb(MEC_DATA_OFFSET(mec_data_addr));
  168. /* Outputs are active low, so clear the bit for on, or set it for off */
  169. if (on)
  170. reg &= ~(1 << (channel & MEC_PORT_CHANNEL_MASK));
  171. else
  172. reg |= 1 << (channel & MEC_PORT_CHANNEL_MASK);
  173. outb(reg, MEC_DATA_OFFSET(mec_data_addr));
  174. mutex_unlock(&mec_io_mutex);
  175. }
  176. static enum led_brightness silicom_mec_led_mc_brightness_get(struct led_classdev *led_cdev)
  177. {
  178. struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
  179. enum led_brightness brightness = LED_OFF;
  180. int i;
  181. for (i = 0; i < mc_cdev->num_colors; i++) {
  182. mc_cdev->subled_info[i].brightness =
  183. silicom_mec_led_get(mc_cdev->subled_info[i].channel);
  184. /* Mark the overall brightness as LED_ON if any of the subleds are on */
  185. if (mc_cdev->subled_info[i].brightness != LED_OFF)
  186. brightness = LED_ON;
  187. }
  188. return brightness;
  189. }
  190. static void silicom_mec_led_mc_brightness_set(struct led_classdev *led_cdev,
  191. enum led_brightness brightness)
  192. {
  193. struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
  194. int i;
  195. led_mc_calc_color_components(mc_cdev, brightness);
  196. for (i = 0; i < mc_cdev->num_colors; i++) {
  197. silicom_mec_port_set(mc_cdev->subled_info[i].channel,
  198. mc_cdev->subled_info[i].brightness);
  199. }
  200. }
  201. static int silicom_gpio_get_direction(struct gpio_chip *gc,
  202. unsigned int offset)
  203. {
  204. u8 *channels = gpiochip_get_data(gc);
  205. /* Input registers have offsets between [0x00, 0x07] */
  206. if (CHANNEL_TO_OFFSET(channels[offset]) < MEC_GPIO_IN_POS)
  207. return GPIO_LINE_DIRECTION_IN;
  208. return GPIO_LINE_DIRECTION_OUT;
  209. }
  210. static int silicom_gpio_direction_input(struct gpio_chip *gc,
  211. unsigned int offset)
  212. {
  213. int direction = silicom_gpio_get_direction(gc, offset);
  214. return direction == GPIO_LINE_DIRECTION_IN ? 0 : -EINVAL;
  215. }
  216. static void silicom_gpio_set(struct gpio_chip *gc,
  217. unsigned int offset,
  218. int value)
  219. {
  220. int direction = silicom_gpio_get_direction(gc, offset);
  221. u8 *channels = gpiochip_get_data(gc);
  222. int channel = channels[offset];
  223. if (direction == GPIO_LINE_DIRECTION_IN)
  224. return;
  225. silicom_mec_port_set(channel, !value);
  226. }
  227. static int silicom_gpio_direction_output(struct gpio_chip *gc,
  228. unsigned int offset,
  229. int value)
  230. {
  231. int direction = silicom_gpio_get_direction(gc, offset);
  232. if (direction == GPIO_LINE_DIRECTION_IN)
  233. return -EINVAL;
  234. silicom_gpio_set(gc, offset, value);
  235. return 0;
  236. }
  237. static int silicom_gpio_get(struct gpio_chip *gc, unsigned int offset)
  238. {
  239. u8 *channels = gpiochip_get_data(gc);
  240. int channel = channels[offset];
  241. return silicom_mec_port_get(channel);
  242. }
  243. static struct mc_subled plat_0222_wan_mc_subled_info[] __initdata = {
  244. {
  245. .color_index = LED_COLOR_ID_WHITE,
  246. .brightness = 1,
  247. .intensity = 0,
  248. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 7),
  249. },
  250. {
  251. .color_index = LED_COLOR_ID_YELLOW,
  252. .brightness = 1,
  253. .intensity = 0,
  254. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 6),
  255. },
  256. {
  257. .color_index = LED_COLOR_ID_RED,
  258. .brightness = 1,
  259. .intensity = 0,
  260. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 5),
  261. },
  262. };
  263. static struct mc_subled plat_0222_sys_mc_subled_info[] __initdata = {
  264. {
  265. .color_index = LED_COLOR_ID_WHITE,
  266. .brightness = 1,
  267. .intensity = 0,
  268. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 4),
  269. },
  270. {
  271. .color_index = LED_COLOR_ID_AMBER,
  272. .brightness = 1,
  273. .intensity = 0,
  274. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 3),
  275. },
  276. {
  277. .color_index = LED_COLOR_ID_RED,
  278. .brightness = 1,
  279. .intensity = 0,
  280. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 2),
  281. },
  282. };
  283. static struct mc_subled plat_0222_stat1_mc_subled_info[] __initdata = {
  284. {
  285. .color_index = LED_COLOR_ID_RED,
  286. .brightness = 1,
  287. .intensity = 0,
  288. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 1),
  289. },
  290. {
  291. .color_index = LED_COLOR_ID_GREEN,
  292. .brightness = 1,
  293. .intensity = 0,
  294. .channel = OFFSET_BIT_TO_CHANNEL(0x0c, 0),
  295. },
  296. {
  297. .color_index = LED_COLOR_ID_BLUE,
  298. .brightness = 1,
  299. .intensity = 0,
  300. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 7),
  301. },
  302. {
  303. .color_index = LED_COLOR_ID_YELLOW,
  304. .brightness = 1,
  305. .intensity = 0,
  306. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 6),
  307. },
  308. };
  309. static struct mc_subled plat_0222_stat2_mc_subled_info[] __initdata = {
  310. {
  311. .color_index = LED_COLOR_ID_RED,
  312. .brightness = 1,
  313. .intensity = 0,
  314. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 5),
  315. },
  316. {
  317. .color_index = LED_COLOR_ID_GREEN,
  318. .brightness = 1,
  319. .intensity = 0,
  320. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 4),
  321. },
  322. {
  323. .color_index = LED_COLOR_ID_BLUE,
  324. .brightness = 1,
  325. .intensity = 0,
  326. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 3),
  327. },
  328. {
  329. .color_index = LED_COLOR_ID_YELLOW,
  330. .brightness = 1,
  331. .intensity = 0,
  332. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 2),
  333. },
  334. };
  335. static struct mc_subled plat_0222_stat3_mc_subled_info[] __initdata = {
  336. {
  337. .color_index = LED_COLOR_ID_RED,
  338. .brightness = 1,
  339. .intensity = 0,
  340. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 1),
  341. },
  342. {
  343. .color_index = LED_COLOR_ID_GREEN,
  344. .brightness = 1,
  345. .intensity = 0,
  346. .channel = OFFSET_BIT_TO_CHANNEL(0x0d, 0),
  347. },
  348. {
  349. .color_index = LED_COLOR_ID_BLUE,
  350. .brightness = 1,
  351. .intensity = 0,
  352. .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 1),
  353. },
  354. {
  355. .color_index = LED_COLOR_ID_YELLOW,
  356. .brightness = 1,
  357. .intensity = 0,
  358. .channel = OFFSET_BIT_TO_CHANNEL(0x0e, 0),
  359. },
  360. };
  361. static struct led_classdev_mc plat_0222_mc_led_info[] __initdata = {
  362. {
  363. .led_cdev = {
  364. .name = "platled::wan",
  365. .brightness = 0,
  366. .max_brightness = 1,
  367. .brightness_set = silicom_mec_led_mc_brightness_set,
  368. .brightness_get = silicom_mec_led_mc_brightness_get,
  369. },
  370. .num_colors = ARRAY_SIZE(plat_0222_wan_mc_subled_info),
  371. .subled_info = plat_0222_wan_mc_subled_info,
  372. },
  373. {
  374. .led_cdev = {
  375. .name = "platled::sys",
  376. .brightness = 0,
  377. .max_brightness = 1,
  378. .brightness_set = silicom_mec_led_mc_brightness_set,
  379. .brightness_get = silicom_mec_led_mc_brightness_get,
  380. },
  381. .num_colors = ARRAY_SIZE(plat_0222_sys_mc_subled_info),
  382. .subled_info = plat_0222_sys_mc_subled_info,
  383. },
  384. {
  385. .led_cdev = {
  386. .name = "platled::stat1",
  387. .brightness = 0,
  388. .max_brightness = 1,
  389. .brightness_set = silicom_mec_led_mc_brightness_set,
  390. .brightness_get = silicom_mec_led_mc_brightness_get,
  391. },
  392. .num_colors = ARRAY_SIZE(plat_0222_stat1_mc_subled_info),
  393. .subled_info = plat_0222_stat1_mc_subled_info,
  394. },
  395. {
  396. .led_cdev = {
  397. .name = "platled::stat2",
  398. .brightness = 0,
  399. .max_brightness = 1,
  400. .brightness_set = silicom_mec_led_mc_brightness_set,
  401. .brightness_get = silicom_mec_led_mc_brightness_get,
  402. },
  403. .num_colors = ARRAY_SIZE(plat_0222_stat2_mc_subled_info),
  404. .subled_info = plat_0222_stat2_mc_subled_info,
  405. },
  406. {
  407. .led_cdev = {
  408. .name = "platled::stat3",
  409. .brightness = 0,
  410. .max_brightness = 1,
  411. .brightness_set = silicom_mec_led_mc_brightness_set,
  412. .brightness_get = silicom_mec_led_mc_brightness_get,
  413. },
  414. .num_colors = ARRAY_SIZE(plat_0222_stat3_mc_subled_info),
  415. .subled_info = plat_0222_stat3_mc_subled_info,
  416. },
  417. { },
  418. };
  419. static struct gpio_chip silicom_gpio_chip = {
  420. .label = "silicom-gpio",
  421. .get_direction = silicom_gpio_get_direction,
  422. .direction_input = silicom_gpio_direction_input,
  423. .direction_output = silicom_gpio_direction_output,
  424. .get = silicom_gpio_get,
  425. .set = silicom_gpio_set,
  426. .base = -1,
  427. .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
  428. .names = plat_0222_gpio_names,
  429. /*
  430. * We're using a mutex to protect the indirect access, so we can sleep
  431. * if the lock blocks
  432. */
  433. .can_sleep = true,
  434. };
  435. static struct silicom_platform_info silicom_plat_0222_cordoba_info __initdata = {
  436. .io_base = MEC_IO_BASE,
  437. .io_len = MEC_IO_LEN,
  438. .led_info = plat_0222_mc_led_info,
  439. .gpiochip = &silicom_gpio_chip,
  440. .gpio_channels = plat_0222_gpio_channels,
  441. /*
  442. * The original generic cordoba does not have the last 4 outputs of the
  443. * plat_0222 variant, the rest are the same, so use the same longer list,
  444. * but ignore the last entries here
  445. */
  446. .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
  447. };
  448. static struct mc_subled cordoba_fp_left_mc_subled_info[] __initdata = {
  449. {
  450. .color_index = LED_COLOR_ID_RED,
  451. .brightness = 1,
  452. .intensity = 0,
  453. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 6),
  454. },
  455. {
  456. .color_index = LED_COLOR_ID_GREEN,
  457. .brightness = 1,
  458. .intensity = 0,
  459. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 5),
  460. },
  461. {
  462. .color_index = LED_COLOR_ID_BLUE,
  463. .brightness = 1,
  464. .intensity = 0,
  465. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 7),
  466. },
  467. {
  468. .color_index = LED_COLOR_ID_AMBER,
  469. .brightness = 1,
  470. .intensity = 0,
  471. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 4),
  472. },
  473. };
  474. static struct mc_subled cordoba_fp_center_mc_subled_info[] __initdata = {
  475. {
  476. .color_index = LED_COLOR_ID_RED,
  477. .brightness = 1,
  478. .intensity = 0,
  479. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 7),
  480. },
  481. {
  482. .color_index = LED_COLOR_ID_GREEN,
  483. .brightness = 1,
  484. .intensity = 0,
  485. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 4),
  486. },
  487. {
  488. .color_index = LED_COLOR_ID_BLUE,
  489. .brightness = 1,
  490. .intensity = 0,
  491. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 3),
  492. },
  493. {
  494. .color_index = LED_COLOR_ID_AMBER,
  495. .brightness = 1,
  496. .intensity = 0,
  497. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 6),
  498. },
  499. };
  500. static struct mc_subled cordoba_fp_right_mc_subled_info[] __initdata = {
  501. {
  502. .color_index = LED_COLOR_ID_RED,
  503. .brightness = 1,
  504. .intensity = 0,
  505. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 2),
  506. },
  507. {
  508. .color_index = LED_COLOR_ID_GREEN,
  509. .brightness = 1,
  510. .intensity = 0,
  511. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 1),
  512. },
  513. {
  514. .color_index = LED_COLOR_ID_BLUE,
  515. .brightness = 1,
  516. .intensity = 0,
  517. .channel = OFFSET_BIT_TO_CHANNEL(0x08, 0),
  518. },
  519. {
  520. .color_index = LED_COLOR_ID_AMBER,
  521. .brightness = 1,
  522. .intensity = 0,
  523. .channel = OFFSET_BIT_TO_CHANNEL(0x09, 5),
  524. },
  525. };
  526. static struct led_classdev_mc cordoba_mc_led_info[] __initdata = {
  527. {
  528. .led_cdev = {
  529. .name = "platled::fp_left",
  530. .brightness = 0,
  531. .max_brightness = 1,
  532. .brightness_set = silicom_mec_led_mc_brightness_set,
  533. .brightness_get = silicom_mec_led_mc_brightness_get,
  534. },
  535. .num_colors = ARRAY_SIZE(cordoba_fp_left_mc_subled_info),
  536. .subled_info = cordoba_fp_left_mc_subled_info,
  537. },
  538. {
  539. .led_cdev = {
  540. .name = "platled::fp_center",
  541. .brightness = 0,
  542. .max_brightness = 1,
  543. .brightness_set = silicom_mec_led_mc_brightness_set,
  544. .brightness_get = silicom_mec_led_mc_brightness_get,
  545. },
  546. .num_colors = ARRAY_SIZE(cordoba_fp_center_mc_subled_info),
  547. .subled_info = cordoba_fp_center_mc_subled_info,
  548. },
  549. {
  550. .led_cdev = {
  551. .name = "platled::fp_right",
  552. .brightness = 0,
  553. .max_brightness = 1,
  554. .brightness_set = silicom_mec_led_mc_brightness_set,
  555. .brightness_get = silicom_mec_led_mc_brightness_get,
  556. },
  557. .num_colors = ARRAY_SIZE(cordoba_fp_right_mc_subled_info),
  558. .subled_info = cordoba_fp_right_mc_subled_info,
  559. },
  560. { },
  561. };
  562. static struct silicom_platform_info silicom_generic_cordoba_info __initdata = {
  563. .io_base = MEC_IO_BASE,
  564. .io_len = MEC_IO_LEN,
  565. .led_info = cordoba_mc_led_info,
  566. .gpiochip = &silicom_gpio_chip,
  567. .gpio_channels = plat_0222_gpio_channels,
  568. .ngpio = ARRAY_SIZE(plat_0222_gpio_channels),
  569. };
  570. /*
  571. * sysfs interface
  572. */
  573. static ssize_t efuse_status_show(struct device *dev,
  574. struct device_attribute *attr,
  575. char *buf)
  576. {
  577. u32 reg;
  578. mutex_lock(&mec_io_mutex);
  579. /* Select memory region */
  580. outb(IO_REG_BANK, EC_ADDR_MSB);
  581. outb(MEC_EFUSE_LSB_ADDR, EC_ADDR_LSB);
  582. /* Get current data from the address */
  583. reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  584. mutex_unlock(&mec_io_mutex);
  585. efuse_status = reg & 0x1;
  586. return sysfs_emit(buf, "%u\n", efuse_status);
  587. }
  588. static DEVICE_ATTR_RO(efuse_status);
  589. static ssize_t uc_version_show(struct device *dev,
  590. struct device_attribute *attr,
  591. char *buf)
  592. {
  593. int uc_version;
  594. u32 reg;
  595. mutex_lock(&mec_io_mutex);
  596. outb(IO_REG_BANK, EC_ADDR_MSB);
  597. outb(DEFAULT_CHAN_LO, EC_ADDR_LSB);
  598. reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  599. mutex_unlock(&mec_io_mutex);
  600. uc_version = FIELD_GET(MEC_VERSION_LOC, reg);
  601. if (uc_version >= 192)
  602. return -EINVAL;
  603. uc_version = FIELD_GET(MEC_VERSION_MAJOR, reg) * 100 +
  604. FIELD_GET(MEC_VERSION_MINOR, reg);
  605. mec_uc_version = uc_version;
  606. return sysfs_emit(buf, "%u\n", mec_uc_version);
  607. }
  608. static DEVICE_ATTR_RO(uc_version);
  609. static ssize_t power_cycle_show(struct device *dev,
  610. struct device_attribute *attr,
  611. char *buf)
  612. {
  613. return sysfs_emit(buf, "%u\n", power_cycle);
  614. }
  615. static void powercycle_uc(void)
  616. {
  617. /* Select memory region */
  618. outb(IO_REG_BANK, EC_ADDR_MSB);
  619. outb(MEC_POWER_CYCLE_ADDR, EC_ADDR_LSB);
  620. /* Set to 1 for current data from the address */
  621. outb(1, MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  622. }
  623. static ssize_t power_cycle_store(struct device *dev,
  624. struct device_attribute *attr,
  625. const char *buf, size_t count)
  626. {
  627. int rc;
  628. unsigned int power_cycle_cmd;
  629. rc = kstrtou32(buf, 0, &power_cycle_cmd);
  630. if (rc)
  631. return -EINVAL;
  632. if (power_cycle_cmd > 0) {
  633. mutex_lock(&mec_io_mutex);
  634. power_cycle = power_cycle_cmd;
  635. powercycle_uc();
  636. mutex_unlock(&mec_io_mutex);
  637. }
  638. return count;
  639. }
  640. static DEVICE_ATTR_RW(power_cycle);
  641. static struct attribute *silicom_attrs[] = {
  642. &dev_attr_efuse_status.attr,
  643. &dev_attr_uc_version.attr,
  644. &dev_attr_power_cycle.attr,
  645. NULL,
  646. };
  647. ATTRIBUTE_GROUPS(silicom);
  648. static struct platform_driver silicom_platform_driver = {
  649. .driver = {
  650. .name = "silicom-platform",
  651. .dev_groups = silicom_groups,
  652. },
  653. };
  654. static int __init silicom_mc_leds_register(struct device *dev,
  655. const struct led_classdev_mc *mc_leds)
  656. {
  657. int size = sizeof(struct mc_subled);
  658. struct led_classdev_mc *led;
  659. int i, err;
  660. for (i = 0; mc_leds[i].led_cdev.name; i++) {
  661. led = devm_kzalloc(dev, sizeof(*led), GFP_KERNEL);
  662. if (!led)
  663. return -ENOMEM;
  664. memcpy(led, &mc_leds[i], sizeof(*led));
  665. led->subled_info = devm_kzalloc(dev, led->num_colors * size, GFP_KERNEL);
  666. if (!led->subled_info)
  667. return -ENOMEM;
  668. memcpy(led->subled_info, mc_leds[i].subled_info, led->num_colors * size);
  669. err = devm_led_classdev_multicolor_register(dev, led);
  670. if (err)
  671. return err;
  672. }
  673. return 0;
  674. }
  675. static u32 rpm_get(void)
  676. {
  677. u32 reg;
  678. mutex_lock(&mec_io_mutex);
  679. /* Select memory region */
  680. outb(IO_REG_BANK, EC_ADDR_MSB);
  681. outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
  682. reg = inw(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  683. mutex_unlock(&mec_io_mutex);
  684. return reg;
  685. }
  686. static u32 temp_get(void)
  687. {
  688. u32 reg;
  689. mutex_lock(&mec_io_mutex);
  690. /* Select memory region */
  691. outb(IO_REG_BANK, EC_ADDR_MSB);
  692. outb(DEFAULT_CHAN_LO_T, EC_ADDR_LSB);
  693. reg = inl(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  694. mutex_unlock(&mec_io_mutex);
  695. return FIELD_GET(MEC_TEMP_LOC, reg) * 100;
  696. }
  697. static umode_t silicom_fan_control_fan_is_visible(const u32 attr)
  698. {
  699. switch (attr) {
  700. case hwmon_fan_input:
  701. case hwmon_fan_label:
  702. return 0444;
  703. default:
  704. return 0;
  705. }
  706. }
  707. static umode_t silicom_fan_control_temp_is_visible(const u32 attr)
  708. {
  709. switch (attr) {
  710. case hwmon_temp_input:
  711. case hwmon_temp_label:
  712. return 0444;
  713. default:
  714. return 0;
  715. }
  716. }
  717. static int silicom_fan_control_read_fan(struct device *dev, u32 attr, long *val)
  718. {
  719. switch (attr) {
  720. case hwmon_fan_input:
  721. *val = rpm_get();
  722. return 0;
  723. default:
  724. return -EOPNOTSUPP;
  725. }
  726. }
  727. static int silicom_fan_control_read_temp(struct device *dev, u32 attr, long *val)
  728. {
  729. switch (attr) {
  730. case hwmon_temp_input:
  731. *val = temp_get();
  732. return 0;
  733. default:
  734. return -EOPNOTSUPP;
  735. }
  736. }
  737. static umode_t silicom_fan_control_is_visible(const void *data,
  738. enum hwmon_sensor_types type,
  739. u32 attr, int channel)
  740. {
  741. switch (type) {
  742. case hwmon_fan:
  743. return silicom_fan_control_fan_is_visible(attr);
  744. case hwmon_temp:
  745. return silicom_fan_control_temp_is_visible(attr);
  746. default:
  747. return 0;
  748. }
  749. }
  750. static int silicom_fan_control_read(struct device *dev,
  751. enum hwmon_sensor_types type,
  752. u32 attr, int channel,
  753. long *val)
  754. {
  755. switch (type) {
  756. case hwmon_fan:
  757. return silicom_fan_control_read_fan(dev, attr, val);
  758. case hwmon_temp:
  759. return silicom_fan_control_read_temp(dev, attr, val);
  760. default:
  761. return -EOPNOTSUPP;
  762. }
  763. }
  764. static int silicom_fan_control_read_labels(struct device *dev,
  765. enum hwmon_sensor_types type,
  766. u32 attr, int channel,
  767. const char **str)
  768. {
  769. switch (type) {
  770. case hwmon_fan:
  771. *str = "Silicom_platform: Fan Speed";
  772. return 0;
  773. case hwmon_temp:
  774. *str = "Silicom_platform: Thermostat Sensor";
  775. return 0;
  776. default:
  777. return -EOPNOTSUPP;
  778. }
  779. }
  780. static const struct hwmon_ops silicom_fan_control_hwmon_ops = {
  781. .is_visible = silicom_fan_control_is_visible,
  782. .read = silicom_fan_control_read,
  783. .read_string = silicom_fan_control_read_labels,
  784. };
  785. static const struct hwmon_chip_info silicom_chip_info = {
  786. .ops = &silicom_fan_control_hwmon_ops,
  787. .info = silicom_fan_control_info,
  788. };
  789. static int __init silicom_platform_probe(struct platform_device *device)
  790. {
  791. struct device *hwmon_dev;
  792. u8 magic, ver;
  793. int err;
  794. if (!devm_request_region(&device->dev, MEC_IO_BASE, MEC_IO_LEN, "mec")) {
  795. dev_err(&device->dev, "couldn't reserve MEC io ports\n");
  796. return -EBUSY;
  797. }
  798. /* Sanity check magic number read for EC */
  799. outb(IO_REG_BANK, MEC_ADDR);
  800. magic = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_LO));
  801. ver = inb(MEC_DATA_OFFSET(DEFAULT_CHAN_HI));
  802. dev_dbg(&device->dev, "EC magic 0x%02x, version 0x%02x\n", magic, ver);
  803. if (magic != SILICOM_MEC_MAGIC) {
  804. dev_err(&device->dev, "Bad EC magic 0x%02x!\n", magic);
  805. return -ENODEV;
  806. }
  807. err = silicom_mc_leds_register(&device->dev, silicom_led_info);
  808. if (err) {
  809. dev_err(&device->dev, "Failed to register LEDs\n");
  810. return err;
  811. }
  812. err = devm_gpiochip_add_data(&device->dev, silicom_gpiochip,
  813. silicom_gpio_channels);
  814. if (err) {
  815. dev_err(&device->dev, "Failed to register gpiochip: %d\n", err);
  816. return err;
  817. }
  818. hwmon_dev = devm_hwmon_device_register_with_info(&device->dev, "silicom_fan", NULL,
  819. &silicom_chip_info, NULL);
  820. err = PTR_ERR_OR_ZERO(hwmon_dev);
  821. if (err) {
  822. dev_err(&device->dev, "Failed to register hwmon_dev: %d\n", err);
  823. return err;
  824. }
  825. return err;
  826. }
  827. static int __init silicom_platform_info_init(const struct dmi_system_id *id)
  828. {
  829. struct silicom_platform_info *info = id->driver_data;
  830. silicom_led_info = info->led_info;
  831. silicom_gpio_channels = info->gpio_channels;
  832. silicom_gpiochip = info->gpiochip;
  833. silicom_gpiochip->ngpio = info->ngpio;
  834. return 1;
  835. }
  836. static const struct dmi_system_id silicom_dmi_ids[] __initconst = {
  837. {
  838. .callback = silicom_platform_info_init,
  839. .ident = "Silicom Cordoba (Generic)",
  840. .matches = {
  841. DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
  842. DMI_MATCH(DMI_BOARD_NAME, "80300-0214-G"),
  843. },
  844. .driver_data = &silicom_generic_cordoba_info,
  845. },
  846. {
  847. .callback = silicom_platform_info_init,
  848. .ident = "Silicom Cordoba (Generic)",
  849. .matches = {
  850. DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
  851. DMI_MATCH(DMI_BOARD_NAME, "80500-0214-G"),
  852. },
  853. .driver_data = &silicom_generic_cordoba_info,
  854. },
  855. {
  856. .callback = silicom_platform_info_init,
  857. .ident = "Silicom Cordoba (plat_0222)",
  858. .matches = {
  859. DMI_MATCH(DMI_BOARD_VENDOR, "Silicom"),
  860. DMI_MATCH(DMI_BOARD_NAME, "80300-0222-G"),
  861. },
  862. .driver_data = &silicom_plat_0222_cordoba_info,
  863. },
  864. { },
  865. };
  866. MODULE_DEVICE_TABLE(dmi, silicom_dmi_ids);
  867. static int __init silicom_platform_init(void)
  868. {
  869. if (!dmi_check_system(silicom_dmi_ids)) {
  870. pr_err("No DMI match for this platform\n");
  871. return -ENODEV;
  872. }
  873. silicom_platform_dev = platform_create_bundle(&silicom_platform_driver,
  874. silicom_platform_probe,
  875. NULL, 0, NULL, 0);
  876. return PTR_ERR_OR_ZERO(silicom_platform_dev);
  877. }
  878. static void __exit silicom_platform_exit(void)
  879. {
  880. platform_device_unregister(silicom_platform_dev);
  881. platform_driver_unregister(&silicom_platform_driver);
  882. }
  883. module_init(silicom_platform_init);
  884. module_exit(silicom_platform_exit);
  885. MODULE_LICENSE("GPL");
  886. MODULE_AUTHOR("Henry Shi <henrys@silicom-usa.com>");
  887. MODULE_DESCRIPTION("Platform driver for Silicom network appliances");